5

I have an application in the market with a bug which I can not seem to solve. Today I have tracked it down to the following method.

public void updateDealList(ArrayList<Deal> deals) {
    // first call or showing bookmakrs (also gets called other times when adapter is null)
    if (dealListAdapter == null || showingBookmarks || !dealListAdapter.moreDealsToDownload()) { 
        dealListAdapter = new EndlessDealListAdapter(getActivity(),
                R.layout.deal_list_item, deals);
        setListAdapter(dealListAdapter);
        listView = getListView();
        if (getActivity().findViewById(R.id.right_fragment_container)!=null){ // if tablet
            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
            listView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT);
        }
        showingBookmarks=false;
        Log.d("UPDATE_DEAL_LIST", "Notified list  created new" + dealListAdapter.getCount());
        return; //PROBLEM OCCURS WHEN APP COMES IN HERE AFTER RESUMING
    }
    dealListAdapter.getDealListAdapter().clear();
    for (Deal d: deals){
        dealListAdapter.getDealListAdapter().add(d);
        Log.d("UPDATE_DEAL_LIST", "added " + d.title);
    }
    dealListAdapter.notifyDataSetChanged();
    Log.d("UPDATE_DEAL_LIST", "Notified list " + dealListAdapter.getCount());
}

This method is passed an arraylist of deal objects which are pulled down from the internet. When the application is first opened data is pulled from the internet and the above method is called which creates a new adapter which is set for the ListFragment. This method is also called when the user requests more deals.

The problem I am having is that the list sometimes thinks its empty dispite the adapter containing deals. This seems to occur when a use resume the application when their device is low on memory (I presume part of the application has been removed from ram). This method is called and the dealListAdapter is null so a new one is created and deals are added. Despite this happening the list remains empty and the user has to force close the app to get it working again.

The line below shows tat when the method is called it enters the if and 21 deals are added to the adapter. Unfortunately the list is empty to the user.

05-23 01:52:32.879: D/UPDATE_DEAL_LIST(3478): Notified list  created new21
bencallis
  • 3,478
  • 4
  • 32
  • 58
  • Let me know if any further code would be helpful. – bencallis May 23 '12 at 01:06
  • After you create the adapter you clear it and add all the entries from the deals array. How are you storing this array? If it is a member variable of the class it won't be preserved if your activity is killed by the system. Do you save the activity state when it is paused or stopped? http://developer.android.com/guide/topics/fundamentals/activities.html#SavingActivityState – Jason Hanley May 23 '12 at 01:49
  • When in the if body in the method it returns at the end so the adapter is not instantly cleared. The adapter is stored as an instance variable in the ListFragment class. The ArrayList itself is not stored, as it changes frequently. No state is currently saved. It seems that when the app is launched again after being killed the problem occurs. What does android do differently when relaunching an app after it is killed by the system as opposed to starting the app from scratch? – bencallis May 23 '12 at 02:03
  • Sorry, missed the return statement. If your app is relaunched after being killed it should behave basically the same as being created fresh. The only difference would be if you were saving state. It seems like the list is not getting the event that the list data has changed or updated, but this should be taken care of in `setListAdapter`. You could try calling `notifyDataSetChanged` after setting the list adapter. The problem could be somewhere else in your code. You can listen for changes to the adapter data by registering a listener with `registerDataSetObserver` to see what is going on. – Jason Hanley May 23 '12 at 02:32
  • Thanks for your thorough response. I had previously tried notifydataset changed hoping it would just work but unfortunately not. Once the list is empty any changes the user makes to the list are not reflected despite the adapter being added to. So it is as if the view has the wrong adapter. I will try and observer the adapter now see if I can see anything else. – bencallis May 23 '12 at 11:44
  • I have added an observer and it is behaving as expected. It seems the adapter is being updated corectly and items are being added (the thumbnails are downloaded showing they have been added in the list), it is just not showing to the user. Instead the empty list view is showing. – bencallis May 23 '12 at 22:56
  • Try calling `setListapdapter` with null before setting the actual adapter.. – Ron May 30 '12 at 16:50
  • This method looks ok and the problem is likely to be in connection with the activity/fragment lifecycle. It would be good if you could share the full activity/fragment source (preferably not in the post, but with a link). Also: what do you mean by "resume"? Was the app really killed or was it just sent to the background? – balazsbalazs May 30 '12 at 21:38
  • The problem with the bug is it is extremely hard to recreate. The problem seems to occur when the app has been previously launched and the user has since opened many applications. When the relaunch the app the oncreate of the main activity is called so I guess the app was killed. The strange thing is if the user experiences the bug and kills the app and relaunches it it always seems to work. – bencallis May 30 '12 at 23:56
  • Well, what you're saying is perfectly in line with what I was thinking. The app itself wasn't killed probably, just the activity. The system tries to recreate the activity's last state, calling the onRestoreInstanceState() - note, that this method will not be called if the app was killed. The listActivity ensures in this method, that the list exist: if not, inflates the default layout. Please, share the activity code somewhere, I can help after that. Until that, I can only guess. :)\ – balazsbalazs May 31 '12 at 15:59
  • Sorry I have been away on holiday and extremely busy with work. I have made some progress on this issue and it was as you said. I now have the refresh button work as it should but when the activity is killed and the user returns the list is empty. Is there a way to check if the activity was killed/list is empty to repopulate it if required onResume? – bencallis Jun 27 '12 at 20:14

2 Answers2

0

You can try one or more of the following options

  • Try calling setListShown(false) just before setting the list adapter.

  • Otherwise, do
    setListShown(false);
    setListAdapter(null);

  • Otherwise, do requestLayout()

Ron
  • 24,175
  • 8
  • 56
  • 97
0

One idea (pretty long shot :)

If the app itself wasn't killed, just the activity, the system tries to recreate the activity's last state, calling the onRestoreInstanceState(). This method will not be called if the app was killed - so this is one of the big differences between the two.

The ListActivity overrides the onRestoreInstanceState(). It ensures, that the List exists, before it goes on. If the list does not exist, it inflates it from the default place.

If you're setting the contentView in the onResume() try to move it to onCreate(), that may solve the problem.

I can help more, if I see the activty's code.

balazsbalazs
  • 4,071
  • 1
  • 24
  • 29