AsyncTask runs on each page of the ViewPager

穿精又带淫゛_ 提交于 2020-01-02 00:49:30

问题


I have 3 Tabs like in the android development tutorial

Now what I want to do is very simple I use Fragments on each page. I want to show different content from a rss feed on each page.

The problem is when I go to the next tab it runs AsyncTask (which is in onCreateView) of the previous Fragment.

So you start on Page 1 it loads the content fine. Then when you go to Page 2 is runs the onCreateView of the Fragment of Page 1 again. And obviously gives an NullException. The point is it should not be running AsyncTask of Page 1 at all at that Page 2.

I don't think there is any example code needed if so tell me which part you need to see. Then I will edit my question.

AsyncTask inside a 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(); 
        }
    }
}

It's called inside a method and that method is executed inside the onCreateView of the 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();
}

The LogCat :


回答1:


Try using isAdded() before onPostExecute(). isAdded() returns true if the fragment is currently added to its activity.

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

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



回答2:


Move your

startNewAsyncTask(); 

to onActivityCreated()




回答3:


I'm assuming your using FragmentPagerAdapter with your ViewPager.

To enable smooth animations, the ViewPager by default keeps the current fragment and the two neighbors in resumed state. So onCreateView is not the best place to start the AsyncTask.

Instead you need to create a custom listener interface. The fragments in the ViewPager should implement it, and call the new interface from the ViewPager's OnPageChangeListener.

Check out my answer to this question or you can read the whole tutorial here.




回答4:


You're getting that exception because you're calling getActivity() too early. You should do it after onActivityCreated() (see this diagram)

Executing of onCreateView() in background is fine and actually is default behaviour. The thing is, ViewPager is optimised to load a content of neighbour non-visible pages in background to improve UX. You can do this: mViewPager.setOffscreenPageLimit(2); (default value is 1) to load all 3 pages at once (1 is loading as currently visible and other 2 as optimisation). Or set it to 0 to disable this behaviour, but it's not the best idea.

In general, you should cash your loaded data and do not load it again by making your fragment's lifecycle methods as light as possible. Page limit of 2 is fine for 3 pages, but if you'll have for example 10 pages, limit of 9 is too much.




回答5:


If I've understood your question right, I think you need unique content with each Fragment right?

Try using the varible arguments of the execute method. For example:

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

In this way you can pass a unique URL per fragment form which you can get your content. I feel you already have this. The doInBackground method has the List of URLs. You just need to pass that information in the execute method and utilize it in doInBackground.

Hope this helps!




回答6:


It is normal that it runs the AsyncTask from the adjacent Fragments, since the ViewPager + PagerAdapter combo, works loading the current, previous and next Fragment.

You should focus the problem not to stop AsyncTask from running, but to let it run w/o throwing a NullPointerException.

The following should be called inside onCreateView()

adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, myList);               
setListAdapter(adapter);

And then, onPostExecute()

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



回答7:


The ViewPager will create views for fragments in adjacent pages and destroy views for fragments which are not adjacent to current page. Thus, the onCreateView of page 1 will get called if you navigate from page1->page2->page3->page2. You can have the viewpager keep more pages in memory by using ViewPager.setOffscreenPageLimit.

The fragmentPagerAdapter retains the fragment objects. Only the views are destroyed. Thus, when viewpage recreates page1's view, the fragment object is the same. Hence, all the fields in the fragment will get retained. As in most applications where there's no realtime data, it is not required not load the data every time the view of the fragment is created, you can store the data in the fragment object after loading. Then, before starting the AsyncTask in onCreateView/onActivityCreated, check if the data has been previously loaded or not.

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();
    }
}

I recommend that you use loaders for loading data for a fragment. For your purpose, you can configure a loader to load data only once. This is a great tutorial on Loaders. http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html

In the tutorial, the loader is configured to return previous data immediately if available, and then fetch data in background and return it after fetching completes. Thus, the UI will get updated after fresh data gets downloaded but at the same time, it will show the previous data initially while the download happens.




回答8:


You can use another activity - this activity will run asynctask and then move to your fragment related activity. In this way it should call only once.

In case you need to update Fragment UI using this AsyncTask then use a static method to call through AsyncTask.



来源:https://stackoverflow.com/questions/21671875/asynctask-runs-on-each-page-of-the-viewpager

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!