Android AsyncTask コンテキストの動作
-
22-09-2019 - |
質問
Android で AsyncTasks を使用していて、問題に対処しています。
簡単な例として、1 つの AsyncTask を持つアクティビティを考えてみましょう。バックグラウンドのタスクは特別なことを行うわけではなく、8 秒間スリープするだけです。
onPostExecute() メソッドの AsyncTask の最後で、結果を確認するためだけに、ボタンの可視性ステータスを View.VISIBLE に設定しています。
これは、AsyncTask の動作中 (8 秒のスリープ ウィンドウ内) にユーザーが携帯電話の向きを変更することを決定するまではうまく機能します。
私は Android アクティビティのライフサイクルを理解しており、アクティビティが破棄されて再作成されることを知っています。
ここで問題が生じます。AsyncTask はボタンを参照しており、最初に AsyncTask を開始したコンテキストへの参照を保持しているようです。
この古いコンテキスト (ユーザーが向きを変更したため) が null になり、AsyncTask が表示しようとしているボタンへの参照に対して NPE をスローすることを期待します。
代わりに、NPE はスローされず、AsyncTask はボタン参照が null ではないと判断し、それを表示に設定します。結果?画面上では何も起こっていません!
アップデート: 私はこれに対処するために、 WeakReference
設定変更が発生したときのアクティビティと切り替えに影響します。これは面倒です。
コードは次のとおりです。
public class Main extends Activity {
private Button mButton = null;
private Button mTestButton = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button) findViewById(R.id.btnStart);
mButton.setOnClickListener(new OnClickListener () {
@Override
public void onClick(View v) {
new taskDoSomething().execute(0l);
}
});
mTestButton = (Button) findViewById(R.id.btnTest);
}
private class TaskDoSomething extends AsyncTask<Long, Integer, Integer>
{
@Override
protected Integer doInBackground(Long... params) {
Log.i("LOGGER", "Starting...");
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
@Override
protected void onPostExecute(Integer result) {
Log.i("LOGGER", "...Done");
mTestButton.setVisibility(View.VISIBLE);
}
}
}
それを実行してみて、AsyncTask が動作している間に携帯電話の向きを変更してください。
解決
AsyncTask は、アクティビティが破棄されて再起動された後に再利用できるようには設計されていません。あなたが述べたように、内部の Handler オブジェクトは古くなります。Romain Guy による Shelves の例では、現在実行中の AsyncTask を単純にキャンセルし、方向変更後に新しい AsyncTask を再開します。
スレッドを新しいアクティビティに引き渡すことは可能ですが、多くの配管作業が追加されます。これを行うための一般的に合意された方法はありませんが、私の方法についてはここで読むことができます。 http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html
他のヒント
あなただけのコンテキストを必要とUIのもののためにそれを使用しない場合、あなたは単にあなたのAsyncTask.YouへのApplicationContextを渡すことができますが、多くの場合、例えば、システムリソースのためのコンテキストを必要とします。
AsyncTaskからUIを更新し、それが厄介を得ることができるように自分自身を変更した構成を扱う回避しようとしないでください。あなたが放送受信機を登録し、ブロードキャストを送ることができるUIを更新するために。
また、それが簡単に多くのテストを行い、上記のような活動とは別のパブリッククラスとしてAsyncTaskを持つ必要があります。残念ながら、Androidのプログラミングは、多くの場合、悪い習慣を強化し、公式の例が支援していません。
このは、リードが、私は常に破壊される/姿勢の変化に再作成から私の活動を防ぐために、事のタイプです。
そうするためにマニフェストファイルでのご<Activity>
タグにこれを追加します:
android:configChanges="orientation|keyboardHidden"
そして、あなたのActivityクラスでオーバーライドonConfigurationChangedます:
@Override
public void onConfigurationChanged(final Configuration newConfig)
{
// Ignore orientation change to keep activity from restarting
super.onConfigurationChanged(newConfig);
}
これを避けるために、あなたがここに回答引けるを使用することができます: https://stackoverflow.com/a/2124731/327011
しかし、それはプライベートではありませんなぜあなたはここにアクティビティあなたがAsyncTask公共クラスを作ることができます(肖像画や風景のための異なるレイアウト)(リードを破壊する必要がある場合は、<のhref = "https://stackoverflow.com/質問/ 4823891 /アンドロイド-asynctask-勧告 - プライベート・クラスやパブリック・クラス」>アンドロイド:AsyncTaskの推奨事項:?その後、プライベートクラスまたはパブリッククラスの)とは、現在の基準値を設定する方法のsetActivityを作成しますアクティビティが破棄されるたびに作成/。
AndroidのAsyncTask:あなたはここでは例を見ることができます>