7

Problem Description

Application which I'm writing has 3 Fragments. Main Fragment Activity has a Search Box with Search button when I press on a Search button the function below is called:

Fragments.values()[tabControl.getCurrentItem()].getFragment().search(tv.getText().toString(), MainActivity.this.getApplicationContext());

Everything works Okay until I press HOME button wait for 30 minutes and relaunch application, after I press Search button in FragmentYellowPages Fragment application crashes on the lines

lvYellowPages.setVisibility(View.VISIBLE);

as lvYellowPages in this case is null, but In a logs I can see that onCreateView function is called. Can you please help me, I can share my code with you.


FragmentPagerAdapter

public class FragmentAdapter extends FragmentPagerAdapter {

    Fragments[] mFragments;

    public FragmentAdapter(FragmentManager fm) {
        super(fm);
    
        mFragments = Fragments.values();
    }

    public enum Fragments {
    
        Favorites(App.getStringByResId(R.string.favorites), new FragmentFavorites()),
        Categories(App.getStringByResId(R.string.categories), new FragmentCategories()),
        YellowPages(App.getStringByResId(R.string.yellow_pages), new FragmentYellowPages());
    
        Fragments(String title, BaseListFragment fragment) {
            this.mTitle = title;
            this.mFragment = fragment;
        }
        
        String mTitle;
        BaseListFragment mFragment;
    
        public String getTitle() {
            return mTitle;
        }
            
        public BaseListFragment getFragment() {
            return mFragment;
        }
    };

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragments[position].getTitle();
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments[position].getFragment();
    }

    @Override
    public int getCount() {
        return mFragments.length;
    }
};

List Fragment

public class FragmentYellowPages extends BaseListFragment {

    final static String TAG = FragmentYellowPages.class.getSimpleName();

    ListView lvYellowPages;
    TextView tvInfo;
    SimpleCursorAdapter scAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    
        View yellowPagesView = inflater.inflate(R.layout.fragment_yellow_pages, container, false);
    
        Log.i(TAG, "onCreateView( )");
    
        tvInfo = (TextView) yellowPagesView.findViewById(R.id.tvYellowPagesInfo);
        lvYellowPages = (ListView) yellowPagesView.findViewById(android.R.id.list);
    
        return yellowPagesView;
    
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        Log.i(TAG, "onCreate( )");
    
        setRetainInstance(true);

    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    
        Log.i(TAG, "onActivityCreated( )"); 
    }


    @Override
    public void onResume() {    
        super.onResume();
    
        Log.i(TAG, "onResume( )");      
    }

    void updateAdapterOnSearch(final String tts, final String lang) {
        Log.i(TAG, String.format("updateAdapterOnSearch(%s, %s)", tts, lang));
    
        /* Show "Yellow Pages" list view and hide Info text view. */
        lvYellowPages.setVisibility(View.VISIBLE);
        tvInfo.setVisibility(View.GONE);
        /* Some code .... */
    }

    @Override
    public void search(final String tts, final Context context) {
        Log.i(TAG, String.format("search(%s)", tts));
    
        /* If user search for the text. */ 
        if (tts != null && tts.length() != 0) {
            updateAdapterOnSearch(tts, lang);
        }
    }

};

Fragment Layout

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffc8"
    android:tag="FRAGMENT_YELLOW_PAGES_TAG" >

    <ListView android:id="@android:id/list"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:visibility="gone"
              android:background="#ffffc8"/>

    <TextView android:id="@+id/tvYellowPagesInfo"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_centerHorizontal="true"
              android:layout_centerVertical="true"
              android:gravity="center"
              android:padding="5dp"
              android:text="@string/no_companies_were_found"
              android:visibility="gone"/>

</RelativeLayout>
Community
  • 1
  • 1
Viktor Apoyan
  • 10,241
  • 20
  • 81
  • 141
  • Do you see "onCreateView" Log when relaunching app after Home button press or just when the app is started for the first time? – sinisha Jun 20 '13 at 09:19
  • 1
    @ViToBrothers Just add `mViewPager.setSaveEnabled(false);` in your `onCreate` function. Oh and you do not need to wait 30 minutes to reproduce, get a task killer and kill the app then open it. – Sherif elKhatib Jun 20 '13 at 09:43
  • @SherifelKhatib I will try it and let you know. If you will be right please add your comment as an answer in order I can accept it. – Viktor Apoyan Jun 20 '13 at 11:11
  • @SherifelKhatib mViewPager.setSaveEnabled(false); doesn't help :( it still crashes. – Viktor Apoyan Jun 20 '13 at 16:17

1 Answers1

8

It's hard to pinpoint the exact problem but I'm almost sure it has to do with the way you built the adapter for the ViewPager.

Everything works Okay until I press HOME button wait for 30 minutes and relaunch application, after I press Search button in FragmentYellowPages Fragment application crashes

As the Activity is in the background(and apparently killed) it will recreate its content when comming back to the foreground. At this point the ViewPager will rebuild its content, but its adapter will not call getItem() as it has all the data to recreate the previous fragments on it own. So you'll end up with two fragment version, the ones currently visible in the ViewPager(so you see the onCreateView() being called) and those that are in the enum. Those fragments from the enum aren't tied to the Activity and so the entire callbacks stack isn't run for them, so onCreateView() will not be called, which will make your lvYellowPages ListView null.

Btw, this is the first time I've seen the fragments of an adapter being built in a enum and I recommend that you avoid this.

The solution is to not reference the fragments of the ViewPager outside. Make the getItem() method return a Fragment instance and if you need to access those fragment there are ways to do it without storing the ViewPager's fragment in lists/enums etc(a quick search on stackoverflow should show you a question about this):

@Override
public Fragment getItem(int position) {
    switch (position) {
    case 0:
       return new FragmentFavorites();
    case 1:
       return new FragmentCategories();
    case 2:
       return new FragmentYellowPages();
    }
    return null;
}

@Override
public int getCount() {
    return 3;
}

Also:

setRetainInstance(true);

I wouldn't use this. It doesn't make sense to retain a fragment used by a ViewPager(as I don't think you're planing to move it somewhere else) and it will also not protect you from the activity being killed in the background.

Viktor Apoyan
  • 10,241
  • 20
  • 81
  • 141
user
  • 85,380
  • 17
  • 189
  • 186
  • can you please bring an example of how to get fragment :( I search through internet and find `findFragmentByTag` method but then I call it it return null :(. I change my code in way which you say, but now I can't get fragment. – Viktor Apoyan Jun 20 '13 at 17:34
  • @ViToBrothers This helps? http://stackoverflow.com/questions/7379165/update-data-in-listfragment-as-part-of-viewpager – user Jun 20 '13 at 17:36
  • why it's so hard ?? I can't understand how to do that :( – Viktor Apoyan Jun 20 '13 at 18:39
  • 1
    @ViToBrothers All you need to do is build a tag like `"android:switcher:"+R.id.theIdOfTheViewPager+":"+ position`(where the position is the position in the `ViewPager`, if you programmatically set and id for the ViewPager, use that). This is assuming you still use a `FragmentPagerAdapter`. Did you tried this and the fragment can't be found? – user Jun 20 '13 at 18:53