AsyncTaskLoader基本示例。 (机器人)

问题描述 投票:21回答:6

我在我的应用程序中使用Loader,并根据我使用此Loader在COntacts上执行的查询得到的结果,我执行一些计算并将它们存储回Sqlite DB中。我希望这个操作是异步的,但我在使用Async任务之间感到困惑,因为我要返回很多不同的数据类型,或者我应该使用简单的处理程序或AsyncTaskLoader,我希望它很简单,因为我是新手装载机。我试图搜索AsyncTaskLoader的例子,但它似乎是火箭科学,在我的场景的上下文中的三个中的任何一个的基本和简单的功能示例将是非常有帮助的。

android android-asynctask android-handler
6个回答
29
投票

如果你想使用AsyncTaskLoader,here's是一个不错的样本。

编辑:我决定做一个更简单的解决方案(基于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;
    }
}

用法:

在你的活动中:

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

内部静态类或普通类:

private static class ImageLoadingTask extends AsyncTaskLoaderEx<Bitmap> {

    public ImageLoadingTask (Context context) {
        super(context);
    }

    @Override
    public Bitmap loadInBackground() {
        //TODO load and return bitmap
    }
}

更新:从支持库27.1.0开始,事情发生了一些变化(链接here):

在版本27.1.0中,每次启动Activity时都会调用onStartLoading()。由于在onStartLoading()中调用了deliverResult(),因此触发onLoadFinished()。这是按预期工作的。

您应该从onStartLoading()中删除对deliverResult()的调用,因为它不需要(加载器已经提供了在loadInBackground()中计算的结果,而您无需任何额外的工作)。

我已经更新了上面的代码以进行此更改。


编辑:更新,kotlin版本可以找到here


12
投票

自Honeycomb和v4兼容性库以来,可以使用AsyncTaskLoader。根据我的理解,AsyncTaskLoader可以通过配置更改生存,如屏幕翻转。但是使用AsyncTask你可以搞砸配置更改。

关键信息:AsyncTaskLoaderLoader的子类。此类执行与AsyncTask相同的功能,但更好一点,它在处理配置更改(屏幕方向)时也很有用。

这里给出了一个很好的例子和解释。 http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html

谷歌直接在API文档中有一个很好的例子。 Android设计模式提供了更多细节和Loaders背后的原因。

本教程肯定会对您有所帮助。 qazxsw poi


9
投票

这是一步一步的教程,以实现http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html。或者查看AsyncTaskLoader上的同一篇文章

  1. 在MainActivity上实现Medium并创建一个LoaderManager.LoaderCallbacks<String>来唯一标识您的加载器并创建一个String键以将字符串url传递给您的加载器 static int
  2. 在MainActivity中覆盖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"; //...} onCreateLoaderand和onLoadFinished函数 onLoaderReset
  3. @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 } 内部返回一个新的onCreateLoader()作为匿名内部类,使用AsyncTaskLoader<String>作为构造函数的参数,并在匿名内部类中覆盖thisloadInBackground onStartLoading
  4. @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(); } }; } 内部使用HTTPUrlConnection或OKHttp或您使用的任何内容进行网络调用。 loadInBackground
  5. @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; } 里面用OPERATION_SEARCH_LOADER作为ID初始化加载器,为bundle绑定null,这对于上下文 onCreate
  6. 现在,无论何时何地想要触发加载器,都要调用此方法 getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);

Walla,你完成了,只是为了提醒你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); } } 是我的自定义函数,它将字符串转换为NetworkUtils.getResponseFromHttpUrl(url);,后者又用于创建URL


7
投票

我喜欢这个简短的例子HTTPUrlConnection

AsyncTask and AsyncTaskLoader

1
投票

也许是为了简化

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

将此部分作为您的活动。该示例模拟延迟,但使新条目易于识别,因为它们将具有不同的时间戳后缀。当然你还需要RecyclerView来显示数据, 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(); } 的答案似乎非常好。

此示例中的加载程序是保留对父活动的引用的内部类。它必须是外部静态类,在生产中没有这样的参考。


0
投票

我更喜欢使用Bolts-Android。这很容易。

this question

https://github.com/BoltsFramework/Bolts-Android
© www.soinside.com 2019 - 2024. All rights reserved.