Подтвердить что ты не робот

Android 3.0 - в чем преимущества использования экземпляров LoaderManager именно так?

С 3.0 мы получили фантазию LoaderManager, которая обрабатывает загрузку данных с использованием AsyncTaskLoader, CursorLoader и других пользовательских экземпляров Loader. Но, читая через документы для них, я просто не мог понять: как это лучше, чем просто использовать старый добрый AsyncTask для загрузки данных?

4b9b3361

Ответ 1

Ну, их намного проще реализовать, и заботиться обо всем, что касается управления жизненным циклом, гораздо меньше подвержено ошибкам.

Просто посмотрите пример кода, чтобы показать результат запроса курсора, который позволяет пользователю интерактивно фильтровать результирующий набор через поле ввода запроса в панели действий:

public static class CursorLoaderListFragment extends ListFragment
        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {

    // This is the Adapter being used to display the list data.
    SimpleCursorAdapter mAdapter;

    // If non-null, this is the current filter the user has provided.
    String mCurFilter;

    @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.
        setEmptyText("No phone numbers");

        // We have a menu item to show in action bar.
        setHasOptionsMenu(true);

        // Create an empty adapter we will use to display the loaded data.
        mAdapter = new SimpleCursorAdapter(getActivity(),
                android.R.layout.simple_list_item_2, null,
                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
        setListAdapter(mAdapter);

        // Prepare the loader.  Either re-connect with an existing one,
        // or start a new one.
        getLoaderManager().initLoader(0, null, this);
    }

    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // Place an action bar item for searching.
        MenuItem item = menu.add("Search");
        item.setIcon(android.R.drawable.ic_menu_search);
        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        SearchView sv = new SearchView(getActivity());
        sv.setOnQueryTextListener(this);
        item.setActionView(sv);
    }

    public boolean onQueryTextChange(String newText) {
        // Called when the action bar search text has changed.  Update
        // the search filter, and restart the loader to do a new query
        // with this filter.
        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
        getLoaderManager().restartLoader(0, null, this);
        return true;
    }

    @Override public boolean onQueryTextSubmit(String query) {
        // Don't care about this.
        return true;
    }

    @Override public void onListItemClick(ListView l, View v, int position, long id) {
        // Insert desired behavior here.
        Log.i("FragmentComplexList", "Item clicked: " + id);
    }

    // These are the Contacts rows that we will retrieve.
    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
        Contacts._ID,
        Contacts.DISPLAY_NAME,
        Contacts.CONTACT_STATUS,
        Contacts.CONTACT_PRESENCE,
        Contacts.PHOTO_ID,
        Contacts.LOOKUP_KEY,
    };

    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // This is called when a new Loader needs to be created.  This
        // sample only has one Loader, so we don't care about the ID.
        // First, pick the base URI to use depending on whether we are
        // currently filtering.
        Uri baseUri;
        if (mCurFilter != null) {
            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                    Uri.encode(mCurFilter));
        } else {
            baseUri = Contacts.CONTENT_URI;
        }

        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
                + Contacts.DISPLAY_NAME + " != '' ))";
        return new CursorLoader(getActivity(), baseUri,
                CONTACTS_SUMMARY_PROJECTION, select, null,
                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // Swap the new cursor in.  (The framework will take care of closing the
        // old cursor once we return.)
        mAdapter.swapCursor(data);
    }

    public void onLoaderReset(Loader<Cursor> loader) {
        // This is called when the last Cursor provided to onLoadFinished()
        // above is about to be closed.  We need to make sure we are no
        // longer using it.
        mAdapter.swapCursor(null);
    }
}

Правильно выполняя этот полный пример самостоятельно с помощью AsyncTask, будет задействовано намного больше кода... и даже тогда вы собираетесь внедрить что-то как полноценное и хорошо работающее? Например, будет ли ваша реализация сохранять загруженный курсор во всех изменениях конфигурации активности, поэтому его не нужно запрашивать при создании новых экземпляров? LoaderManager/Loader сделает это автоматически для вас, а также заботится о правильном создании и закрытии курсора на основе жизненного цикла активности.

Также обратите внимание, что использование этого кода не требует, чтобы вы вообще думали о том, чтобы выполнить длительную работу с основного потока пользовательского интерфейса. LoaderManager и CursorLoader позаботятся обо всем этом, гарантируя, что вы никогда не заблокируете основной поток при взаимодействии с курсором. Чтобы сделать это правильно, на самом деле вам нужно иметь одновременно два объекта курсора одновременно в точках, поэтому вы можете продолжать отображать интерактивный интерфейс с текущим курсором, пока загружается следующий для показа. LoaderManager делает все это для вас.

Это просто более простой API - не нужно знать о AsyncTask и думать о том, что нужно запускать в фоновом режиме, не нужно думать о жизненном цикле активности или о том, как использовать старые API-интерфейсы управляемого управления в Activity ( который так же не работал, как и LoaderManager).

(Btw не забывает новую "поддерживающую" статическую библиотеку, которая позволяет использовать полный API LoaderManager в старых версиях Android до 1.6!)