我在我的应用程序中使用Loader,并根据我使用此Loader在COntacts上执行的查询得到的结果执行一些计算并将它们存储回Sqlite DB中。我希望这个操作是异步的,但我在使用Async任务之间感到困惑,因为我要返回很多不同的数据类型,或者我应该使用简单的处理程序或AsyncTaskLoader,我希望它很简单,因为我是新手装载机。我试图搜索AsyncTaskLoader的例子,但它似乎是火箭科学,在我的场景的上下文中的三个中的任何一个的基本和简单的功能示例将是非常有帮助的。
答案 0 :(得分: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。
答案 1 :(得分:12)
自Honeycomb和v4兼容性库以来,可以使用AsyncTaskLoader
。根据我的理解,AsyncTaskLoader
可以通过配置更改(如屏幕翻转)继续存在。但是使用AsyncTask
可能会导致配置更改陷入困境。
关键信息:AsyncTaskLoader
是Loader
的子类。该类执行与AsyncTask相同的功能,但更好一点,它在处理配置更改(屏幕方向)时也很有用。
这里给出了一个非常好的例子和解释。 http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html
Google直接在API文档中有一个很好的例子。 Android设计模式提供了一些更详细的信息以及Loaders背后的原因。
答案 2 :(得分:9)
以下是实施
上查看同一篇文章AsyncTaskLoader
的分步教程。或在Medium
在MainActivity上实现LoaderManager.LoaderCallbacks<String>
并创建一个static int
来唯一标识您的加载器并创建一个String键以将字符串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";
//...}
覆盖MainActivity中的onCreateLoader
,onLoadFinished
和onLoaderReset
个功能
@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>
作为匿名内部类,this
作为构造函数的参数并覆盖loadInBackground
&amp;匿名内部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();
}
};
}
在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;
}
在onCreate
内部,使用OPERATION_SEARCH_LOADER作为ID初始化加载程序,对于捆绑包为null,对于上下文,这是
getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);
现在,无论何时何地想要触发加载器,都可以调用此方法
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,你已经完成了,只是为了提醒你NetworkUtils.getResponseFromHttpUrl(url);
是我的自定义函数,它将字符串转换为URL
,后者又用于创建HTTPUrlConnection
答案 3 :(得分:7)
我喜欢这个简短的例子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();
}
}
答案 4 :(得分:1)
简化,可能
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();
}
将此部分作为您的活动。该示例模拟延迟,但使新条目易于识别,因为它们将具有不同的时间戳后缀。当然,您还需要使用RecyclerView来显示数据,this question的答案似乎非常好。
此示例中的加载程序是保持对父活动的引用的内部类。它必须是外部静态类,在生产中没有这样的参考。
答案 5 :(得分:0)
我更喜欢使用Bolts-Android。这很容易。
https://github.com/BoltsFramework/Bolts-Android
Task.callInBackground(new Callable<Void>() {
public Void call() {
// Do a bunch of stuff.
}
}).continueWith(...);