Androidスレッド:知っておくべきことすべて
公開: 2022-03-11すべてのAndroid開発者は、ある時点または別の時点で、アプリケーション内のスレッドを処理する必要があります。
アプリケーションがAndroidで起動されると、「メイン」スレッドと呼ばれる最初の実行スレッドが作成されます。 メインスレッドは、適切なユーザーインターフェイスウィジェットへのイベントのディスパッチと、AndroidUIツールキットのコンポーネントとの通信を担当します。
アプリケーションの応答性を維持するには、メインスレッドを使用して、ブロックされたままになる可能性のある操作を実行しないようにすることが重要です。
ネットワーク操作とデータベース呼び出し、および特定のコンポーネントのロードは、メインスレッドで回避する必要がある操作の一般的な例です。 それらがメインスレッドで呼び出されると、同期的に呼び出されます。つまり、操作が完了するまでUIは完全に応答しなくなります。 このため、通常は別々のスレッドで実行されます。これにより、実行中にUIがブロックされるのを回避できます(つまり、UIから非同期で実行されます)。
Androidには、スレッドを作成および管理するための多くの方法が用意されており、スレッド管理をより快適にする多くのサードパーティライブラリが存在します。 ただし、手元にある非常に多くの異なるアプローチでは、適切なアプローチを選択することは非常に混乱する可能性があります。
この記事では、スレッド化が不可欠になるAndroid開発のいくつかの一般的なシナリオと、それらのシナリオなどに適用できるいくつかの簡単なソリューションについて学習します。
Androidでのスレッド
Androidでは、すべてのスレッドコンポーネントを2つの基本的なカテゴリに分類できます。
- アクティビティ/フラグメントに接続されているスレッド:これらのスレッドは、アクティビティ/フラグメントのライフサイクルに関連付けられており、アクティビティ/フラグメントが破棄されるとすぐに終了します。
- アクティビティ/フラグメントに接続されていないスレッド:これらのスレッドは、スポーンされたアクティビティ/フラグメント(存在する場合)の存続期間を超えて実行し続けることができます。
アクティビティ/フラグメントにアタッチするスレッドコンポーネント
AsyncTask
AsyncTask
は、スレッド化のための最も基本的なAndroidコンポーネントです。 使い方は簡単で、基本的なシナリオに適しています。
使用例:
public class ExampleActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new MyTask().execute(url); } private class MyTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { String url = params[0]; return doSomeWork(url); } @Override protected void onPostExecute(String result) { super.onPostExecute(result); // do something with result } } }
ただし、アクティビティ/フラグメントの存続期間を超えて実行するために遅延タスクが必要な場合、 AsyncTask
は不十分です。 画面の回転のような単純なものでも、アクティビティが破壊される可能性があることに注意してください。
ローダー
ローダーは、上記の問題の解決策です。 ローダーは、アクティビティが破棄されると自動的に停止し、アクティビティが再作成された後に自動的に再起動することもできます。
ローダーには主に、 AsyncTaskLoader
とCursorLoader
の2種類があります。 CursorLoader
の詳細については、この記事の後半で説明します。
AsyncTaskLoader
はAsyncTask
に似ていますが、少し複雑です。
使用例:
public class ExampleActivity extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getLoaderManager().initLoader(1, null, new MyLoaderCallbacks()); } private class MyLoaderCallbacks implements LoaderManager.LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { return new MyLoader(ExampleActivity.this); } @Override public void onLoadFinished(Loader loader, Object data) { } @Override public void onLoaderReset(Loader loader) { } } private class MyLoader extends AsyncTaskLoader { public MyLoader(Context context) { super(context); } @Override public Object loadInBackground() { return someWorkToDo(); } } }
アクティビティ/フラグメントにアタッチしないスレッドコンポーネント
サービス
Service
は、UIを使用せずに長い(または場合によっては長い)操作を実行するのに役立つコンポーネントです。
Service
は、ホスティングプロセスのメインスレッドで実行されます。 特に指定しない限り、サービスは独自のスレッドを作成せず、別のプロセスで実行されません。
使用例:
public class ExampleService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { doSomeLongProccesingWork(); stopSelf(); return START_NOT_STICKY; } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
Service
の場合、 stopSelf()
またはstopService()
メソッドのいずれかを呼び出して、作業が完了したときにサービスを停止するのはユーザーの責任です。
IntentService
Service
と同様に、 IntentService
は別のスレッドで実行され、作業が完了すると自動的に停止します。
IntentService
は通常、UIにアタッチする必要のない短いタスクに使用されます。
使用例:
public class ExampleService extends IntentService { public ExampleService() { super("ExampleService"); } @Override protected void onHandleIntent(Intent intent) { doSomeShortWork(); } }
Androidの7つのスレッドパターン
ユースケース1:サーバーからの応答を必要とせずにネットワーク経由でリクエストを行う
サーバーの応答を気にすることなく、APIリクエストをサーバーに送信したい場合があります。 たとえば、プッシュ登録トークンをアプリケーションのバックエンドに送信している場合があります。
これにはネットワーク経由でのリクエストが含まれるため、メインスレッド以外のスレッドからリクエストを行う必要があります。
オプション1:AsyncTaskまたはローダー
呼び出しを行うためにAsyncTask
またはローダーを使用でき、それは機能します。
ただし、 AsyncTask
とローダーはどちらもアクティビティのライフサイクルに依存しています。 つまり、呼び出しが実行されるのを待って、ユーザーがアクティビティを離れないようにするか、アクティビティが破棄される前に実行されることを期待する必要があります。
オプション2:サービス
Service
はどのアクティビティにも関連付けられていないため、このユースケースに適している可能性があります。 したがって、アクティビティが破棄された後でも、ネットワーク呼び出しを続行できます。 さらに、サーバーからの応答が不要なため、ここでもサービスが制限されることはありません。
ただし、サービスはUIスレッドで実行を開始するため、スレッドを自分で管理する必要があります。 また、ネットワーク通話が完了したら、サービスが停止していることを確認する必要があります。
これには、そのような単純なアクションに必要な作業よりも多くの労力が必要になります。
オプション3:IntentService
私の意見では、これが最良の選択肢でしょう。
IntentService
はアクティビティにアタッチせず、UI以外のスレッドで実行されるため、ここでのニーズに完全に対応します。 さらに、 IntentService
は自動的に停止するため、手動で管理する必要もありません。
ユースケース2:ネットワーク呼び出しを行い、サーバーから応答を取得する
このユースケースはおそらくもう少し一般的です。 たとえば、バックエンドでAPIを呼び出し、その応答を使用して画面のフィールドに入力したい場合があります。
オプション1:サービスまたはIntentService
Service
またはIntentService
は以前のユースケースではうまくいきましたが、ここでそれらを使用することはお勧めできません。 Service
またはIntentService
からメインUIスレッドにデータを取得しようとすると、非常に複雑になります。
オプション2:AsyncTaskまたはローダー
一見すると、 AsyncTask
またはローダーがここでの明らかな解決策のように見えます。 それらは使いやすく、シンプルでわかりやすいものです。
ただし、 AsyncTask
またはローダーを使用する場合は、ボイラープレートコードを記述する必要があることに気付くでしょう。 さらに、これらのコンポーネントではエラー処理が大きな雑用になります。 単純なネットワーク呼び出しでも、潜在的な例外を認識し、それらをキャッチして、それに応じて行動する必要があります。 これにより、データを含むカスタムクラスで応答をラップし、エラー情報を含めることができます。フラグは、アクションが成功したかどうかを示します。
これは、すべての呼び出しに対して行う必要のある非常に多くの作業です。 幸いなことに、RxJavaというはるかに優れたシンプルなソリューションが利用できるようになりました。
オプション3:RxJava
Netflixによって開発されたライブラリであるRxJavaについて聞いたことがあるかもしれません。 Javaではほとんど魔法です。
RxAndroidを使用すると、AndroidでRxJavaを使用でき、非同期タスクの処理が簡単になります。 AndroidでのRxJavaについて詳しくは、こちらをご覧ください。
RxJavaには、 Observer
とSubscriber
の2つのコンポーネントがあります。
オブザーバーは、何らかのアクションを含むコンポーネントです。 そのアクションを実行し、成功した場合は結果を返し、失敗した場合はエラーを返します。
一方、サブスクライバーは、サブスクライブすることにより、オブザーバブルから結果(またはエラー)を受け取ることができるコンポーネントです。
RxJavaを使用して、最初にオブザーバブルを作成します。
Observable.create((ObservableOnSubscribe<Data>) e -> { Data data = mRestApi.getData(); e.onNext(data); })
オブザーバブルが作成されたら、サブスクライブできます。
RxAndroidライブラリを使用すると、オブザーバブルでアクションを実行するスレッドと、応答(つまり、結果またはエラー)を取得するスレッドを制御できます。
これらの2つの関数を使用して、オブザーバブルを連鎖させます。
.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()
スケジューラーは、特定のスレッドでアクションを実行するコンポーネントです。 AndroidSchedulers.mainThread()
は、メインスレッドに関連付けられたスケジューラーです。
API呼び出しがmRestApi.getData()
であり、 Data
オブジェクトを返す場合、基本的な呼び出しは次のようになります。
Observable.create((ObservableOnSubscribe<Data>) e -> { try { Data data = mRestApi.getData(); e.onNext(data); } catch (Exception ex) { e.onError(ex); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(match -> Log.i(“rest api, "success"), throwable -> Log.e(“rest api, "error: %s" + throwable.getMessage()));
RxJavaを使用する他の利点に立ち入ることなく、スレッドの複雑さを抽象化することにより、RxJavaがどのように成熟したコードを記述できるかをすでに理解できます。
ユースケース3:ネットワークコールの連鎖
順番に実行する必要があるネットワーク呼び出し(つまり、各操作が前の操作の応答/結果に依存する場合)の場合、スパゲッティコードの生成に特に注意する必要があります。
たとえば、別のAPI呼び出しを介して最初にフェッチする必要があるトークンを使用してAPI呼び出しを行う必要がある場合があります。
オプション1:AsyncTaskまたはローダー
AsyncTask
またはローダーを使用すると、ほぼ間違いなくスパゲッティコードになります。 全体的な機能を正しく理解することは困難であり、プロジェクト全体で膨大な量の冗長なボイラープレートコードが必要になります。
オプション2:flatMapを使用したRxJava
flatMap
では、flatMap演算子は、ソースobservableから出力された値を取得し、別のobservableを返します。 オブザーバブルを作成してから、最初のオブザーバブルから出力された値を使用して別のオブザーバブルを作成できます。これにより、基本的にそれらがチェーンされます。
手順1.トークンをフェッチするオブザーバブルを作成します。
public Observable<String> getTokenObservable() { return Observable.create(subscriber -> { try { String token = mRestApi.getToken(); subscriber.onNext(token); } catch (IOException e) { subscriber.onError(e); } }); }
ステップ2.トークンを使用してデータを取得するオブザーバブルを作成します。
public Observable<String> getDataObservable(String token) { return Observable.create(subscriber -> { try { Data data = mRestApi.getData(token); subscriber.onNext(data); } catch (IOException e) { subscriber.onError(e); } }); }
ステップ3.2つのオブザーバブルをチェーンしてサブスクライブします。
getTokenObservable() .flatMap(new Function<String, Observable<Data>>() { @Override public Observable<Data> apply(String token) throws Exception { return getDataObservable(token); } }) .subscribe(data -> { doSomethingWithData(data) }, error -> handleError(e));
このアプローチの使用はネットワーク呼び出しに限定されないことに注意してください。 シーケンスで実行する必要があるが、別々のスレッドで実行する必要のあるアクションのセットで機能します。
上記のすべてのユースケースは非常に単純です。 スレッド間の切り替えは、それぞれがタスクを終了した後にのみ発生しました。 より高度なシナリオ(たとえば、2つ以上のスレッドが互いにアクティブに通信する必要がある場合)も、このアプローチでサポートできます。
ユースケース4:別のスレッドのUIスレッドと通信する
ファイルをアップロードし、完了したらユーザーインターフェイスを更新するシナリオを考えてみます。
ファイルのアップロードには時間がかかることがあるため、ユーザーを待たせる必要はありません。 ここで機能を実装するために、サービス、おそらくIntentService
を使用できます。
ただし、この場合、より大きな課題は、ファイルのアップロード(別のスレッドで実行された)が完了した後にUIスレッドでメソッドを呼び出すことができることです。
オプション1:サービス内のRxJava
RxJavaは、それ自体で、またはIntentService
内で、理想的ではない場合があります。 Observable
にサブスクライブするときは、コールバックベースのメカニズムを使用する必要がありますIntentService
は、コールバックではなく、単純な同期呼び出しを実行するように構築されています。
一方、 Service
では、手動でサービスを停止する必要があり、より多くの作業が必要になります。
オプション2:BroadcastReceiver
Androidはこのコンポーネントを提供します。このコンポーネントは、カスタムイベントだけでなく、グローバルイベント(バッテリーイベント、ネットワークイベントなど)もリッスンできます。 このコンポーネントを使用して、アップロードの完了時にトリガーされるカスタムイベントを作成できます。
これを行うには、 BroadcastReceiver
を拡張するカスタムクラスを作成し、それをマニフェストに登録し、 Intent
とIntentFilter
を使用してカスタムイベントを作成する必要があります。 イベントをトリガーするには、 sendBroadcast
メソッドが必要です。

マニフェスト:
<receiver android:name="UploadReceiver"> <intent-filter> <action android:name="com.example.upload"> </action> </intent-filter> </receiver>
受信者:
public class UploadReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getBoolean(“success”, false) { Activity activity = (Activity)context; activity.updateUI(); } }
送信者:
Intent intent = new Intent(); intent.setAction("com.example.upload"); sendBroadcast(intent);
このアプローチは実行可能なオプションです。 しかし、お気づきのように、これにはいくつかの作業が必要であり、ブロードキャストが多すぎると処理が遅くなる可能性があります。
オプション3:ハンドラーの使用
Handler
は、スレッドにアタッチして、単純なメッセージまたはRunnable
可能なタスクを介してそのスレッドで何らかのアクションを実行するように作成できるコンポーネントです。 これは、特定のスレッドでのメッセージ処理を担当する別のコンポーネントであるLooper
と連携して機能します。
Handler
が作成されると、コンストラクターでLooper
オブジェクトを取得できます。これは、ハンドラーが接続されているスレッドを示します。 メインスレッドにアタッチされたハンドラーを使用する場合は、 Looper.getMainLooper()
を呼び出して、メインスレッドに関連付けられたルーパーを使用する必要があります。
この場合、バックグラウンドスレッドからUIを更新するには、UIスレッドにアタッチされたハンドラーを作成してから、アクションをRunnable
として投稿します。
Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { // update the ui from here } });
このアプローチは最初のアプローチよりもはるかに優れていますが、これを行うためのさらに簡単な方法があります…
オプション3:EventBusを使用する
EventBus
によって人気のあるライブラリであるEventBusを使用すると、コンポーネントが相互に安全に通信できるようになります。 私たちのユースケースはUIを更新するだけの場合なので、これが最も簡単で安全な選択になります。
手順1.イベントクラスを作成します。 例: UIEvent
。
ステップ2.イベントを購読します。
@Subscribe(threadMode = ThreadMode.MAIN) public void onUIEvent(UIEvent event) {/* Do something */}; register and unregister eventbus : @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); }
手順3.イベントを投稿しますEventBus.getDefault().post(new UIEvent());
アノテーションのThreadMode
パラメーターを使用して、このイベントをサブスクライブするスレッドを指定します。 ここでの例では、イベントの受信者がUIを更新できるようにするため、メインスレッドを選択しています。
必要に応じて、追加情報を含むようにUIEvent
クラスを構造化できます。
サービス中:
class UploadFileService extends IntentService { // … Boolean success = uploadFile(File file); EventBus.getDefault().post(new UIEvent(success)); // ... }
アクティビティ/フラグメント:
@Subscribe(threadMode = ThreadMode.MAIN) public void onUIEvent(UIEvent event) {//show message according to the action success};
EventBus library
を使用すると、スレッド間の通信がはるかに簡単になります。
ユースケース5:ユーザーアクションに基づくスレッド間の双方向通信
メディアプレーヤーを作成していて、アプリケーション画面を閉じても音楽を再生し続けることができるようにしたいとします。 このシナリオでは、UIがメディアスレッド(再生、一時停止、その他のアクションなど)と通信できるようにし、メディアスレッドが特定のイベント(エラー、バッファリングステータスなど)に基づいてUIを更新できるようにする必要があります。 、など)。
完全なメディアプレーヤーの例は、この記事の範囲を超えています。 ただし、こことここで優れたチュートリアルを見つけることができます。
オプション1:EventBusを使用する
ここでEventBus
を使用できます。 ただし、UIスレッドからイベントを投稿して、サービスで受信することは一般的に安全ではありません。 これは、メッセージを送信したときにサービスが実行されているかどうかを知る方法がないためです。
オプション2:BoundServiceを使用する
BoundService
は、アクティビティ/フラグメントにバインドされるService
です。 これは、アクティビティ/フラグメントがサービスが実行されているかどうかを常に認識し、さらに、サービスのパブリックメソッドにアクセスできることを意味します。
これを実装するには、サービス内にカスタムBinder
を作成し、サービスを返すメソッドを作成する必要があります。
public class MediaService extends Service { private final IBinder mBinder = new MediaBinder(); public class MediaBinder extends Binder { MediaService getService() { // Return this instance of LocalService so clients can call public methods return MediaService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } }
アクティビティをサービスにバインドするには、サービスステータスを監視するクラスであるServiceConnection
を実装し、メソッドbindService
を使用してバインドを行う必要があります。
// in the activity MediaService mService; // flag indicates the bound status boolean mBound; @Override protected void onStart() { super.onStart(); // Bind to LocalService Intent intent = new Intent(this, MediaService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { MediaBinder binder = (MediaBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } };
完全な実装例はここにあります。
ユーザーが[再生]または[一時停止]ボタンをタップしたときにサービスと通信するには、サービスにバインドしてから、サービスの関連するパブリックメソッドを呼び出すことができます。
メディアイベントがあり、それをアクティビティ/フラグメントに戻したい場合は、以前の手法の1つ( BroadcastReceiver
、 Handler
、 EventBus
)を使用できます。
ユースケース6:アクションを並行して実行して結果を得る
観光アプリを作成していて、複数のソース(さまざまなデータプロバイダー)から取得した地図にアトラクションを表示したいとします。 すべてのソースが信頼できるわけではないため、失敗したソースを無視して、とにかくマップのレンダリングを続行することをお勧めします。
プロセスを並列化するには、各API呼び出しを異なるスレッドで実行する必要があります。
オプション1:RxJavaを使用する
RxJavaでは、 merge()
またはconcat()
演算子を使用して、複数のオブザーバブルを1つに結合できます。 次に、「マージされた」オブザーバブルをサブスクライブして、すべての結果を待つことができます。
ただし、このアプローチは期待どおりに機能しません。 1つのAPI呼び出しが失敗した場合、マージされたobservableは全体的な失敗を報告します。
オプション2:ネイティブJavaコンポーネントを使用する
JavaのExecutorService
は、固定(構成可能)数のスレッドを作成し、それらに対して同時にタスクを実行します。 このサービスはFuture
オブジェクトを返し、最終的にはinvokeAll()
メソッドを介してすべての結果を返します。
ExecutorService
に送信する各タスクは、例外をスローできるタスクを作成するためのインターフェースであるCallable
インターフェースに含まれている必要があります。
invokeAll()
から結果を取得したら、すべての結果を確認して、それに応じて続行できます。
たとえば、3つの異なるエンドポイントから入ってくる3つのアトラクションタイプがあり、3つの並列呼び出しを行いたいとします。
ExecutorService pool = Executors.newFixedThreadPool(3); List<Callable<Object>> tasks = new ArrayList<>(); tasks.add(new Callable<Object>() { @Override public Integer call() throws Exception { return mRest.getAttractionType1(); } }); // ... try { List<Future<Object>> results = pool.invokeAll(tasks); for (Future result : results) { try { Object response = result.get(); if (response instance of AttractionType1... {} if (response instance of AttractionType2... {} ... } catch (ExecutionException e) { e.printStackTrace(); } } } catch (InterruptedException e) { e.printStackTrace(); }
このようにして、すべてのアクションを並行して実行します。 したがって、各アクションのエラーを個別にチェックし、必要に応じて個々の失敗を無視することができます。
このアプローチは、RxJavaを使用するよりも簡単です。 これはより単純で短く、1つの例外のためにすべてのアクションが失敗するわけではありません。
ユースケース#7:ローカルSQLiteデータベースのクエリ
ローカルSQLiteデータベースを処理する場合は、データベースの呼び出し(特に大規模なデータベースや複雑なクエリの場合)に時間がかかり、UIがフリーズする可能性があるため、データベースをバックグラウンドスレッドから使用することをお勧めします。
SQLiteデータをクエリすると、実際のデータをフェッチするために使用できるCursor
オブジェクトが取得されます。
Cursor cursor = getData(); String name = cursor.getString(<colum_number>);
オプション1:RxJavaを使用する
バックエンドからデータを取得するのと同じように、RxJavaを使用してデータベースからデータを取得できます。
public Observable<Cursor> getLocalDataObservable() { return Observable.create(subscriber -> { Cursor cursor = mDbHandler.getData(); subscriber.onNext(cursor); }); }
getLocalDataObservable()
によって返されるobservableを次のように使用できます。
getLocalDataObservable() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(cursor -> String name = cursor.getString(0), throwable -> Log.e(“db, "error: %s" + throwable.getMessage()));
これは確かに良いアプローチですが、まさにこのシナリオのために構築されたコンポーネントがあるため、さらに優れたアプローチがあります。
オプション2:CursorLoader+ContentProviderを使用する
Androidには、SQLiteデータをロードして対応するスレッドを管理するためのネイティブコンポーネントであるCursorLoader
が用意されています。 これはCursor
を返すLoader
であり、 getString()
、 getLong()
などの単純なメソッドを呼び出すことでデータを取得するために使用できます。
public class SimpleCursorLoader extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> { public static final String TAG = SimpleCursorLoader.class.getSimpleName(); private static final int LOADER_ID = 0x01; private TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_cursor_loader); textView = (TextView) findViewById(R.id.text_view); getSupportLoaderManager().initLoader(LOADER_ID, null, this); } public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { return new CursorLoader(this, Uri.parse("content://com.github.browep.cursorloader.data") , new String[]{"col1"}, null, null, null); } public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { if (cursor != null && cursor.moveToFirst()) { String text = textView.getText().toString(); while (cursor.moveToNext()) { text += "<br />" + cursor.getString(1); cursor.moveToNext(); } textView.setText(Html.fromHtml(text) ); } } public void onLoaderReset(Loader<Cursor> cursorLoader) { } }
CursorLoader
はContentProvider
コンポーネントと連携します。 このコンポーネントは、開発者がより優れたユーザーエクスペリエンスをはるかに簡単に実装できるようにする、多数のリアルタイムデータベース機能(変更通知、トリガーなど)を提供します。
Androidでのスレッド化に対する特効薬の解決策はありません
Androidはスレッドを処理および管理するための多くの方法を提供しますが、それらのどれも特効薬ではありません。
ユースケースに応じて適切なスレッド化アプローチを選択することで、ソリューション全体の実装と理解を容易にする上ですべての違いを生むことができます。 ネイティブコンポーネントは、場合によってはうまく適合しますが、すべてではありません。 同じことが、派手なサードパーティのソリューションにも当てはまります。
この記事が次のAndroidプロジェクトで作業するときに役立つことを願っています。 以下のコメントで、Androidでのスレッド化の経験、または上記のソリューションがうまく機能する(またはうまくいかない)ユースケースを共有してください。