Android AsyncTaskLoader 基本示例。(安卓)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20279216/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:34:36  来源:igfitidea点击:

AsyncTaskLoader basic example. (Android)

androidandroid-asynctaskandroid-handler

提问by Skynet

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 a Sqlite DB. I want this operation to be Asynchronous, however I am confused between using an Async task, as I have lot of different data types to return or should I use a simple handler or an AsyncTaskLoader, I want it to be simple as I am new to Loaders. I tried to search around for examples of AsyncTaskLoader but it seems rocket science, a basic and simple functional example of any of the three in the context of my scenario would be a lot helpful.

我在我的应用程序中使用了一个加载器,并根据我从使用这个加载器对联系人执行的查询中得到的结果,我执行了一些计算并将它们存储回 Sqlite 数据库中。我希望这个操作是异步的,但是我在使用异步任务之间感到困惑,因为我有很多不同的数据类型要返回,或者我应该使用简单的处理程序还是 AsyncTaskLoader,我希望它很简单,因为我是新手装载机。我试图四处搜索 AsyncTaskLoader 的示例,但似乎火箭科学,在我的场景中,这三个中任何一个的基本和简单的功能示例都会有很大帮助。

回答by android developer

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

如果您希望使用 AsyncTaskLoader,这里有一个很好的示例。

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

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

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

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).

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

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

I've updated the code above for this change.

我已针对此更改更新了上面的代码。



EDIT: Updated, kotlin version can be found here.

编辑:更新后,可在此处找到 kotlin 版本。

回答by DeepakPanwar

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

由于 Honeycomb 和 v4 兼容库,可以使用AsyncTaskLoader. 据我了解,AsyncTaskLoader可以通过屏幕翻转等配置更改生存。但是使用AsyncTask您可能会弄乱配置更改。

Key information: AsyncTaskLoaderis 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).

关键信息:AsyncTaskLoaderLoader. 此类执行与 AsyncTask 相同的功能,但更好一些,它还可用于处理配置更改(屏幕方向)。

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

这里给出了一个很好的例子和解释。 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.

谷歌直接在 API Docs 中有一个很好的例子。Android Design Patterns 提供了更多细节和 Loaders 背后的推理。

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

本教程将绝对帮助您。http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html

回答by Sanjeev

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

这是实现AsyncTaskLoader. 或Medium上查看同一篇文章

  1. Implement LoaderManager.LoaderCallbacks<String>on MainActivity and create a static intto 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 onLoaderResetfunctions 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 thisas the constructor's parameter and override loadInBackground& onStartLoadinginside 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 loadInBackgroundmake 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 onCreateinitialize 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);
        }
    }
    
  1. LoaderManager.LoaderCallbacks<String>在 MainActivity 上实现并创建一个static int以唯一标识您的加载程序并创建一个字符串键以将字符串 url 传递给您的加载程序

    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. 覆盖onCreateLoader,onLoadFinishedonLoaderResetMainActivity 中的函数

    @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. insideonCreateLoader()返回一个 newAsyncTaskLoader<String>作为匿名内部类,this作为构造函数的参数并覆盖loadInBackground&onStartLoading内部匿名内部类

    @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. 在内部loadInBackground使用 HTTPUrlConnection 或 OKHttp 或您使用的任何内容进行网络调用。

     @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. 内部onCreate使用 OPERATION_SEARCH_LOADER 作为 ID 初始化加载器,包为 null,上下文为 this

    getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);
    
  6. 现在调用这个方法,无论何时何地你想触发加载器

    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 URLwhich in turn used to create HTTPUrlConnection

Walla,你已经完成了,只是提醒你NetworkUtils.getResponseFromHttpUrl(url);是我的自定义函数,它将字符串转换为字符串URL,然后用于创建HTTPUrlConnection

回答by JDOaktown

I like this brief example AsyncTask and AsyncTaskLoader.

我喜欢这个简短的例子 AsyncTask 和 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();
   }
}

回答by Audrius Meskauskas

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 questionseems very good.

将此作为您活动的一部分。该示例模拟延迟,但使新条目易于识别,因为它们将具有不同的时间戳后缀。当然你还需要 RecyclerView 来显示数据,这个问题的答案似乎很好。

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.

本例中的加载器是保持对父活动的引用的内部类。它必须是生产中没有此类引用的外部静态类。

回答by Behrouz.M

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

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

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

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

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