私はアプリケーションでローダーを使用しており、このローダーを使用してCOntactsで実行したクエリから得られた結果に基づいて、いくつかの計算を実行し、それらをSqlite DBに保存します。私はこの操作を非同期にしたいのですが、返されるデータ型がたくさんあるため、または単純なハンドラーまたはAsyncTaskLoaderを使用する必要があるため、Asyncタスクを使用することで混乱しています。ローダー。 AsyncTaskLoaderの例を探してみましたが、ロケットサイエンスのように思えます。私のシナリオでの3つのいずれかの基本的で単純な機能の例は、非常に役立ちます。
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以降、少し変更されました(link here ):
バージョン27.1.0では、onStartLoading()はアクティビティが開始されるたびに呼び出されます。 onStartLoading()でdeliverResult()を呼び出すため、onLoadFinished()をトリガーします。これは意図したとおりに機能しています。
必要ないため、onStartLoading()からdeliverResult()への呼び出しを削除する必要があります(ローダーは、追加作業なしでloadInBackground()で計算された結果を既に配信しています)。
この変更のために上記のコードを更新しました。
編集:更新、kotlinバージョンを見つけることができます- ここ 。
Honeycombとv4 Compatibility Libraryから、AsyncTaskLoader
を使用できます。私が理解していることから、AsyncTaskLoader
は画面の反転のような設定変更を通して生き残ることができます。しかし、AsyncTask
を使用すると、構成の変更を台無しにすることができます。
重要な情報:AsyncTaskLoader
はLoader
のサブクラスです。このクラスはAsyncTaskと同じ機能を実行しますが、少し改善されており、構成の変更(画面の向き)の処理にも役立ちます。
非常に良い例と説明がここにあります。 http://www.javacodegeeks.com/2013/01/Android-loaders-versus-asynctask.html
GoogleにはAPIドキュメントに直接良い例があります。 Android Design Patternsは、ローダーの背後にある詳細と推論を提供します。
このチュートリアルは間違いなくあなたを助けます。 http://www.javacodegeeks.com/2013/08/Android-custom-loader-to-load-data-directly-from-sqlite-database.html
AsyncTaskLoader
を実装するための段階的なチュートリアルを次に示します。またはMediumに関する同じ記事をご覧ください
MainActivityに_LoaderManager.LoaderCallbacks<String>
_を実装し、ローダーを一意に識別する_static int
_を作成し、文字列URLをローダーに渡すStringキーを作成します
_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
}
_
inside onCreateLoader()
は、コンストラクターのパラメーターとしてthis
を持つ匿名内部クラスとして新しい_AsyncTaskLoader<String>
_を返し、匿名内部クラス内で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();
}
};
}
_
内部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、コンテキストの場合はthis
_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);
}
}
_
これで完了です。NetworkUtils.getResponseFromHttpUrl(url);
は、カスタム関数で、文字列をURL
に変換し、HTTPUrlConnection
の作成に使用します。
この簡単な例 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();
}
}
一生懸命単純化
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も必要です。 この質問 に対する答えは非常に良いようです。
この例のローダーは、親アクティビティへの参照を保持する内部クラスです。本番環境では、このような参照のない外部静的クラスでなければなりません。
Bolts-Androidの使用を好みます。これはとても簡単だ。
https://github.com/BoltsFramework/Bolts-Android
Task.callInBackground(new Callable<Void>() {
public Void call() {
// Do a bunch of stuff.
}
}).continueWith(...);