Фрагмент с AsyncTaskLoader влияет на прикрепление других фрагментов к активности
Я использовал AsyncTaskLoader для загрузки курсора из запроса к базе данных. Я следовал примеру Android разработчиков: http://developer.android.com/reference/android/content/AsyncTaskLoader.html
Но каким-то образом фрагменты, которые добавляются в адаптер страницы после этого фрагмента (который использует загрузчик), не привязываются к действию, и когда он пытается использовать методы, которым требуется действие (например, getString()), генерируется исключение. и говорит, что этот фрагмент не привязан к какой-либо деятельности.
Вот некоторый код:
Добавление фрагментов на страницу адаптера.
mAdapter = new PlaceFragmentPagerAdapter(getSupportFragmentManager()); NewFragment newFrag = new NewFragment(); mAdapter.addFragment(newShiftFrag); ListFragment listFrag = new ListFragment(); mAdapter.addFragment(listFrag); SettingsFragment settingsFrag = new SettingsFragment(); mAdapter.addFragment(settingsFrag); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter);
Реализация AsyncTaskLoader:
абстрактный открытый класс AbstractCursorLoader extends AsyncTaskLoader {
abstract protected Cursor buildCursor(); Cursor lastCursor=null; public AbstractCursorLoader(Context context) { super(context); } /** * Runs on a worker thread, loading in our data. Delegates * the real work to concrete subclass' buildCursor() method. */ @Override public Cursor loadInBackground() { Cursor cursor=buildCursor(); if (cursor!=null) { // Ensure the cursor window is filled cursor.getCount(); } return(cursor); } /** * Runs on the UI thread, routing the results from the * background thread to whatever is using the Cursor * (e.g., a CursorAdapter). */ @Override public void deliverResult(Cursor cursor) { if (isReset()) { // An async query came in while the loader is stopped if (cursor!=null) { cursor.close(); } return; } Cursor oldCursor=lastCursor; lastCursor=cursor; if (isStarted()) { super.deliverResult(cursor); } if (oldCursor!=null && oldCursor!=cursor && !oldCursor.isClosed()) { oldCursor.close(); } } /** * Starts an asynchronous load of the list data. * When the result is ready the callbacks will be called * on the UI thread. If a previous load has been completed * and is still valid the result may be passed to the * callbacks immediately. * * Must be called from the UI thread. */ @Override protected void onStartLoading() { if (lastCursor!=null) { deliverResult(lastCursor); } if (takeContentChanged() || lastCursor==null) { forceLoad(); } } /** * Must be called from the UI thread, triggered by a * call to stopLoading(). */ @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } /** * Must be called from the UI thread, triggered by a * call to cancel(). Here, we make sure our Cursor * is closed, if it still exists and is not already closed. */ @Override public void onCanceled(Cursor cursor) { if (cursor!=null && !cursor.isClosed()) { cursor.close(); } } /** * Must be called from the UI thread, triggered by a * call to reset(). Here, we make sure our Cursor * is closed, if it still exists and is not already closed. */ @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); if (lastCursor!=null && !lastCursor.isClosed()) { lastCursor.close(); } lastCursor=null; } } private static class ListLoader extends AbstractCursorLoader { private String mName; public ShiftsListLoader(Context context, String name) { super(context); mName = name; } @Override protected Cursor buildCursor() { PlacesHandler wph = new PlacesHandler(this.getContext()); return wph.GetShifts(mName); } }
Инициализация загрузчика:
@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Give some text to display if there is no data. In a real // application this would come from a resource. // TODO change to resource and back setEmptyText("Nothing here.."); // Start out with a progress indicator. setListShown(false); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this);
}
@Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new ListLoader(getActivity(), mWorkPlaceName); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Create an empty adapter we will use to display the loaded data. mAdapter = new ListCursorAdapter(getActivity(), data, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); setListAdapter(mAdapter); } @Override public void onLoaderReset(Loader<Cursor> loader) { // TODO Auto-generated method stub }
Я действительно понятия не имею, почему это происходит.
PS У меня были некоторые проблемы с созданием блоков кода в комментарии, я думаю, что в нем есть ошибка, извините.
Заранее спасибо, Элад.
1 ответ
Короткий ответ заключается в том, что вы никогда не должны предполагать, что фрагмент находится в каком-либо определенном состоянии, пока не получит соответствующий обратный вызов жизненного цикла, сигнализирующий об этом.
То, что вы видите, - это добавленная оптимизация во время ICS, которой пользуется ViewPager. FragmentPagerAdapter специально помечает фрагменты за пределами экрана как невидимые для пользователя, вызывая setUserVisibleHint. FragmentManager использует это для определения приоритетов выполнения загрузчиков, чтобы пользователь сначала увидел полностью загруженную видимую страницу, а загрузка боковых страниц не замедляет процесс загрузки видимой страницы. В частности, это задерживает перемещение фрагмента в состояние "запущено", что также происходит, когда запускаются загрузчики.
Если пользователь прокручивает на другую страницу, пока он находится в процессе, FragmentManager переместит фрагмент в начальное состояние и сразу же начнет запускать его загрузчики как часть FragmentPagerAdapter#setPrimaryItem(), поскольку этот метод помечает текущую страницу как пользовательскую. видимый.