Androidスレッド:知っておくべきことすべて

公開: 2022-03-11

すべてのAndroid開発者は、ある時点または別の時点で、アプリケーション内のスレッドを処理する必要があります。

アプリケーションがAndroidで起動されると、「メイン」スレッドと呼ばれる最初の実行スレッドが作成されます。 メインスレッドは、適切なユーザーインターフェイスウィジェットへのイベントのディスパッチと、AndroidUIツールキットのコンポーネントとの通信を担当します。

アプリケーションの応答性を維持するには、メインスレッドを使用して、ブロックされたままになる可能性のある操作を実行しないようにすることが重要です。

ネットワーク操作とデータベース呼び出し、および特定のコンポーネントのロードは、メインスレッドで回避する必要がある操作の一般的な例です。 それらがメインスレッドで呼び出されると、同期的に呼び出されます。つまり、操作が完了するまでUIは完全に応答しなくなります。 このため、通常は別々のスレッドで実行されます。これにより、実行中にUIがブロックされるのを回避できます(つまり、UIから非同期で実行されます)。

Androidには、スレッドを作成および管理するための多くの方法が用意されており、スレッド管理をより快適にする多くのサードパーティライブラリが存在します。 ただし、手元にある非常に多くの異なるアプローチでは、適切なアプローチを選択することは非常に混乱する可能性があります。

この記事では、スレッド化が不可欠になるAndroid開発のいくつかの一般的なシナリオと、それらのシナリオなどに適用できるいくつかの簡単なソリューションについて学習します。

Androidでのスレッド

Androidでは、すべてのスレッドコンポーネントを2つの基本的なカテゴリに分類できます。

  1. アクティビティ/フラグメントに接続されているスレッド:これらのスレッドは、アクティビティ/フラグメントのライフサイクルに関連付けられており、アクティビティ/フラグメントが破棄されるとすぐに終了します。
  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は不十分です。 画面の回転のような単純なものでも、アクティビティが破壊される可能性があることに注意してください。

ローダー

ローダーは、上記の問題の解決策です。 ローダーは、アクティビティが破棄されると自動的に停止し、アクティビティが再作成された後に自動的に再起動することもできます。

ローダーには主に、 AsyncTaskLoaderCursorLoaderの2種類があります。 CursorLoaderの詳細については、この記事の後半で説明します。

AsyncTaskLoaderAsyncTaskに似ていますが、少し複雑です。

使用例:

 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には、 ObserverSubscriberの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を拡張するカスタムクラスを作成し、それをマニフェストに登録し、 IntentIntentFilterを使用してカスタムイベントを作成する必要があります。 イベントをトリガーするには、 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つ( BroadcastReceiverHandlerEventBus )を使用できます。

ユースケース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) { } }

CursorLoaderContentProviderコンポーネントと連携します。 このコンポーネントは、開発者がより優れたユーザーエクスペリエンスをはるかに簡単に実装できるようにする、多数のリアルタイムデータベース機能(変更通知、トリガーなど)を提供します。

Androidでのスレッド化に対する特効薬の解決策はありません

Androidはスレッドを処理および管理するための多くの方法を提供しますが、それらのどれも特効薬ではありません。

ユースケースに応じて適切なスレッド化アプローチを選択することで、ソリューション全体の実装と理解を容易にする上ですべての違いを生むことができます。 ネイティブコンポーネントは、場合によってはうまく適合しますが、すべてではありません。 同じことが、派手なサードパーティのソリューションにも当てはまります。

この記事が次のAndroidプロジェクトで作業するときに役立つことを願っています。 以下のコメントで、Androidでのスレッド化の経験、または上記のソリューションがうまく機能する(またはうまくいかない)ユースケースを共有してください。