AsyncTaskLoader basic example. (Android)

前端 未结 6 1654
[愿得一人]
[愿得一人] 2020-12-01 02:56

I am using a Loader in my application and based on the result I get from the query I perform on COntacts using this Loader I perform some calculations and store them back in

相关标签:
6条回答
  • 2020-12-01 03:31

    Simplifying hard, maybe

      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();
      }
    

    Make this part of your Activity. The sample simulates delay, but makes new entries easy to recognize because they will have the different time stamp suffix. Of course you also need RecyclerView to display the data, the answer to this question seems very good.

    The loader in this example is the inner class that keeps the reference to the parent activity. It must be external static class without such reference in production.

    0 讨论(0)
  • 2020-12-01 03:36

    Here's step by step tutorial to implement AsyncTaskLoader. or check out this same article on Medium

    1. Implement LoaderManager.LoaderCallbacks<String> on MainActivity and create a static int to uniquely identify your loader and create a String key to pass string url to your loader

      public 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";
      //...}
      
    2. Override onCreateLoader,onLoadFinishedand onLoaderReset functions inside 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
      }
      
    3. inside onCreateLoader() return a new AsyncTaskLoader<String> as an anonymous inner class with this as the constructor's parameter and override loadInBackground & onStartLoading inside anonymous inner class

      @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();
              }
          };
      }
      
    4. Inside loadInBackground make a network call using HTTPUrlConnection or OKHttp or anything that you 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;
          }
      
    5. Inside onCreate initialize the loader with OPERATION_SEARCH_LOADER as the ID, null for the bundle, and this for the context

      getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);
      
    6. Now call this method, whenever and wherever you want to trigger the loader

      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, you are done, just to remind you NetworkUtils.getResponseFromHttpUrl(url); is my custom function which take string convert it into URL which in turn used to create HTTPUrlConnection

    0 讨论(0)
  • I like this brief example AsyncTask and 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();
       }
    }
    
    0 讨论(0)
  • 2020-12-01 03:48

    Since Honeycomb and the v4 Compatibility Library it is possible to use AsyncTaskLoader. From what I understand, the AsyncTaskLoader can survive through config changes like screen flips. But using AsyncTask you can mess up with configuration changes.

    Key information: AsyncTaskLoader is subclass of Loader. This class performs the same function as the AsyncTask, but a bit better, it can also be useful in handling configuration changes (screen orientation).

    A very good example and explanation is given here. http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html

    Google has a pretty good example directly in the API Docs. Android Design Patterns provides some more detail and the reasoning behind Loaders.

    This tutorial will definetly help You. http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html

    0 讨论(0)
  • 2020-12-01 03:49

    If you wish to use AsyncTaskLoader, here's a nice sample for you.

    EDIT: I've decided to make a simpler solution (based on this repo):

    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;
        }
    }
    

    Usage:

    in your activity:

            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) {
                }
            });
    

    inner static class , or a normal class:

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

    Update: starting from support library 27.1.0, things changed a bit (link here) :

    In version 27.1.0, onStartLoading() is called every time the Activity is started. Since you call deliverResult() in onStartLoading(), you trigger onLoadFinished(). This is Working as Intended.

    You should remove your call to deliverResult() from onStartLoading() as it is not needed (Loaders already deliver results computed in loadInBackground() without any additional work needed on your part).

    I've updated the code above for this change.


    EDIT: Updated, kotlin version can be found here.

    0 讨论(0)
  • 2020-12-01 03:54

    I prefer using Bolts-Android. it is very easy.

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

    Task.callInBackground(new Callable<Void>() {
      public Void call() {
        // Do a bunch of stuff.
      }
    }).continueWith(...);
    
    0 讨论(0)
提交回复
热议问题