Androidアプリケーションの開発におけるマルチスレッドの実装については、すでに多くのことが書かれています。 同じ記事で、ユーザーに迷惑をかける理由を与えずに、今日のダウンロード/読み取り/保存/カウントの一般的な方法を比較したいと思います。 特定の決定がいつ適切になるのか、何もしないほうがよいのかを理解するようにしてください。 Androidアプリケーションに関しては、Threadクラスやjava.util.concurrentパッケージなどの通常のものでは不十分な理由を示すようにします。
この記事には、各アプローチの実装のすべての詳細を強調するタスクはありませんが、基本を告げずにそれらを比較することは不可能です。 したがって...
スレッド
JavaからAndroid APIに移行したThreadクラスは、おそらく新しいスレッドを開始する最も簡単な方法です。 これを行う方法の例をいくつか示します。Threadから継承者を作成するか、Runnableインターフェイスを実装するオブジェクトをThreadクラスのインスタンスに渡すことができます。
例1.拡張スレッド。class WorkingThread extends Thread{ @Override public void run() {
例2.実行可能。 class WorkingClass implements Runnable{ @Override public void run() {
原則として、必要な操作を実行した後、結果をユーザーに提供する必要があります。 ただし、別のスレッドからUI要素を取得してアクセスすることはできません。 Androidマルチスレッドモデルのため、インターフェイス要素の状態の変更は、これらの要素が作成されたスレッドからのみ許可されます。そうでない場合、CalledFromWrongThreadExceptionがスローされます。 この場合、Android APIは複数のソリューションを一度に提供します。
例1.#postを表示(実行可能なアクション)。 public class MainActivity extends Activity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.hello); WorkingClass workingClass = new WorkingClass(); Thread thread = new Thread(workingClass); thread.start(); } class WorkingClass implements Runnable{ @Override public void run() {
例2.アクティビティ#runOnUiThread(実行可能なアクション)。 public class MainActivity extends Activity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.hello); WorkingClass workingClass = new WorkingClass(); Thread thread = new Thread(workingClass); thread.start(); } class WorkingClass implements Runnable{ @Override public void run() {
例3.ハンドラー。 public class MainActivity extends Activity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.hello); WorkingClass workingClass = new WorkingClass(true); Thread thread = new Thread(workingClass); thread.start(); } class WorkingClass implements Runnable{ public static final int SUCCESS = 1; public static final int FAIL = 2; private boolean dummyResult; public WorkingClass(boolean dummyResult){ this.dummyResult = dummyResult; } @Override public void run() {
一般的には簡単ですが、インターフェイス要素と積極的にやり取りすることになると、コードはRunnableインターフェイスのヒープまたはかなり大きなHandlerクラスになります。 メインストリームとバックグラウンドストリームの同期作業を簡素化するために、AsyncTaskクラスはAndroid 1.5で既に提案されています
AsyncTask
AsyncTaskを使用するには、パラメーター化された型でその下位クラスを作成し、必要なメソッドをオーバーライドする必要があります。 開始後、AsyncTaskは次の順序でメソッドを呼び出します:onPreExecute()、doInBackground(Params ...)、onPostExecute(Result)。最初と最後はUIスレッドで呼び出され、2番目は別のスレッドで推測できます。 さらに、AsyncTaskクラスにより、UIスレッドはpublishProgress(Progress ...)メソッドを使用して実行の進行状況をUIスレッドに通知できます。このメソッドは、UIスレッドでonProgressUpdate(Progress ...)を呼び出します。
AsyncTaskの例 public class MainActivity extends Activity { private static final String IMAGE_URL = "http://eastbancgroup.com/images/ebtLogo.gif"; TextView textView; ImageView imageView; ProgressDialog progressDialog; DownloadTask downloadTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.hello); imageView = (ImageView)findViewById(R.id.imageView); downloadTask = new DownloadTask();
この画像読み込みの例は、準備、バックグラウンド操作、更新の進行、最終アクション、作業の停止など、AsyncTaskクラスによって提供されるすべての可能性を示しています。 また、これらの各段階で、開発者はバックグラウンドスレッドとメインスレッドの同期について心配する必要はありません。
多くの場合、AsyncTaskはThreadクラスを使用してスレッドを作成する方が便利ですが、マルチスレッドを実装する最初の方法の方が有利な場合があります。 私たちの意見では、マルチスレッドを実装する方法を選択する際に考慮すべき重要な違いは次のとおりです。
2番目のポイントは特に重要です。 事実、このタスクの作業モデルのために、それらに基づいて長寿命のバックグラウンドプロセス(タイマーなど)を作成することは不可能です。 このようなAsyncTaskのインスタンスはキューを詰まらせ、後続のタスクの実行を妨げます。 しかし、多数のスレッドとハンドラーを使用すると、逆に、間隔を置いてコードを実行するのは非常に簡単です。
タイマーの例。 public class MainActivity extends Activity { TextView textView; private int counter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); counter = 0; textView = (TextView)findViewById(R.id.hello);
注:実際には、AsyncTask#executeOnExecutor(Executor exec、Params ... params)メソッドhttps://developer.android.com/reference/android/os/AsyncTask.htmlを使用して、複数のAsyncTaskを並行して実行できます。 。前の例では、特定のアクティビティのコンテキストでThreadとAsyncTaskの両方が使用されています。 ほとんどの場合、これは正常ですが、このようなモデルは特定の問題を引き起こす可能性があります。 バックグラウンドではありますが、AsyncTaskまたはThreadを使用すると、不要になったアクティビティのインスタンスをガベージコレクターで削除できないことを理解する必要があります。 また、たとえば、デバイスの画面を回すと、非常に簡単に発生します。 画面の向きが変更されるたびに、新しいアクティビティが作成され、そのたびにAsyncTaskが呼び出されます。 ダウンロードしたイメージのサイズが大きいほど、アプリケーションは
OutOfMemoryErrorエラーでより速く閉じます。 この例よりも悪いのは、おそらく、多くの教育記事で示されているように、匿名クラスの使用です。 新しいタスクまたはスレッドへのリンクを保存せずに、同じアクティビティを閉じたときに実行を停止するなど、プロセスの進行を制御する機能を自分で奪います。
合計:
ThreadクラスとAsyncTaskクラスの比較から、いくつかの結論を導き出すことができます。
スレッドの使用が正当化されるタスク:
- 優先度の設定が必要な操作。 CPUリソースを積極的に消費する操作。
- 操作は、任意の時間間隔で何度も実行されます。
- 複数のバックグラウンドスレッドを並行して実行します。
AsyncTaskの使用が正当化されるタスク:
- 数秒しか費やさないことが予想される操作。 少量のデータのダウンロード、簡単なファイルシステム操作。
- バックグラウンドスレッドからインターフェイス要素をアクティブに管理します。
ThreadおよびAsyncTaskを使用した作業に課せられる主な条件:作業がアクティビティ/フラグメントのコンテキストで開始された場合、アクティビティ/フラグメントが停止した後、できるだけ早く終了する必要があります。
ローダー
データ操作には種類があり、その実行は、アプリケーションのメインスレッドでは許可されますが、インターフェイスの速度を著しく低下させたり、
ANRメッセージを発生させることさえあり
ます 。 このような操作の良い例は、データベース/ファイルからの読み取りです。 最近まで、すでに説明したThreadおよびAsyncTaskを使用してデータベースを操作することは良い習慣でしたが、Loader 3.0やLoaderManagerなどのクラスが追加されました。その目的は、データのActivityまたはFragmentへの非同期読み込みを簡素化することです。 古いプラットフォームの場合、同じクラスが
androidサポートライブラリで利用可能です。
ローダーを使用する原則は次のとおりです。
1. Loaderクラスまたはその標準の子孫を拡張する独自のクラスを作成する必要があります。
2.ジェネリックタイプDデータのロードを実装する
3.アクティビティで、LoaderManagerへのリンクを取得し、ローダーを初期化して、ローダーとLoaderManager.LoaderCallbacksへのコールバックをマネージャーに渡します。
標準のCursorLoaderクラスを使用して、電話の連絡先のリストを表示する方法の例を次に示します。
ローダーの例。 public class MainActivity extends ListActivity implements LoaderCallbacks<Cursor> {
アプリケーションマニフェストで連絡先を読み取るための適切なアクセス許可を指定することを忘れないでください。
合計:
ローダーテンプレートの使用は、表示を担当するアプリケーションのコンポーネント(アクティビティ、フラグメント)と密接に関連しているため、データの読み込み操作の実行時間は、これらのコンポーネントの寿命に匹敵するはずです。
サービスとIntentService
サービスは、Androidアプリケーションのコンポーネントの1つです。 サービス自体は、独立したプロセスまたは独立したスレッドではありません。 ただし、サービスには独自のライフサイクルがあり、サービスで長期的な操作を実行するのに適しています。 サービスのコンテキストで起動された追加のスレッドは、アプリケーションを介したユーザーのナビゲーションを妨げることなく実行できます。 通常、サービスとアプリケーションの他のコンポーネントとの間の通信には、ServiceConnection / IBinderインターフェイスまたはブロードキャストメッセージの2つの方法が使用されます。 最初の方法の本質は、サービスの実行中のインスタンスへのリンクを取得することです。 これは、このメソッドがマルチタスクの問題を何らかの形で解決するということではなく、サービスの管理に適しています。 また、ブロードキャストメッセージを使用した通信はスレッドセーフであるため、この例では考慮されます。
サービス例。 public class BackgroundService extends Service { public static final String CHANNEL = BackgroundService.class.getSimpleName()+".broadcast";
public class MainActivity extends Activity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.hello);
サービスとアクティビティは、プロジェクトマニフェストで宣言する必要があることを忘れないでください。
さらに、Android APIはIntentServiceクラスを提供します。これは標準サービスを拡張しますが、別のストリームで転送されたデータの処理を実行します。 新しいリクエストが到着すると、IntentServiceは新しいスレッドを作成し、IntentService#onHandleIntent(Intent intent)
https://developer.android.com/reference/android/app/IntentService.html#onHandleIntent (android.content.Intent)を呼び出し、再定義のみ可能です。 新しいリクエストが到着したときに前のリクエストの処理がまだ終了していない場合、リクエストはキューに入れられます。
IntentServiceの例。 public class DownloadService extends IntentService { public DownloadService() { super("DownloadService"); } public static final String CHANNEL = DownloadService.class.getSimpleName()+".broadcast"; private void sendResult() { Intent intent = new Intent(CHANNEL); sendBroadcast(intent); } @Override public IBinder onBind(Intent intent) { return null; }
public class MainActivity extends Activity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.hello);
合計:
通常、サービスのライフサイクルはアクティビティよりも長くなります。 一度開始すると、サービスは動作しなくなるまで生き続け、その後は自動的に停止します。 開発者は基本的に、着信メッセージ(意図)の目的の処理を整理するだけで済みます。比較、キューの作成など、各操作の完了に関するメッセージの送信です。
例からわかるように、ブロードキャストメッセージの送信元は重要ではありません。主なことは、メインストリームで受信されることです。
ダウンロードマネージャー
Android API 9以降、DowloadManagerシステムサービスのおかげで、ネットワーク経由でファイルをダウンロードおよび保存するタスクがさらに簡単になりました。 必要なことは、このUriサービスを転送することだけです。必要に応じて、ダウンロード中およびダウンロード後に通知領域に表示されるテキストを指定し、DownloadManagerが送信できるイベントをサブスクライブします。このサービスは、エラーに応答して、接続の確立を処理し、ダウンロードを再開し、通知バーで通知を作成し、そしてもちろん、バックグラウンドストリーム自体でファイルをダウンロードします。
DownloadManagerの例。
DownloadManagerの例 public class MainActivity extends Activity { private static final String IMAGE_URL = "http://eastbancgroup.com/images/ebtLogo.gif"; ImageView imageView; DownloadManager downloadManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView)findViewById(R.id.imageView);
DownloadManagerには1つの機能があります。 実際、予想に反してファイルのアップロードが成功したという通知をクリックすると、DownloadManager.ACTION_NOTIFICATION_CLICKEDなどのブロードキャストメッセージは送信されません。 しかし、代わりに、サービスはこのクリックを処理できるアクティビティを見つけようとします。 したがって、このイベントに応答する場合は、プロジェクトマニフェストに目的のアクティビティに、次の内容がほぼ含まれる新しいインテントフィルターを追加します。
<intent-filter> <action android:name="android.intent.action.VIEW" /> <data android:mimeType="application/my-mime" /> (mime type, ) <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
この場合、通知をクリックすると、アクティビティが起動され、ダウンロード識別子を含むインテントが既に転送されます。 たとえば、次のように取得できます。
意図の意図= getIntent();
文字列データ= intent.getDataString();
合計:
DownloadManagerサービスは、画像、メディアファイル、アーカイブなど、アプリケーションとは別にユーザーが関心を持つ可能性のある大きなファイルをダウンロードするのに便利です。他のアプリケーションもアップロードしたファイルにアクセスできることに注意してください。これは、Androidアプリケーションのバックグラウンド作業を実装するためのすべてのパターンを網羅したということではありませんが、高い信頼性で、議論された方法は非常に広く普及していると言えます。この記事が、背景作業を最も正確かつ便利に設計するのに役立つことを願っています。