Android 3.0 - ¿cuáles son las ventajas de usar instancias de LoaderManager exactamente?


Con la 3.0 tenemos la fantasía LoaderManager, que maneja la carga de datos usando AsyncTaskLoader, CursorLoader y otras instancias personalizadas Loader. Pero leyendo a través de los documentos para estos simplemente no pude entender el punto: ¿cómo son estos mejores que simplemente usar el viejo AsyncTask para la carga de datos?

Author: user1422551, 2011-04-09

1 answers

Bueno, son mucho más simples de implementar, y se encargan de todo lo relacionado con la gestión del ciclo de vida, por lo que son mucho menos propensos a errores.

Simplemente mire el código de ejemplo, para mostrar el resultado de una consulta del cursor que permite al usuario filtrar interactivamente el conjunto de resultados a través de un campo de entrada de consulta en la barra de acciones:

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

    // This is the Adapter being used to display the list's 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);
    }
}

Implementar correctamente este ejemplo completo con AsyncTask va a implicar mucho más código... e incluso entonces, ¿vas a implementar algo como ¿completo y bien funcionando? Por ejemplo, ¿su implementación retendrá el cursor cargado a través de los cambios de configuración de la actividad para que no sea necesario volver a consultarlo cuando se creen las nuevas instancias? LoaderManager / Loader lo hará automáticamente por usted, además de encargarse de crear y cerrar correctamente el Cursor en función del ciclo de vida de la actividad.

También tenga en cuenta que el uso de este código no requiere que piense en absoluto en asegurarse de que el trabajo de larga duración se realice fuera del hilo principal de la interfaz de usuario. LoaderManager y CursorLoader se encargan de todo eso por ti, asegurando que nunca bloquearás el hilo principal mientras interactúas con el cursor. Para hacer esto correctamente, en realidad necesita tener dos objetos de cursor activos al mismo tiempo en puntos, para que pueda continuar mostrando una interfaz de usuario interactiva con su Cursor actual mientras se carga el siguiente que se muestra. LoaderManager hace todo eso por ti.

Esto es solo una API mucho más simple no no hay necesidad de saber acerca de AsyncTask y pensar en lo que necesita ejecutarse en segundo plano, no hay necesidad de pensar en el ciclo de vida de la actividad o cómo utilizar las antiguas API de "cursor administrado" en la Actividad (que no funcionaba tan bien como LoaderManager de todos modos).

(Por cierto, no olvides la nueva biblioteca estática "support" que te permite usar la API completa de LoaderManager en versiones anteriores de Android hasta 1.6!)

 53
Author: hackbod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-09 08:53:25