Ejemplo básico de AsyncTaskLoader. (Androide)

Estoy usando un Loader en mi aplicación y, en función del resultado que obtengo de la consulta que realizo en COntacts con este Loader, realizo algunos cálculos y los vuelvo a almacenar en un DB Sqlite. Quiero que esta operación sea asíncrona, sin embargo, estoy confundido entre utilizar una tarea Async, ya que tengo muchos tipos de datos diferentes para devolver o si debo usar un controlador simple o un AsyncTaskLoader, quiero que sea simple ya que soy nuevo en Cargadores Traté de buscar ejemplos de AsyncTaskLoader, pero parece ciencia de cohetes, un ejemplo funcional básico y simple de cualquiera de los tres en el contexto de mi escenario sería de mucha ayuda.

Si desea utilizar AsyncTaskLoader, aquí hay una buena muestra para usted.

EDITAR: He decidido hacer una solución más simple (basada en este repository ):

public abstract class AsyncTaskLoaderEx extends AsyncTaskLoader { private static final AtomicInteger sCurrentUniqueId = new AtomicInteger(0); private T mData; public boolean hasResult = false; public static int getNewUniqueLoaderId() { return sCurrentUniqueId.getAndIncrement(); } public AsyncTaskLoaderEx(final Context context) { super(context); onContentChanged(); } @Override protected void onStartLoading() { if (takeContentChanged()) forceLoad(); //this part should be removed from support library 27.1.0 : //else if (hasResult) // deliverResult(mData); } @Override public void deliverResult(final T data) { mData = data; hasResult = true; super.deliverResult(data); } @Override protected void onReset() { super.onReset(); onStopLoading(); if (hasResult) { onReleaseResources(mData); mData = null; hasResult = false; } } protected void onReleaseResources(T data) { //nothing to do. } public T getResult() { return mData; } } 

Uso:

en tu actividad:

  getSupportLoaderManager().initLoader(TASK_ID, TASK_BUNDLE, new LoaderManager.LoaderCallbacks() { @Override public Loader onCreateLoader(final int id, final Bundle args) { return new ImageLoadingTask(MainActivity.this); } @Override public void onLoadFinished(final Loader loader, final Bitmap result) { if (result == null) return; //TODO use result } @Override public void onLoaderReset(final Loader loader) { } }); 

clase estática interna, o una clase normal:

 private static class ImageLoadingTask extends AsyncTaskLoaderEx { public ImageLoadingTask (Context context) { super(context); } @Override public Bitmap loadInBackground() { //TODO load and return bitmap } } 

Actualización: a partir de la biblioteca de soporte 27.1.0, las cosas cambiaron un poco (enlace aquí ):

En la versión 27.1.0, se llama a onStartLoading () cada vez que se inicia la actividad. Como llama a deliverResult () en onStartLoading (), activa onLoadFinished (). Esto está funcionando según lo previsto.

Debería eliminar su llamada a deliverResult () desde onStartLoading () ya que no es necesaria (los cargadores ya entregan resultados calculados en loadInBackground () sin necesidad de ningún trabajo adicional de su parte).

He actualizado el código anterior para este cambio.

Desde Honeycomb y la Biblioteca de compatibilidad v4, es posible utilizar AsyncTaskLoader . Por lo que entiendo, AsyncTaskLoader puede sobrevivir a través de cambios de configuración, como volteos de pantalla. Pero al usar AsyncTask puedes AsyncTask con los cambios de configuración.

Información clave: AsyncTaskLoader es la subclase de Loader . Esta clase realiza la misma función que la AsyncTask, pero un poco mejor, también puede ser útil en el manejo de cambios de configuración (orientación de la pantalla).

Un muy buen ejemplo y explicación se da aquí. http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html

Google tiene un buen ejemplo directamente en los API Doc. Android Design Patterns proporciona más detalles y el razonamiento detrás de los cargadores.

Este tutorial definitivamente lo ayudará. http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html

Aquí hay un tutorial paso a paso para implementar AsyncTaskLoader . o mira este mismo artículo en Medium

  1. Implemente LoaderManager.LoaderCallbacks en MainActivity y cree un static int para identificar de forma única su cargador y cree una clave String para pasar la URL de cadena a su cargador

     public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks{ public static final int OPERATION_SEARCH_LOADER = 22; public static final String OPERATION_QUERY_URL_EXTRA = "query"; //...} 
  2. Sobrescribe las onCreateLoader , onLoadFinished y onLoaderReset dentro de MainActivity

     @Override public Loader onCreateLoader(int id, final Bundle args) { //Here we will initiate AsyncTaskLoader return null; } @Override public void onLoadFinished(Loader loader, String operationResult) { //Think of this as AsyncTask onPostExecute method, the result from onCreateLoader will be available in operationResult variable and here you can update UI with the data fetched. Log.d("MAINACTIVITY","result : "+ operationResult); } @Override public void onLoaderReset(Loader loader) { //Don't bother about it, Android Studio will override it for you } 
  3. inside onCreateLoader() devuelve un nuevo AsyncTaskLoader como una clase interna anónima con this como el parámetro del constructor y anula loadInBackground & onStartLoading dentro de la clase interna anónima

     @Override public Loader onCreateLoader(int id, final Bundle args) { return new AsyncTaskLoader(this) { @Override public String loadInBackground() { //Think of this as AsyncTask doInBackground() method, here you will actually initiate Network call return null; } @Override protected void onStartLoading() { //Think of this as AsyncTask onPreExecute() method,start your progress bar,and at the end call forceLoad(); forceLoad(); } }; } 
  4. Dentro de loadInBackground realiza una llamada de red usando HTTPUrlConnection o OKHttp o cualquier cosa que uses.

      @Override public String loadInBackground() { String url = args.getString(OPERATION_QUERY_URL_EXTRA);//This is a url in string form if (url!=null&&"".equals(url)) { return null;//if url is null, return } String operationResult=""; try { operationResult = NetworkUtils.getResponseFromHttpUrl(url);//This just create a HTTPUrlConnection and return result in strings } catch (IOException e) { e.printStackTrace(); } return operationResult; } 
  5. Dentro de onCreate inicialice el cargador con OPERATION_SEARCH_LOADER como ID, nulo para el paquete, y esto para el contexto

     getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this); 
  6. Ahora llama a este método, cuando y donde quieras activar el cargador

     private void makeOperationSearchQuery(String url) { // Create a bundle called queryBundle Bundle queryBundle = new Bundle(); // Use putString with OPERATION_QUERY_URL_EXTRA as the key and the String value of the URL as the value queryBundle.putString(OPERATION_QUERY_URL_EXTRA,url); // Call getSupportLoaderManager and store it in a LoaderManager variable LoaderManager loaderManager = getSupportLoaderManager(); // Get our Loader by calling getLoader and passing the ID we specified Loader loader = loaderManager.getLoader(OPERATION_SEARCH_LOADER); // If the Loader was null, initialize it. Else, restart it. if(loader==null){ loaderManager.initLoader(OPERATION_SEARCH_LOADER, queryBundle, this); }else{ loaderManager.restartLoader(OPERATION_SEARCH_LOADER, queryBundle, this); } } 

Walla, has terminado, solo para recordarte NetworkUtils.getResponseFromHttpUrl(url); es mi función personalizada que toma la cadena convertirla en URL que a su vez solía crear HTTPUrlConnection

Me gusta este breve ejemplo AsyncTask y AsyncTaskLoader .

 class FooLoader extends AsyncTaskLoader { public FooLoader(Context context, Bundle args) { super(context); // do some initializations here } public String loadInBackground() { String result = ""; // ... // do long running tasks here // ... return result; } } class FooLoaderClient implements LoaderManager.LoaderCallbacks { Activity context; // to be used for support library: // FragmentActivity context2; public Loader onCreateLoader(int id, Bundle args) { // init loader depending on id return new FooLoader(context, args); } public void onLoadFinished(Loader loader, String data) { // ... // update UI here // } public void onLoaderReset(Loader loader) { // ... } public void useLoader() { Bundle args = new Bundle(); // ... // fill in args // ... Loader loader = context.getLoaderManager().initLoader(0, args, this); // with support library: // Loader loader = // context2.getSupportLoaderManager().initLoader(0, args, this); // call forceLoad() to start processing loader.forceLoad(); } } 

Simplificando duro, tal vez

  private void loadContent() { getLoaderManager().initLoader(1000, new Bundle(), new LoaderManager.LoaderCallbacks>() { @Override public Loader> onCreateLoader(int id, Bundle args) { return new AsyncTaskLoader>(MainActivity.this.getApplicationContext()) { @Override public List loadInBackground() { Log.i("B", "Load background data "); ArrayList data = new ArrayList<>(); for (int i = 0; i < 5000; i++) { data.add("Data." + i + " " + System.currentTimeMillis()); } try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return data; } }; } @Override public void onLoadFinished(Loader> loader, List data) { Log.i("B", "Here are your data loaded" + data); if (!loader.isAbandoned()) { mAdapter.setData(data); // Read also about RecyclerView } } @Override public void onLoaderReset(Loader> loader) { Log.i("B", "Loader reset"); } }).forceLoad(); } @Override protected void onDestroy() { // Abandon the loader so that it should not attempt to modify already dead GUI component getLoaderManager().getLoader(1000).abandon(); super.onDestroy(); } 

Haga esta parte de su actividad. La muestra simula la demora, pero hace que las nuevas entradas sean fáciles de reconocer porque tendrán el sufijo de marca de tiempo diferente. Por supuesto, también necesita RecyclerView para mostrar los datos, la respuesta a esta pregunta parece muy buena.

El cargador en este ejemplo es la clase interna que guarda la referencia a la actividad principal. Debe ser una clase estática externa sin dicha referencia en producción.

Prefiero usar Bolts-Android. es muy fácil.

https://github.com/BoltsFramework/Bolts-Android

 Task.callInBackground(new Callable() { public Void call() { // Do a bunch of stuff. } }).continueWith(...);