14

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 : logcat

Shishi
  • 601
  • 2
  • 8
  • 27
  • pls post your code and log cat ! – Rachita Nanda Feb 10 '14 at 08:09
  • I don't know which part of the code you would like to see since the loading of the content works from the first page. The logcat only gives a `NullPointerException` when it runs the `AsyncTask` on a Page where it should not be running it which is logical. – Shishi Feb 10 '14 at 08:14
  • post your async task and piece of code where its called from ..Along with your log cat error .Error must be pointing out the line at which null pointer occurs .Post that part also – Rachita Nanda Feb 10 '14 at 08:16
  • at which line you got null pointer exception? – Piyush Feb 10 '14 at 08:24
  • The line which it points to is where the adapter is initialized. I think that's because the list item is no longer accessible, but I could be wrong about that. The error should be gone when it stop to run the AsyncTask when I go to that Page. – Shishi Feb 10 '14 at 08:25
  • `adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, listData); ` – Shishi Feb 10 '14 at 08:25
  • where is your adapter declaration? – Piyush Feb 10 '14 at 08:27
  • The declaration is at the top of the class. I think you call it a global declaration – Shishi Feb 10 '14 at 08:30
  • Yes i know that. But in your code there is no. So i asked about that – Piyush Feb 10 '14 at 08:31
  • what is NieuwsSectionFragment ? and in which fragment you are using startNewAsyncTask(); – keshav Feb 10 '14 at 09:21
  • NieuwsSectionFragment is a ListFragment and I use startNewAsyncTask(); in the onCreateView of NieuwsSectionFragment – Shishi Feb 10 '14 at 09:44
  • I'm currently using a workaround with booleans in both onCreate and on the tabstrippager so my asynctask runs only on the page of content still need to find a better way – Shishi Feb 20 '14 at 14:09

8 Answers8

3

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
    }
}
Matt
  • 74,352
  • 26
  • 153
  • 180
Deepak Negi
  • 893
  • 10
  • 19
2

Move your

startNewAsyncTask(); 

to onActivityCreated()

Piyush
  • 18,895
  • 5
  • 32
  • 63
localhost
  • 5,568
  • 1
  • 33
  • 53
1

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.

Community
  • 1
  • 1
LordRaydenMK
  • 13,074
  • 5
  • 50
  • 56
0

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.

Dmide
  • 6,422
  • 3
  • 24
  • 31
0

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!

Jignesh Shah
  • 599
  • 1
  • 5
  • 13
0

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();
Christopher Francisco
  • 15,672
  • 28
  • 94
  • 206
0

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.

Manas Chaudhari
  • 276
  • 2
  • 4
0

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.

Anil Jadhav
  • 2,128
  • 1
  • 17
  • 31