thread - android handler example
Ejemplo básico de AsyncTaskLoader.(Androide) (6)
Estoy usando un Loader en mi aplicación y, en función del resultado que obtengo de la consulta que realizo en COntacts utilizando este Loader, realizo algunos cálculos y los vuelvo a almacenar en un DB de Sqlite. Quiero que esta operación sea asincrónica, sin embargo, estoy confundido al usar una tarea asíncrona, ya que tengo muchos tipos de datos diferentes que devolver o si debo usar un controlador simple o un AsyncTaskLoader, quiero que sea simple ya que soy nuevo en Los cargadores Intenté buscar ejemplos de AsyncTaskLoader, pero parece una ciencia espacial, un ejemplo funcional simple y básico de cualquiera de los tres en el contexto de mi escenario sería muy útil.
Aquí hay un tutorial paso a paso para implementar
AsyncTaskLoader
. o echa un vistazo a este mismo artículo en Medium
Implemente
LoaderManager.LoaderCallbacks<String>
en MainActivity y cree unstatic int
para identificar de forma única su cargador y crear una clave de cadena para pasar la URL de cadena a su cargadorpublic class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<String>{ public static final int OPERATION_SEARCH_LOADER = 22; public static final String OPERATION_QUERY_URL_EXTRA = "query"; //...}
Reemplace las
onCreateLoader
,onLoadFinished
yonLoaderReset
dentro de MainActivity@Override public Loader<String> onCreateLoader(int id, final Bundle args) { //Here we will initiate AsyncTaskLoader return null; } @Override public void onLoadFinished(Loader<String> 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<String> loader) { //Don''t bother about it, Android Studio will override it for you }
dentro de
onCreateLoader()
devuelve un nuevoAsyncTaskLoader<String>
como una clase interna anónima conthis
como el parámetro del constructor y reemplazaloadInBackground
&onStartLoading
dentro de la clase interna anónima@Override public Loader<String> onCreateLoader(int id, final Bundle args) { return new AsyncTaskLoader<String>(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(); } }; }
Inside
loadInBackground
realiza una llamada de red utilizando HTTPUrlConnection o OKHttp o cualquier cosa que use.@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; }
Inside
onCreate
inicializa el cargador con OPERATION_SEARCH_LOADER como ID, nulo para el paquete, y esto para el contextogetSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);
Ahora llame a este método, cuando y donde quiera que desee 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<String> 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 la que toma la cadena la convierte en una URL
que a su vez se usa para crear HTTPUrlConnection
Desde Honeycomb y la biblioteca de compatibilidad v4 es posible usar AsyncTaskLoader
. Por lo que entiendo, el AsyncTaskLoader
puede sobrevivir a través de cambios en la configuración, como los cambios de pantalla. Pero al usar AsyncTask
puede desordenar los cambios de configuración.
Información clave: AsyncTaskLoader
es subclase de Loader
. Esta clase realiza la misma función que AsyncTask, pero un poco mejor, también puede ser útil para manejar los 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 ejemplo bastante bueno directamente en los documentos API. Los patrones de diseño de Android proporcionan más detalles y el razonamiento detrás de los cargadores.
Este tutorial definitivamente te ayudará. http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html
Me gusta este breve ejemplo de 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();
}
}
Prefiero usar Pernos-Android. es muy fácil.
https://github.com/BoltsFramework/Bolts-Android
Task.callInBackground(new Callable<Void>() {
public Void call() {
// Do a bunch of stuff.
}
}).continueWith(...);
Si desea utilizar AsyncTaskLoader, here''s una buena muestra para usted.
EDITAR: He decidido hacer una solución más simple (basada en este repositorio ):
public abstract class AsyncTaskLoaderEx<T> extends AsyncTaskLoader<T> {
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<Bitmap>() {
@Override
public Loader<Bitmap> onCreateLoader(final int id, final Bundle args) {
return new ImageLoadingTask(MainActivity.this);
}
@Override
public void onLoadFinished(final Loader<Bitmap> loader, final Bitmap result) {
if (result == null)
return;
//TODO use result
}
@Override
public void onLoaderReset(final Loader<Bitmap> loader) {
}
});
Clase estática interna, o una clase normal:
private static class ImageLoadingTask extends AsyncTaskLoaderEx<Bitmap> {
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 here ):
En la versión 27.1.0, se llama a onStartLoading () cada vez que se inicia la Actividad. Como llama a deliverResult () en onStartLoading (), desencadena onLoadFinished (). Esto está funcionando según lo previsto.
Debe eliminar su llamada a deliverResult () de onStartLoading () ya que no es necesario (los cargadores ya entregan los resultados calculados en loadInBackground () sin ningún trabajo adicional necesario de su parte).
He actualizado el código anterior para este cambio.
Simplificando duro, tal vez
private void loadContent() {
getLoaderManager().initLoader(1000, new Bundle(),
new LoaderManager.LoaderCallbacks<List<String>>() {
@Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
return new AsyncTaskLoader<List<String>>(MainActivity.this.getApplicationContext()) {
@Override
public List<String> loadInBackground() {
Log.i("B", "Load background data ");
ArrayList<String> 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<List<String>> loader, List<String> 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<List<String>> 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 el retraso, 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 mantiene la referencia a la actividad principal. Debe ser una clase estática externa sin tal referencia en producción.