我的问题与这个问题基本相同:
Sometimes don’t get onCreateLoader callback after calling initLoader
我有一个包含在ViewPager中的2个ListFragments.它们首先加载正常,但是当我更改方向时,initLoader方法不会调用onCreateLoader.
但是,如果我恢复到初始方向,一切都很好.
这是我的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; } } }
这是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,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,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.QR_MESSAGE}; return new CursorLoader(context,HistoryContentProvider.CONTENT_URI,projection,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,int position,long id) { //do something } }
我尝试过这些方法的不同组合destroyLoader,restartLoader,但没有任何成功……有谁知道发生了什么?
编辑
使用LoaderManager.enableDebugLogging(true)我得到以下日志:
第一次加载就是这样
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}
然后,当我改变方向时,它会给出这个
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}}
然后,当我回到初始方向时,我得到一个错误,但然后一切都再次加载:
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}
然后日志重复进行以进一步改变方向.为什么CursorLoader被停止而没有重新启动?我该如何重新启动它们?
编辑2
由于nikis,我解决了我的问题,但他提出了一个有趣的问题:
为什么android.app.LoaderManager无法正常工作?
这是使用支持版本后的日志.
第一个加载提供与上面相同的内容,但在方向更改后,重新使用游标不会阻止它们.
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}
当我回到初始方向时,这就是结果:
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}
解决方法
您可以使用LoaderManager.enableDebugLogging(true)来调试加载器行为,或者您可以尝试使用getSupportLoadermanager()而不是getLoaderManager()