[解決済み】AsyncTaskは本当に概念的に欠陥があるのか、それとも私が何かを見逃しているだけなのか?
質問
私はこの問題を数ヶ月間調査し、さまざまな解決策を考え出しましたが、どれも大規模なハッキングなので満足していません。設計に欠陥のあるクラスがフレームワークに入り、誰もそれについて話していないことが未だに信じられないので、私が何かを見逃しているだけなのだと思います。
問題は
AsyncTask
. ドキュメントによると、それは
をバックグラウンドで実行することができます。 を実行し、その結果を UIスレッドを操作することなく スレッドおよび/またはハンドラです。
この例では、引き続き、いくつかの例示的な
showDialog()
メソッドが呼び出されるのは
onPostExecute()
. しかし、これは
まったくもって作為的である
というのも、ダイアログを表示するには、常に有効な
Context
と、AsyncTask
は、コンテキストオブジェクトへの強い参照を決して保持してはなりません。
.
理由は明白で、タスクを起動させたアクティビティが破壊されたらどうなるか?例えば、画面を反転させた場合など、よくあることです。もしタスクがそれを作成したコンテキストへの参照を保持していたら、役に立たないコンテキストオブジェクトを保持することになるだけでなく(ウィンドウは破壊されたので 任意の UIインタラクションは例外で失敗します!)さらに、メモリリークを引き起こす危険性さえあります。
ここで私の論理に欠陥がなければ、これは次のように訳されます。
onPostExecute()
なぜなら、もしコンテキストにアクセスできないのなら、このメソッドがUIスレッドで実行されることにどんな意味があるでしょうか?ここで意味のあることは何もできないのです。
回避策としては、AsyncTaskにコンテキストインスタンスを渡さず、AsyncTaskのインスタンスとして
Handler
インスタンスです。Handlerはコンテキストとタスクを緩く結合するので、リークのリスクなしにそれらの間でメッセージを交換することができます(そうでしょうか)。しかし、それではAsyncTaskの前提、つまりハンドラを気にする必要がない、というのは間違っていることになります。また、同じスレッドでメッセージを送受信している(UIスレッドで作成し、同じくUIスレッドで実行されるonPostExecute()でそれを通して送信する)ので、Handlerを乱用しているようにも思えます。
さらに言えば、この回避策をとったとしても、コンテキストが破壊されたときに レコードなし が発生します。つまり、画面の向きを変えた後など、コンテキストを再作成するときに、タスクを再スタートさせなければならないのです。これは遅くて無駄なことです。
これに対する私の解決策は、(
Droid-Fuのライブラリに実装されている
のマッピングを維持することです。
WeakReference
は、コンポーネント名から、一意のアプリケーションオブジェクト上の現在のインスタンスまでです。AsyncTaskが起動するたびに、そのマップに呼び出し元のコンテキストを記録し、コールバックのたびに、そのマッピングから現在のコンテキストインスタンスを取得します。これにより、決して古いコンテキストインスタンスを参照しないことが保証されます。
と
の場合、コールバックで常に有効なコンテキストにアクセスできるので、そこで意味のあるUI作業を行うことができます。また、参照は弱いものであり、与えられたコンポーネントのインスタンスが存在しなくなるとクリアされるため、リークすることもありません。
しかし、これは複雑な回避策であり、Droid-Fuライブラリのクラスのいくつかをサブクラス化する必要があるため、かなり邪魔な方法となっています。
あとは単純に知りたいですね。 私が何かを見落としているだけなのか、それともAsyncTaskは本当に全く欠陥のないものなのでしょうか?それを使ってどのように仕事をした経験がありますか?どのようにこれらの問題を解決しましたか?
ご意見ありがとうございました。
解決方法は?
こんなのはどうでしょう。
class MyActivity extends Activity {
Worker mWorker;
static class Worker extends AsyncTask<URL, Integer, Long> {
MyActivity mActivity;
Worker(MyActivity activity) {
mActivity = activity;
}
@Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... progress) {
if (mActivity != null) {
mActivity.setProgressPercent(progress[0]);
}
}
@Override
protected void onPostExecute(Long result) {
if (mActivity != null) {
mActivity.showDialog("Downloaded " + result + " bytes");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWorker = (Worker)getLastNonConfigurationInstance();
if (mWorker != null) {
mWorker.mActivity = this;
}
...
}
@Override
public Object onRetainNonConfigurationInstance() {
return mWorker;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mWorker != null) {
mWorker.mActivity = null;
}
}
void startWork() {
mWorker = new Worker(this);
mWorker.execute(...);
}
}
関連
-
[解決済み】コレクションが変更され、列挙操作が実行されないことがある。
-
[解決済み] カスタムアダプタからnotifyDataSetChangeが機能しない
-
[解決済み】Android Studio AVD - Emulator: 終了コード 1 でプロセスが終了
-
[解決済み】googleコンソールエラー`OR-IEH-01`について
-
[解決済み] Bitmapオブジェクトに画像を読み込む際にOutOfMemoryが発生する問題
-
[解決済み] アプリケーションを終了することは嫌われますか?
-
[解決済み] AndroidでToastを表示する方法を教えてください。
-
[解決済み] Java 8 の並列ストリームにおけるカスタムスレッドプール
-
[解決済み】警告。この AsyncTask クラスは静的であるべきで、さもなければリークが発生する可能性があります。
-
[解決済み】アクティビティコンテキストまたはアプリケーションコンテキストを呼び出すタイミングは?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Android - SDKバージョン23のアップデート後、ACTION-VIEWインテントフィルタを持つアクティビティを少なくとも1つ追加する。
-
[解決済み】Android Studioの初回起動。Android SDKアドオンリストにアクセスできない
-
[解決済み】Android Studioです。「プロジェクトが C ドライブに作成されている場合、「タスク ':app:mergeDebugResources' の実行に失敗しました。
-
[解決済み】ビットマップを保存する場所について
-
[解決済み】Android Studioでused import文がunused import文に指定されるのはなぜ?
-
[解決済み】Android Studio 3.2 - com.android.tools.build:aapt2:3.2.0-4818971 を見つけられませんでした。
-
[解決済み] BIOSのセキュリティ設定でVT-xを有効にする(お使いのコンピュータのドキュメントを参照)。
-
[解決済み] 複数のデバイスを接続しているときにADB Shellを使用するには?error: more than one device and emulator "で失敗します。
-
[解決済み] APKのインストール中にDELETE_FAILED_INTERNAL_ERRORエラーが発生する。
-
[解決済み】バックグラウンドタスク、プログレスダイアログ、オリエンテーションの変更 - 100%動作する解決策はありますか?