0

今日、私の Android プロジェクトで aSyncTasks に関連する問題が発生しました。いくつかの調査で答えが見つかりました。私が話した人は誰も気づいていなかったので、誰かがそれを見つけた場合に備えて、SO コミュニティと共有したいと思いました。何の役にも立たない。

私の問題の簡単な概要:

インストール ボタンがクリックされたときに複数のファイルのパッケージをダウンロードする UI アクティビティ クラスがあります。ファイル「バンドル」は最大 4 つの個別のダウンロードに分割され、「グループ」情報はファイルの他の情報とともにカスタム クラスに保存されます。ダウンロード ボタンをクリックすると、(最大) 4 つのジョブが、ユーティリティ DownloadUtilities を介して android DownloadManager でキューに入れられます。

「グループ」ダウンロードが DownloadUtilities によってキューに入れられると、DownloadManager ダウンロード参照が「グループ」カスタム クラス内に保存され、後で使用できるようになります。

Download Utilities クラスには BroadcastReceiver があり、ファイルの確認を行い、ダウンロードが終了したことを示すためにグループの各要素のダウンロード参照を 0 に更新します。

グループ内のすべてのファイルのダウンロードが完了すると、それらを処理できます。これは、DownloadReceiver によってトリガーされる aSyncTask によって行われます。

これにより、要求されたダウンロードが完了したことを UI アクティビティに通知するブロードキャストがトリガーされ、それに応じて UI を更新できるようになります。

この時点まではすべて問題なく機能していましたが、「グループ」の進行状況を表示する進行状況バーを追加しようとしたときに問題が見つかりました。

ProgressBar を更新するために、ダウンロード マネージャーにクエリを実行してダウンロード ファイルの合計サイズを計算し、1 秒に 1 回ポーリングして ProgressBar を更新し、現在のダウンロードの進行状況を表示できる新しい aSyncTask を作成しました。

doInBackground() スレッドは、次の疑似コードで構成されています。

Check if file1 is being downloaded, if it is, query the download manager for the total file size and add it to total
Same for files2-4

While the download references are not all 0:
If file 1 is still downloading, get file1 download total
Same for file2-4
Update progress to sum of downloaded bytes/total

私が見つけた問題は、システムがデッドロックすることでした。Logcat は、DownloadManager によるファイルのダウンロードが完了したこと、および ProcessDownload aSyncTask onPreExecute() が実行されているが、doInBackGround が実行されていないことをダウンロード受信者に通知する必要があります。

その間、ダウンロード モニターは常に doInBackground() でループしていました。これは、while ループの条件が決してアサート解除されないためです。これは、アサート解除が ProcessDownload doInBackground() スレッドで行われるためです。

aSyncTask doInBackground() メソッドが相互に排他的であり、デッドロックを引き起こしていることが明らかになりましたが、その理由はわかりませんでした。過去に、これが問題を引き起こすような方法で意図的にコードを構成したことはありません...

私の理解では、aSyncTasks はコードをマルチスレッド化するための開発者に優しい方法を提供していましたが、そうではないようです...

4

1 に答える 1

2

そのため、問題の原因を突き止めようと数時間を費やし、aSyncTasks の理解に基づいて、コードが機能しない理由はないという結論に達しました...だから私はインターネットに取り掛かりましたドキュメントをトロールして、最終的に答えを見つけました。

「HONEYCOMB 以降、タスクは単一のスレッドで実行され、並列実行によって引き起こされる一般的なアプリケーション エラーを回避します。

本当に並列実行が必要な場合は、THREAD_POOL_EXECUTOR を使用して executeOnExecutor(java.util.concurrent.Executor, Object[]) を呼び出すことができます。"

私の考えは以前はそうだったようですが、ハニカムの時点ではそうではありませんでした。aSyncTasks DO は UI スレッドからコードをオフロードしますが、複数の aSyncTask doInBackground() メソッドが順次実行される単一のスレッドにコードをオフロードします。

それで、私は戻ってコードを変更し、古き良き Java スレッドを使用するようにしました。

依存コードの使用を考えている人は、aSyncTasks を注意深く検討して、その動作を確実に理解することをお勧めします。これらは、私が過去に何度も使用した優れたツールですが、より高度な機能を備えています。基本に戻ったほうがいいかもしれません。

これが誰かの役に立てば幸いです。aSyncTask の動作を単純に誤解しているため、私はこれに多くの時間を費やしました -.-

答えてくれたソース

編集:

http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.htmlを教えてくれた Krylez に感謝します。これには、Honeycomb の前と後の両方のデバイスとの互換性を維持する非常に雄弁な方法も含まれています。必要なのは、aSyncTask 呼び出しを変更することだけです

if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
  myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else {
  myTask.execute();
}

Honeycomb+ の動作を以前の状態に戻します

于 2013-07-24T21:16:57.337 に答える