AsyncTask
は、別のスレッドで複雑なタスクを実行するのに最適です。
ただし、AsyncTask
の実行中に方向の変更または別の構成の変更があると、現在のActivity
が破棄されて再起動されます。 AsyncTask
のインスタンスはそのアクティビティに接続されているため、失敗し、「強制終了」メッセージウィンドウが表示されます。
したがって、これらのエラーを回避し、AsyncTaskが失敗するのを防ぐための、ある種の「ベストプラクティス」を探しています。
私が今まで見たのは:
onRetainNonConfigurationInstance
を介して新しいアクティビティインスタンスで更新するActivity
が破棄されたときにタスクをキャンセルし、Activity
が再度作成されたときにタスクを再起動するだけです。いくつかのコード例:
画面回転中のAndroid AsyncTasks、パートI および パートII
問題を最もよく解決し、実装も簡単な最適なアプローチを見つけるのを手伝ってもらえますか?これを正しく解決する方法がわからないので、コード自体も重要です。
しないではなくAndroid:configChanges
を使用してこの問題に対処します。これは非常に悪い習慣です。
ではなく、Activity#onRetainNonConfigurationInstance()
を使用してください。これはモジュール性が低く、Fragment
ベースのアプリケーションには適していません。
私の記事を読む 保持されているFragment
sを使用して設定変更を処理する方法を説明できます。回転の変化に対してAsyncTask
を適切に保持する問題を解決します。基本的に、AsyncTask
内でFragment
をホストし、Fragment
でsetRetainInstance(true)
を呼び出し、保持されているAsyncTask
を介してActivity
の進行状況/結果をFragment
に報告する必要があります。
私は通常、AsyncTasksが.onPostExecute()コールバックでブロードキャストインテントを起動するようにすることでこれを解決します。したがって、それらは直接開始したアクティビティを変更しません。アクティビティは、動的なBroadcastReceiversでこれらのブロードキャストをリッスンし、それに応じて動作します。
このように、AsyncTasksは結果を処理する特定のActivityインスタンスを気にする必要がありません。終了すると「シャウト」し、タスクの結果に関心があるアクティビティがアクティブでフォーカスされている(再開された状態にある)場合、処理されます。
ランタイムがブロードキャストを処理する必要があるため、これには少しオーバーヘッドが伴いますが、通常は気にしません。デフォルトのシステム全体ではなくLocalBroadcastManagerを使用すると、速度が少し速くなると思います。
Fragment
を使用して、setRetainInstance(true)
を使用して(ユーザーが画面を回転させるときなど)ランタイム構成の変更を処理するAsyncTaskの別の例を次に示します。確定的な(定期的に更新される)プログレスバーも表示されます。
この例は、公式ドキュメント 設定変更中のオブジェクトの保持 に一部基づいています。
この例では、バックグラウンドスレッドを必要とする作業は、インターネットからUIに画像を読み込むだけです。
Alex Lockwoodは、「保持されたフラグメント」を使用してAsyncTasksで実行時構成の変更を処理する場合のベストプラクティスであると考えています。 onRetainNonConfigurationInstance()
は、LintのAndroid Studioで廃止されます。公式ドキュメントでは、Android:configChanges
を使用して、 設定変更を自分で処理する 、...
構成の変更を自分で処理すると、システムが自動的にそれらを適用しないため、代替リソースの使用がはるかに困難になる可能性があります。この手法は、構成の変更による再起動を回避する必要がある場合の最後の手段と見なされる必要があり、ほとんどのアプリケーションには推奨されません。
次に、バックグラウンドスレッドにAsyncTaskを使用する必要があるかどうかの問題があります。
AsyncTaskの公式リファレンス 警告...
AsyncTasksは、短い操作(最大で数秒)に使用するのが理想的です。スレッドを長時間実行し続ける必要がある場合は、Java.util.concurrent pacakgeが提供するさまざまなAPIを使用することを強くお勧めします。エグゼキューター、ThreadPoolExecutor、およびFutureTask。
または、サービス、ローダー(CursorLoaderまたはAsyncTaskLoaderを使用)、またはコンテンツプロバイダーを使用して非同期操作を実行することもできます。
残りの記事を次のように分割します。
基本的なAsyncTaskをアクティビティの内部クラスとして開始します(内部クラスである必要はありませんが、おそらく便利です)。この段階では、AsyncTaskはランタイム構成の変更を処理しません。
public class ThreadsActivity extends ActionBarActivity {
private ImageView mPictureImageView;
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mPictureImageView.setImageBitmap(bitmap);
}
}
/**
* Requires in AndroidManifext.xml
* <uses-permission Android:name="Android.permission.INTERNET" />
*/
private Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream)
new URL(url).getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
mPictureImageView =
(ImageView) findViewById(R.id.imageView_picture);
}
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask()
.execute("http://i.imgur.com/SikTbWe.jpg");
}
}
Fragementクラスを拡張し、独自のUIを持たないネストされたクラスRetainedFragmentを追加します。このフラグメントのonCreateイベントにsetRetainInstance(true)を追加します。データを設定および取得する手順を提供します。
public class ThreadsActivity extends Activity {
private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
...
public static class RetainedFragment extends Fragment {
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The key to making data survive
// runtime configuration changes.
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Integer,Bitmap> {
....
一番外側のActivityクラスのonCreate()でRetainedFragmentを処理します:既に存在する場合は参照します(アクティビティが再起動する場合)。存在しない場合は作成して追加します。次に、既に存在する場合は、RetainedFragmentからデータを取得し、そのデータを使用してUIを設定します。
public class ThreadsActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
final String retainedFragmentTag = "RetainedFragmentTag";
mPictureImageView =
(ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar =
(ProgressBar) findViewById(R.id.progressBar_loading);
// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must
// reference it with a tag.
mRetainedFragment =
(RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {
// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction()
.add(mRetainedFragment, retainedFragmentTag).commit();
// The Retained Fragment exists
} else {
mPictureImageView
.setImageBitmap(mRetainedFragment.getData());
}
}
UIからAsyncTaskを開始します
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
確定的な進行状況バーを追加してコーディングします。
アクティビティレイアウト。
<ScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
tools:context="com.example.mysecondapp.ThreadsActivity">
<RelativeLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:orientation="vertical"
Android:paddingBottom="@dimen/activity_vertical_margin"
Android:paddingLeft="@dimen/activity_horizontal_margin"
Android:paddingRight="@dimen/activity_horizontal_margin"
Android:paddingTop="@dimen/activity_vertical_margin">
<ImageView
Android:id="@+id/imageView_picture"
Android:layout_width="300dp"
Android:layout_height="300dp"
Android:background="@Android:color/black" />
<Button
Android:id="@+id/button_get_picture"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_alignParentLeft="true"
Android:layout_alignParentStart="true"
Android:layout_below="@id/imageView_picture"
Android:onClick="getPicture"
Android:text="Get Picture" />
<Button
Android:id="@+id/button_clear_picture"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_alignBottom="@id/button_get_picture"
Android:layout_toEndOf="@id/button_get_picture"
Android:layout_toRightOf="@id/button_get_picture"
Android:onClick="clearPicture"
Android:text="Clear Picture" />
<ProgressBar
Android:id="@+id/progressBar_loading"
style="?android:attr/progressBarStyleHorizontal"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:layout_below="@id/button_get_picture"
Android:progress="0"
Android:indeterminateOnly="false"
Android:visibility="invisible" />
</RelativeLayout>
</ScrollView>
次のアクティビティ:サブクラス化されたAsyncTask内部クラス。ランタイム構成の変更を処理するサブクラス化されたRetainedFragment内部クラス(ユーザーが画面を回転するときなど);定期的に更新される確定プログレスバー。 ...
public class ThreadsActivity extends Activity {
private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
private ProgressBar mLoadingProgressBar;
public static class RetainedFragment extends Fragment {
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The key to making data survive runtime configuration changes.
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask extends AsyncTask<String,
Integer, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
// Simulate a burdensome load.
int sleepSeconds = 4;
for (int i = 1; i <= sleepSeconds; i++) {
SystemClock.sleep(1000); // milliseconds
publishProgress(i * 20); // Adjust for a scale to 100
}
return com.example.standardapplibrary.Android.Network
.loadImageFromNetwork(
urls[0]);
}
@Override
protected void onProgressUpdate(Integer... progress) {
mLoadingProgressBar.setProgress(progress[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
publishProgress(100);
mRetainedFragment.setData(bitmap);
mPictureImageView.setImageBitmap(bitmap);
mLoadingProgressBar.setVisibility(View.INVISIBLE);
publishProgress(0);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
final String retainedFragmentTag = "RetainedFragmentTag";
mPictureImageView = (ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar = (ProgressBar) findViewById(R.id.progressBar_loading);
// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must reference it with a tag.
mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
retainedFragmentTag);
// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {
// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction().add(mRetainedFragment,
retainedFragmentTag).commit();
// The Retained Fragment exists
} else {
mPictureImageView.setImageBitmap(mRetainedFragment.getData());
}
}
public void getPicture(View view) {
mLoadingProgressBar.setVisibility(View.VISIBLE);
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
public void clearPicture(View view) {
mRetainedFragment.setData(null);
mPictureImageView.setImageBitmap(null);
}
}
この例では、実際の作業を行うライブラリ関数(上記の明示的なパッケージプレフィックスcom.example.standardapplibrary.Android.Networkで参照)...
public static Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream) new URL(url)
.getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
バックグラウンドタスクでAndroidManifest.xmlに必要な権限を追加します...
<manifest>
...
<uses-permission Android:name="Android.permission.INTERNET" />
AndroidManifest.xmlにアクティビティを追加します...
<manifest>
...
<application>
<activity
Android:name=".ThreadsActivity"
Android:label="@string/title_activity_threads"
Android:parentActivityName=".MainActivity">
<meta-data
Android:name="Android.support.PARENT_ACTIVITY"
Android:value="com.example.mysecondapp.MainActivity" />
</activity>
最近、私は良い解決策を見つけました here 。 RetainConfigurationを介したタスクオブジェクトの保存に基づいています。私の観点からすると、このソリューションは非常にエレガントであり、私にとってはそれを使い始めました。 basetaskからasynctaskをネストするだけで十分です。
これにはローダーを使用できます。 Doc ここをチェック
この投稿の@Alex Lockwoodの回答と@William&@quickdraw mcgrawの回答に基づいて: アクティビティ/フラグメントが一時停止されている場合のハンドラメッセージの処理方法 、私は一般的なソリューションを書きました。
この方法で回転が処理され、非同期タスクの実行中にアクティビティがバックグラウンドに移行した場合、アクティビティは再開されるとコールバック(onPreExecute、onProgressUpdate、onPostExecuteおよびonCancelled)を受け取るため、IllegalStateExceptionはスローされません( アクティビティ/フラグメントが一時停止されている場合のハンドラーメッセージの処理方法 )。
AsyncTask(例:AsyncTaskFragment <Params、Progress、Result>)のような一般的な引数タイプを使用して同じものを作成することは素晴らしいことですが、私はそれをすぐにはできず、現時点では時間がありませんでした。誰でも改善したい場合は、お気軽に!
コード:
import Android.app.Activity;
import Android.os.AsyncTask;
import Android.os.Bundle;
import Android.os.Message;
import Android.support.v4.app.Fragment;
import Android.support.v4.app.FragmentManager;
import Android.support.v7.app.AppCompatActivity;
public class AsyncTaskFragment extends Fragment {
/* ------------------------------------------------------------------------------------------ */
// region Classes & Interfaces
public static abstract class Task extends AsyncTask<Object, Object, Object> {
private AsyncTaskFragment _fragment;
private void setFragment(AsyncTaskFragment fragment) {
_fragment = fragment;
}
@Override
protected final void onPreExecute() {
// Save the state :
_fragment.setRunning(true);
// Send a message :
sendMessage(ON_PRE_EXECUTE_MESSAGE, null);
}
@Override
protected final void onPostExecute(Object result) {
// Save the state :
_fragment.setRunning(false);
// Send a message :
sendMessage(ON_POST_EXECUTE_MESSAGE, result);
}
@Override
protected final void onProgressUpdate(Object... values) {
// Send a message :
sendMessage(ON_PROGRESS_UPDATE_MESSAGE, values);
}
@Override
protected final void onCancelled() {
// Save the state :
_fragment.setRunning(false);
// Send a message :
sendMessage(ON_CANCELLED_MESSAGE, null);
}
private void sendMessage(int what, Object obj) {
Message message = new Message();
message.what = what;
message.obj = obj;
Bundle data = new Bundle(1);
data.putString(EXTRA_FRAGMENT_TAG, _fragment.getTag());
message.setData(data);
_fragment.handler.sendMessage(message);
}
}
public interface AsyncTaskFragmentListener {
void onPreExecute(String fragmentTag);
void onProgressUpdate(String fragmentTag, Object... progress);
void onCancelled(String fragmentTag);
void onPostExecute(String fragmentTag, Object result);
}
private static class AsyncTaskFragmentPauseHandler extends PauseHandler {
@Override
final protected void processMessage(Activity activity, Message message) {
switch (message.what) {
case ON_PRE_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPreExecute(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
case ON_POST_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPostExecute(message.getData().getString(EXTRA_FRAGMENT_TAG), message.obj); break; }
case ON_PROGRESS_UPDATE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onProgressUpdate(message.getData().getString(EXTRA_FRAGMENT_TAG), ((Object[])message.obj)); break; }
case ON_CANCELLED_MESSAGE : { ((AsyncTaskFragmentListener)activity).onCancelled(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
}
}
}
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Attributes
private Task _task;
private AsyncTaskFragmentListener _listener;
private boolean _running = false;
private static final String EXTRA_FRAGMENT_TAG = "EXTRA_FRAGMENT_TAG";
private static final int ON_PRE_EXECUTE_MESSAGE = 0;
private static final int ON_POST_EXECUTE_MESSAGE = 1;
private static final int ON_PROGRESS_UPDATE_MESSAGE = 2;
private static final int ON_CANCELLED_MESSAGE = 3;
private AsyncTaskFragmentPauseHandler handler = new AsyncTaskFragmentPauseHandler();
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Getters
public AsyncTaskFragmentListener getListener() { return _listener; }
public boolean isRunning() { return _running; }
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Setters
public void setTask(Task task) {
_task = task;
_task.setFragment(this);
}
public void setListener(AsyncTaskFragmentListener listener) { _listener = listener; }
private void setRunning(boolean running) { _running = running; }
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Fragment lifecycle
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onResume() {
super.onResume();
handler.resume(getActivity());
}
@Override
public void onPause() {
super.onPause();
handler.pause();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
_listener = (AsyncTaskFragmentListener) activity;
}
@Override
public void onDetach() {
super.onDetach();
_listener = null;
}
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Utils
public void execute(Object... params) {
_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
public void cancel(boolean mayInterruptIfRunning) {
_task.cancel(mayInterruptIfRunning);
}
public static AsyncTaskFragment getRetainedOrNewFragment(AppCompatActivity activity, String fragmentTag) {
FragmentManager fm = activity.getSupportFragmentManager();
AsyncTaskFragment fragment = (AsyncTaskFragment) fm.findFragmentByTag(fragmentTag);
if (fragment == null) {
fragment = new AsyncTaskFragment();
fragment.setListener( (AsyncTaskFragmentListener) activity);
fm.beginTransaction().add(fragment, fragmentTag).commit();
}
return fragment;
}
// endregion
/* ------------------------------------------------------------------------------------------ */
}
PauseHandlerが必要です:
import Android.app.Activity;
import Android.os.Handler;
import Android.os.Message;
import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.List;
/**
* Message Handler class that supports buffering up of messages when the activity is paused i.e. in the background.
*
* https://stackoverflow.com/questions/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused
*/
public abstract class PauseHandler extends Handler {
/**
* Message Queue Buffer
*/
private final List<Message> messageQueueBuffer = Collections.synchronizedList(new ArrayList<Message>());
/**
* Flag indicating the pause state
*/
private Activity activity;
/**
* Resume the handler.
*/
public final synchronized void resume(Activity activity) {
this.activity = activity;
while (messageQueueBuffer.size() > 0) {
final Message msg = messageQueueBuffer.get(0);
messageQueueBuffer.remove(0);
sendMessage(msg);
}
}
/**
* Pause the handler.
*/
public final synchronized void pause() {
activity = null;
}
/**
* Store the message if we have been paused, otherwise handle it now.
*
* @param msg Message to handle.
*/
@Override
public final synchronized void handleMessage(Message msg) {
if (activity == null) {
final Message msgCopy = new Message();
msgCopy.copyFrom(msg);
messageQueueBuffer.add(msgCopy);
} else {
processMessage(activity, msg);
}
}
/**
* Notification message to be processed. This will either be directly from
* handleMessage or played back from a saved message when the activity was
* paused.
*
* @param activity Activity owning this Handler that isn't currently paused.
* @param message Message to be handled
*/
protected abstract void processMessage(Activity activity, Message message);
}
サンプル使用法:
public class TestActivity extends AppCompatActivity implements AsyncTaskFragmentListener {
private final static String ASYNC_TASK_FRAGMENT_A = "ASYNC_TASK_FRAGMENT_A";
private final static String ASYNC_TASK_FRAGMENT_B = "ASYNC_TASK_FRAGMENT_B";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button testButton = (Button) findViewById(R.id.test_button);
final AsyncTaskFragment fragment = AsyncTaskFragment.getRetainedOrNewFragment(TestActivity.this, ASYNC_TASK_FRAGMENT_A);
testButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!fragment.isRunning()) {
fragment.setTask(new Task() {
@Override
protected Object doInBackground(Object... objects) {
// Do your async stuff
return null;
}
});
fragment.execute();
}
}
});
}
@Override
public void onPreExecute(String fragmentTag) {}
@Override
public void onProgressUpdate(String fragmentTag, Float percent) {}
@Override
public void onCancelled(String fragmentTag) {}
@Override
public void onPostExecute(String fragmentTag, Object result) {
switch (fragmentTag) {
case ASYNC_TASK_FRAGMENT_A: {
// Handle ASYNC_TASK_FRAGMENT_A
break;
}
case ASYNC_TASK_FRAGMENT_B: {
// Handle ASYNC_TASK_FRAGMENT_B
break;
}
}
}
}
フラグメントをかわしたい場合は、 onRetainCustomNonConfigurationInstance() といくつかの配線を使用して、方向の変更で実行中のAsyncTaskを保持できます。
(このメソッドは非推奨の onRetainNonConfigurationInstance() の代替であることに注意してください。
しかし、この解決策は頻繁に言及されていないようです。説明のために、簡単な実行例を作成しました。
乾杯!
public class MainActivity extends AppCompatActivity {
private TextView result;
private Button run;
private AsyncTaskHolder asyncTaskHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
result = (TextView) findViewById(R.id.textView_result);
run = (Button) findViewById(R.id.button_run);
asyncTaskHolder = getAsyncTaskHolder();
run.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
asyncTaskHolder.execute();
}
});
}
private AsyncTaskHolder getAsyncTaskHolder() {
if (this.asyncTaskHolder != null) {
return asyncTaskHolder;
}
//Not deprecated. Get the same instance back.
Object instance = getLastCustomNonConfigurationInstance();
if (instance == null) {
instance = new AsyncTaskHolder();
}
if (!(instance instanceof ActivityDependant)) {
Log.e("", instance.getClass().getName() + " must implement ActivityDependant");
}
return (AsyncTaskHolder) instance;
}
@Override
//Not deprecated. Save the object containing the running task.
public Object onRetainCustomNonConfigurationInstance() {
return asyncTaskHolder;
}
@Override
protected void onStart() {
super.onStart();
if (asyncTaskHolder != null) {
asyncTaskHolder.attach(this);
}
}
@Override
protected void onStop() {
super.onStop();
if (asyncTaskHolder != null) {
asyncTaskHolder.detach();
}
}
void updateUI(String value) {
this.result.setText(value);
}
interface ActivityDependant {
void attach(Activity activity);
void detach();
}
class AsyncTaskHolder implements ActivityDependant {
private Activity parentActivity;
private boolean isRunning;
private boolean isUpdateOnAttach;
@Override
public synchronized void attach(Activity activity) {
this.parentActivity = activity;
if (isUpdateOnAttach) {
((MainActivity) parentActivity).updateUI("done");
isUpdateOnAttach = false;
}
}
@Override
public synchronized void detach() {
this.parentActivity = null;
}
public synchronized void execute() {
if (isRunning) {
Toast.makeText(parentActivity, "Already running", Toast.LENGTH_SHORT).show();
return;
}
isRunning = true;
new AsyncTask<Void, Integer, Void>() {
@Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100; i += 10) {
try {
Thread.sleep(500);
publishProgress(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
if (parentActivity != null) {
((MainActivity) parentActivity).updateUI(String.valueOf(values[0]));
}
}
@Override
protected synchronized void onPostExecute(Void aVoid) {
if (parentActivity != null) {
((MainActivity) parentActivity).updateUI("done");
} else {
isUpdateOnAttach = true;
}
isRunning = false;
}
}.execute();
}
}
library を実装しました。これにより、タスクの実行中のアクティビティの一時停止と再作成に関する問題を解決できます。
AsmykPleaseWaitTask
とAsmykBasicPleaseWaitActivity
を実装する必要があります。画面を回転させてアプリケーションを切り替える場合でも、アクティビティとバックグラウンドタスクは正常に機能します。