onCreateLoader not called when orientation changes

android

Solution

You can use `LoaderManager.enableDebugLogging(true)` to debug your loader behavior or you can try to use `getSupportLoadermanager()` instead of `getLoaderManager()`

Problem

My problem is basically the same as this one:Sometimes don't get onCreateLoader callback after calling initLoader I have 2 `ListFragments` that are contained in a `ViewPager`. They get loaded ok at first, but when I change the orientation, the `initLoader`method doesn't call the `onCreateLoader`. However, if I revert back to the initial orientation, all is fine again. Here is my code for the `FragmentActivity`: ``` import java.util.Locale; import com.d.camera.R; import com.d.camera.R.id; import com.d.camera.R.layout; import com.d.camera.R.menu; import com.d.camera.R.string; import android.app.ActionBar; import android.app.FragmentTransaction; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.NavUtils; import android.support.v4.view.ViewPager; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class HistoryFragments extends FragmentActivity implements ActionBar.TabListener { /** * The {@link android.support.v4.view.PagerAdapter} that will provide * fragments for each of the sections. We use a * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which * will keep every loaded fragment in memory. If this becomes too memory * intensive, it may be best to switch to a * {@link android.support.v4.app.FragmentStatePagerAdapter}. */ SectionsPagerAdapter mSectionsPagerAdapter; /** * The {@link ViewPager} that will host the section contents. */ ViewPager mViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_history_fragments); // Set up the action bar. final ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowHomeEnabled(false); // Create the adapter that will return a fragment for each of the three // primary sections of the app. mSectionsPagerAdapter = new SectionsPagerAdapter( getSupportFragmentManager()); // Set up the ViewPager with the sections adapter. mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mSectionsPagerAdapter); // When swiping between different sections, select the corresponding // tab. We can also use ActionBar.Tab#select() to do this if we have // a reference to the Tab. mViewPager .setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { actionBar.setSelectedNavigationItem(position); } }); // For each of the sections in the app, add a tab to the action bar. for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) { // Create a tab with text corresponding to the page title defined by // the adapter. Also specify this Activity object, which implements // the TabListener interface, as the callback (listener) for when // this tab is selected. actionBar.addTab(actionBar.newTab() .setText(mSectionsPagerAdapter.getPageTitle(i)) .setTabListener(this)); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.history, menu); return true; } @Override public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { // When the given tab is selected, switch to the corresponding page in // the ViewPager. mViewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { } @Override public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { } /** * A {@link FragmentPagerAdapter} that returns a fragment corresponding to * one of the sections/tabs/pages. */ public class SectionsPagerAdapter extends FragmentPagerAdapter { public SectionsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { // getItem is called to instantiate the fragment for the given page. // Return a DummySectionFragment (defined as a static inner class // below) with the page number as its lone argument. Fragment fragment = null; if (position == 0) { fragment = new HistoryListFragment(); } else{ fragment = new HistoryElemListFragment(); } return fragment; } @Override public int getCount() { // Show 2 total pages. return 2; } @Override public CharSequence getPageTitle(int position) { Locale l = Locale.getDefault(); switch (position) { case 0: return getString(R.string.title_section1).toUpperCase(l); case 1: return getString(R.string.title_section2).toUpperCase(l); case 2: return getString(R.string.title_section3).toUpperCase(l); } return null; } } } ``` and here is the code of one of the `ListFragments`: ``` import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import com.d.camera.HistoryContentProvider; import com.d.camera.HistoryDatabase; import com.d.camera.HistoryEntry; import com.d.camera.R; import android.app.LoaderManager; import android.content.Context; import android.content.CursorLoader; import android.content.Intent; import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.view.ViewGroup; import android.widget.FilterQueryProvider; import android.widget.ListView; import android.widget.SimpleCursorAdapter; import android.widget.TextView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.SimpleCursorAdapter.ViewBinder; public class HistoryListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor>{ private static final int LOADER_ID = 0x01; private static final int DELETE_ID = Menu.FIRST + 1; public static final String SECTION_NUMBER = "section_number"; private SimpleCursorAdapter adapter; private Context context; public HistoryListFragment() { } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); context = getActivity(); fillData(); registerForContextMenu(getListView()); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.list_fragment, container, false); return rootView; } private void fillData() { getActivity().getLoaderManager().initLoader(LOADER_ID, null, this); String[] from = new String[] { HistoryDatabase.MOBILE_RESULT, HistoryDatabase.FP_TIMESTAMP, HistoryDatabase.PRODUCT_IMAGE,}; int[] to = new int[] { R.id.resultImage, R.id.time, R.id.productImage,}; adapter = new SimpleCursorAdapter(context, R.layout.history_element, null, from, to, 0); // We want monitor the list setup and change the milliseconds time to a readable format.******* adapter.setViewBinder(new ViewBinder(){ public boolean setViewValue(View v, Cursor c, int columnIndex) { if(columnIndex == c.getColumnIndex(HistoryDatabase.FP_TIMESTAMP)) { Long timeInMilli = c.getLong(c.getColumnIndex(HistoryDatabase.FP_TIMESTAMP)); SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH); String formatedTime = sdf.format(new Date(timeInMilli)); TextView tv = (TextView)v; tv.setText(formatedTime); return true; } return false; } }); //********************************************************************************************* setListAdapter(adapter); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, DELETE_ID, 0, R.string.menu_delete); } @Override public boolean onContextItemSelected(MenuItem item) { //... return super.onContextItemSelected(item); } // Reaction to the menu selection @Override public boolean onOptionsItemSelected(MenuItem item) { //... return super.onOptionsItemSelected(item); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { String[] projection = new String[]{HistoryDatabase.ID, HistoryDatabase.MOBILE_RESULT, HistoryDatabase.MOBILE_SCORE, HistoryDatabase.FP_TIMESTAMP, HistoryDatabase.PRODUCT_IMAGE, HistoryDatabase.QR_MESSAGE}; return new CursorLoader(context, HistoryContentProvider.CONTENT_URI, projection, null, null, HistoryDatabase.ID + " DESC"); } @Override public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { adapter.swapCursor(cursor); } @Override public void onLoaderReset(Loader<Cursor> cursorLoader) { adapter.swapCursor(null); } public void onListItemClick(ListView l, View v, int position, long id) { //do something } } ``` I've tried different combinations of these methods `destroyLoader`,`restartLoader`, but without any success... Does anyone know what's going on? Edit Using `LoaderManager.enableDebugLogging(true)` I get the following logs: The first load gives this ``` initLoader in LoaderManager{424a7950 in HistoryFragments{42507c68}}: args=null Starting: LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} Created new loader LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} initLoader in LoaderManager{424a7950 in HistoryFragments{42507c68}}: args=null Starting: LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} Created new loader LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} onLoadComplete: LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} onLoadFinished in CursorLoader{42faae18 id=1}: CursorWrapperInner{42506d58} onLoadComplete: LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} onLoadFinished in CursorLoader{42e5a2f8 id=2}: CursorWrapperInner{42fab1f0} ``` Then when I change the orientation, it gives this ``` Retaining in LoaderManager{424a7950 in HistoryFragments{42507c68}} Retaining: LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} Retaining: LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} Destroying Inactive in LoaderManager{424a7950 in HistoryFragments{42507c68}} initLoader in LoaderManager{424a7950 in HistoryFragments{426d0af0}}: args=null Re-using existing loader LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} initLoader in LoaderManager{424a7950 in HistoryFragments{426d0af0}}: args=null Re-using existing loader LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} Finished Retaining in LoaderManager{424a7950 in HistoryFragments{426d0af0}} Finished Retaining: LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} Stopping: LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} Finished Retaining: LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} Stopping: LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} ``` Then when I come back to the initial orientation, I get an error but then everything gets loaded again: ``` Retaining in LoaderManager{424a7950 in HistoryFragments{426d0af0}} Called doRetain when not started: LoaderManager{424a7950 in HistoryFragments{426d0af0}} java.lang.RuntimeException: here at android.app.LoaderManagerImpl.doRetain(LoaderManager.java:795) at android.app.Activity.performStop(Activity.java:5497) at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3591) at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3654) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3854) at android.app.ActivityThread.access$800(ActivityThread.java:159) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1322) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:176) at android.app.ActivityThread.main(ActivityThread.java:5419) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862) at dalvik.system.NativeStart.main(Native Method) Destroying Active in LoaderManager{424a7950 in HistoryFragments{426d0af0}} Destroying: LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} Reseting: LoaderInfo{42e5a280 #2 : CursorLoader{42e5a2f8}} Destroying: LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} Reseting: LoaderInfo{424a7da0 #1 : CursorLoader{42faae18}} Destroying Inactive in LoaderManager{424a7950 in HistoryFragments{426d0af0}} Destroying Active in LoaderManager{424a7950 in HistoryFragments{426d0af0}} Destroying Inactive in LoaderManager{424a7950 in HistoryFragments{426d0af0}} initLoader in LoaderManager{4271b070 in HistoryFragments{42702c68}}: args=null Starting: LoaderInfo{4271b690 #1 : CursorLoader{4271b708}} Created new loader LoaderInfo{4271b690 #1 : CursorLoader{4271b708}} initLoader in LoaderManager{4271b070 in HistoryFragments{42702c68}}: args=null Starting: LoaderInfo{42720ab8 #2 : CursorLoader{42720b30}} Created new loader LoaderInfo{42720ab8 #2 : CursorLoader{42720b30}} onLoadComplete: LoaderInfo{4271b690 #1 : CursorLoader{4271b708}} onLoadFinished in CursorLoader{4271b708 id=1}: CursorWrapperInner{4271f518} onLoadComplete: LoaderInfo{42720ab8 #2 : CursorLoader{42720b30}} onLoadFinished in CursorLoader{42720b30 id=2}: CursorWrapperInner{427222c0} ``` The logs then repeat themselves for further orientation changes. Why are the `CursorLoaders` being stopped and not restarted? How can I restart them? EDIT 2 I fixed my problem thanks to nikis, but he raised an interesting question: why was the `android.app.LoaderManager` not working? Here's the logs after using the support version. The first load gives the same thing as above, but after the orientation change, re-using the cursors doesn't stop them. ``` Retaining in LoaderManager{43945c70 in HistoryFragments{427cb468}} Retaining: LoaderInfo{439d8a58 #2 : CursorLoader{439d8ad0}} Retaining: LoaderInfo{432dea88 #1 : CursorLoader{4393ad48}} Destroying Inactive in LoaderManager{43945c70 in HistoryFragments{427cb468}} initLoader in LoaderManager{43945c70 in HistoryFragments{438f0a38}}: args=null Re-using existing loader LoaderInfo{432dea88 #1 : CursorLoader{4393ad48}} initLoader in LoaderManager{43945c70 in HistoryFragments{438f0a38}}: args=null Re-using existing loader LoaderInfo{439d8a58 #2 : CursorLoader{439d8ad0}} Starting in LoaderManager{43945c70 in HistoryFragments{438f0a38}} Finished Retaining in LoaderManager{43945c70 in HistoryFragments{438f0a38}} Finished Retaining: LoaderInfo{439d8a58 #2 : CursorLoader{439d8ad0}} onLoadFinished in CursorLoader{439d8ad0 id=2}: CursorWrapperInner{43952be0} Finished Retaining: LoaderInfo{432dea88 #1 : CursorLoader{4393ad48}} onLoadFinished in CursorLoader{4393ad48 id=1}: CursorWrapperInner{43952fa8} ``` And when I come back to the initial orientation, this is the result: ``` Retaining in LoaderManager{43945c70 in HistoryFragments{438f0a38}} Retaining: LoaderInfo{439d8a58 #2 : CursorLoader{439d8ad0}} Retaining: LoaderInfo{432dea88 #1 : CursorLoader{4393ad48}} Destroying Inactive in LoaderManager{43945c70 in HistoryFragments{438f0a38}} initLoader in LoaderManager{43945c70 in HistoryFragments{432cab50}}: args=null Re-using existing loader LoaderInfo{432dea88 #1 : CursorLoader{4393ad48}} initLoader in LoaderManager{43945c70 in HistoryFragments{432cab50}}: args=null Re-using existing loader LoaderInfo{439d8a58 #2 : CursorLoader{439d8ad0}} Starting in LoaderManager{43945c70 in HistoryFragments{432cab50}} Finished Retaining in LoaderManager{43945c70 in HistoryFragments{432cab50}} Finished Retaining: LoaderInfo{439d8a58 #2 : CursorLoader{439d8ad0}} onLoadFinished in CursorLoader{439d8ad0 id=2}: CursorWrapperInner{43952be0} Finished Retaining: LoaderInfo{432dea88 #1 : CursorLoader{4393ad48}} onLoadFinished in CursorLoader{4393ad48 id=1}: CursorWrapperInner{43952fa8} ```

Original source

Related problems