Подтвердить что ты не робот

AsyncTask работает на каждой странице ViewPager

У меня есть 3 вкладки, как в учебное пособие по разработке Android

Теперь то, что я хочу сделать, очень просто. Я использую фрагменты на каждой странице. Я хочу показать различный контент из RSS-ленты на каждой странице.

Проблема заключается в том, когда я перехожу на следующую вкладку, в которой запускается AsyncTask (которая находится в onCreateView) предыдущего фрагмента.

Итак, вы начинаете на странице 1, чтобы загрузить контент. Затем, когда вы переходите на страницу 2, снова запускается onCreateView фрагмента страницы. И, очевидно, дает NullException. Дело в том, что он вообще не должен запускать AsyncTask на странице 1.

Я не думаю, что есть какой-то примерный код, если это так, скажите мне, какую часть вам нужно увидеть. Затем я отредактирую свой вопрос.

AsyncTask внутри объекта ListFragment:

public class MyAsyncTask extends AsyncTask<List<String>, Void, List<String>>
{
    // List of messages of the rss feed
    private List<Message> messages;
    private volatile boolean running = true;

    @SuppressWarnings("unused")
    private WeakReference<NieuwsSectionFragment> fragmentWeakRef;

    private MyAsyncTask(NieuwsSectionFragment fragment)
    {
        this.fragmentWeakRef = new WeakReference<NieuwsSectionFragment>(fragment);
    }
    @Override
    protected void onPreExecute() 
    {
        super.onPreExecute();

        mProgress.show();
       //  progress.setVisibility(View.VISIBLE); //<< set here
    }
    @Override
    protected void onCancelled()
    {
        Log.w("onCancelled", "now cancelled");
        running = false;
    }
    @Override
    protected List<String> doInBackground(List<String>... urls)
    {
        FeedParser parser = FeedParserFactory.getParser();
        messages = parser.parse();
        List<String> titles = new ArrayList<String>(messages.size());
        for (Message msg : messages)
        {
            titles.add(msg.getTitle());
            // Log.w("doInBackground", msg.getTitle());
        }
        return titles;
    }

    @Override
    protected void onPostExecute(List<String> result)
    {
        super.onPostExecute(result);            
        mProgress.dismiss();
        if (result != null)
        {
            PostData data = null;
            listData = new PostData[result.size()];
            for (int i = 0; i < result.size(); i++)
            {
                data = new PostData();                  
                data.postTitle = result.get(i);
                data.postThumbUrl = "http://igo.nl/foto/app_thumb/28991-Taxi-vast-na-poging-tot-nemen-van-sluiproute.jpg";                  
                listData[i] = data;
                Log.w("onPostExecute", "" + listData[i].postTitle);
            }
            adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, listData);               
            setListAdapter(adapter);
            adapter.notifyDataSetChanged(); 
        }
    }
}

Он вызван внутри метода и этот метод выполняется внутри onCreateView в ListFragment:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    startNewAsyncTask();
    View rootView = inflater.inflate(R.layout.fragment_section_nieuws, container, false);
    return rootView;
}

@SuppressWarnings("unchecked")
public void startNewAsyncTask()
{       
    MyAsyncTask asyncTask = new MyAsyncTask(this);
    this.asyncTaskWeakRef = new WeakReference<MyAsyncTask>(asyncTask);
    asyncTask.execute();
}

LogCat: logcat

4b9b3361

Ответ 1

Попробуйте использовать isAdded() до onPostExecute(). isAdded() возвращает true, если фрагмент в настоящее время добавлен к его активности.

http://developer.android.com/reference/android/app/Fragment.html#isAdded()

@Override
protected void postExecute(){
    if(isAdded()){
        //perform Display Changes
    }
}

Ответ 2

Переместите

startNewAsyncTask(); 

to onActivityCreated()

Ответ 3

Я предполагаю, что вы используете FragmentPagerAdapter со своим ViewPager.

Чтобы включить плавные анимации, ViewPager по умолчанию сохраняет текущий фрагмент и два соседа в возобновленном состоянии. Так что onCreateView не лучшее место для запуска AsyncTask.

Вместо этого вам нужно создать пользовательский интерфейс прослушивателя. fragments в ViewPager должен реализовать его и вызвать новый интерфейс из ViewPager's OnPageChangeListener.

Посмотрите мой ответ на этот вопрос или вы можете прочитать весь учебник здесь.

Ответ 4

Трудность здесь заключается в том, что после перехода к следующему фрагменту (Fragment 2), AsyncTask из Fragment 1 все еще работает. Вы можете подумать: "Хорошо, в таком случае я остановил свой AsyncTask на onDestroyView, например", но другой сложной точкой является тот факт, что AsyncTask не прекратится сразу после вызова . cancel. Итак, проблема: на onPostExecute вы ссылаетесь на ресурсы, которые не существуют после переключения на Fragment 2. Давайте посмотрим на ArrayAdapter: 310:

mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

Ясно, что context есть null. Давайте углубимся и рассмотрим метод getActivity - он просто возвращает значение поля mActivity.

В таком случае, почему он null? Поскольку после удаления Fragment FragmentManager вызывает initState, который явно удаляет ссылку на объект Activity:

void initState() {
    ...
    mActivity = null;
    ...
}

Как защитить от NPE? Всегда проверяйте результат getActivity на onPostExecute для null.

Ответ 5

Вы получаете это исключение, потому что вы вызываете getActivity() слишком рано. Вы должны сделать это после onActivityCreated() (см. эту диаграмму)

Выполнение onCreateView() в фоновом режиме является прекрасным и фактически является поведением по умолчанию. Дело в том, что ViewPager оптимизирован для загрузки содержимого соседних невидимых страниц в фоновом режиме для улучшения UX. Вы можете сделать это: mViewPager.setOffscreenPageLimit(2); (значение по умолчанию равно 1) для загрузки всех трех страниц одновременно (1 загружается как видимый в настоящее время, а другой 2 - как оптимизация). Или установите его на 0, чтобы отключить это поведение, но это не лучшая идея.

В общем, вы должны обналичить свои загруженные данные и не загружать их снова, сделав методы жизненного цикла фрагмента настолько легкими, насколько это возможно. Предел страницы 2 - штраф на 3 страницы, но если у вас будет, например, 10 страниц, ограничение на 9 слишком велико.

Ответ 6

Если я правильно понял ваш вопрос, я думаю, что вам нужен уникальный контент с каждым фрагментом?

Попробуйте использовать переменные аргумента метода execute. Например:

yourTask.execute(<some-unique-URL>, parameter, one-more-parameter);

Таким образом, вы можете передать уникальный URL-адрес для фрагмента, который вы можете получить. Я чувствую, что у вас уже есть это. Метод doInBackground имеет список URL-адресов. Вам просто нужно передать эту информацию в методе выполнения и использовать ее в doInBackground.

Надеюсь, это поможет!

Ответ 7

Это нормально, что он запускает AsyncTask из соседнего Fragments, так как комбо ViewPager + PagerAdapter работает, загружая текущий, предыдущий и следующий Fragment.

Вы должны сфокусировать проблему, чтобы не остановить выполнение AsyncTask, но позволить ей запускать без бросания NullPointerException.

Внутри onCreateView()

должно быть вызвано следующее:
adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, myList);               
setListAdapter(adapter);

И затем onPostExecute()

myList.clear();
myList.addAll(listData);
adapter.notifyDataSetChanged();

Ответ 8

ViewPager создаст представления для фрагментов на соседних страницах и уничтожит представления для фрагментов, которые не смежны с текущей страницей. Таким образом, onCreateView на стр. 1 вызывается, если вы переходите со страницы1- > page2- > page3- > page2. Вы можете заставить просмотрщик хранить больше страниц в памяти, используя ViewPager.setOffscreenPageLimit.

fragmentPagerAdapter сохраняет объекты фрагмента. Только взгляды уничтожены. Таким образом, когда viewpage воссоздает представление page1, объект фрагмента тот же. Следовательно, все поля в фрагменте будут сохранены. Как и в большинстве приложений, где нет данных в реальном времени, не требуется загружать данные каждый раз при создании представления фрагмента, вы можете сохранить данные в объекте фрагмента после загрузки. Затем перед запуском AsyncTask в onCreateView/onActivityCreated проверьте, были ли данные ранее загружены или нет.

class PageFragment {

private List<String> mData;

...
void onActivityCreated() {
    if (data == null) { // OR if the data is expired
        startAsyncTask();
    } else {
        updateViews();
    }
}

void updateViews() {
    // Display mData in views
}

class LoadDataTask extends AsyncTask<List<String>, ..., ...> {

    ...
    void onPostExecute(List<String> result) {
        PageFragment.this.mData = result;
        PageFragment.this.updateViews();
    }
}

Я рекомендую использовать загрузчики для загрузки данных для фрагмента. Для вашей цели вы можете настроить загрузчик для загрузки данных только один раз. Это отличный учебник по загрузчикам. http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html

В учебнике загрузчик настроен на немедленное возвращение предыдущих данных, если он доступен, и затем извлекать данные в фоновом режиме и возвращать их после завершения набора. Таким образом, пользовательский интерфейс будет обновляться после загрузки свежих данных, но в то же время он сначала отображает предыдущие данные во время загрузки.

Ответ 9

Вы можете использовать другое действие - это действие будет выполняться как асинхронное, а затем перейти к активности, связанной с фрагментом. Таким образом, он должен звонить только один раз.

Если вам нужно обновить интерфейс Fragment с помощью этой AsyncTask, используйте статический метод для вызова AsyncTask.