バックグラウンドでタスクを実行する

Stackoverflowでは、Androidでバックグラウンドタスクを実行することについて次のような質問がよく寄せられます。 与えられた期間で繰り返す。 原則として、最初に使用されるのはServiceです。



場合によっては、このようなアプローチはブレーキをかけ、ユーザーインターフェイスの応答速度を低下させる可能性があります。 いつ発生し、どのように対処するかを説明します...



なぜサービスを「減速」できるのか



各Androidアプリケーションは、デフォルトで個別のプロセスで起動されます。 各プロセスで、スレッドが開始されます。 デフォルトでは、すべてのアプリケーションコンポーネント(Activity、Service、BroadcastReceiver)は1つの「メイン」スレッド(別名UIスレッド)で起動されます。 たとえば、サービス内で長いネットワークコールや何らかの重い初期化を開始すると、アプリケーション全体、そのインターフェイス、そしておそらく、Forceを閉じるようにという申し出を受けます...しかし、アプリケーションの他の部分と同じスレッドでのサービスの動作はその利点-インターフェイス要素にアクセスできます。 サービスが別のスレッドで実行されている場合、UIにアクセスできません。



どうする



この問題を解決するには、重いタスクに別のスレッドを使用する価値があります。 実際、サービス内で作成する必要さえありません...



別のスレッドでタスクを実行するには、次のSDKツールを使用できます。



スレッドを開始



ストリームは簡単に作成できます...



Thread myThread = new Thread(new Runnable() { @Override pubic void run() { doLongAndComplicatedTask(); } }); myThread.start(); // 
      
      





すべてが単純ですが、長いタスクを完了した後、UIを更新するときに問題が発生します。



 final TextView txtResult = (TextView)findViewById(R.id.txtResult); Thread myThread = new Thread(new Runnable() { @Override public void run() { txtResult.setText(doLongAndComplicatedTask()); } }); myThread.start();
      
      





実行の結果、エラーが発生します。 「エイリアン」スレッドがUIにアクセスしようとしました! 治す方法は? ハンドラーを使用する必要があります。 コードを修正しましょう...



 final Handler myHandler = new Handler(); //     . final TextView txtResult = (TextView)findViewById(R.id.txtResult); Thread myThread = new Thread(new Runnable() { final String result = doLongAndComplicatedTask(); myHandler.post(new Runnable() { //  Handler,   UI-Thread @Override public void run() { txtResult.setText(result); //    } }); }); myThread.start();
      
      





AsyncTask-すべてが簡単です



このようなタスクをAndroid SDKに実装するには、組み込みツールAsyncTaskがあります。 このクラスを使用すると、コードがどのスレッドで実行されているかを考えずに、すべてが自動的に行われます。 AsyncTaskで書き直された上記の例を検討してください。



 class LongAndComplicatedTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... noargs) { return doLongAndComplicatedTask(); } @Override protected void onPostExecute(String result) { txtResult.setText(result); } } LongAndComplicatedTask longTask = new LongAndComplicatedTask(); //   longTask.execute(); // 
      
      





doInBackgroundメソッドは別のスレッドで実行され、その実行結果はonPostExecuteメソッドに渡されます。onPostExecuteメソッドはUI-Threadで実行されます

次のことに注意してください。



そして、タスクを一定の間隔で定期的に実行する必要がある場合はどうなりますか?



タイマー 定期的な起動への最も簡単なアプローチ。



Javaは、繰り返しタスクを実行するためのタイマーを提供します。 前の例のAsyncTaskを1分間に1回実行してみましょう...



 Timer myTimer = new Timer(); //   final Handler uiHandler = new Handler(); final TextView txtResult = (TextView)findViewById(R.id.txtResult); myTimer.schedule(new TimerTask() { //   @Override public void run() { final String result = doLongAndComplicatedTask(); uiHandler.post(new Runnable() { @Override public void run() { txtResult.setText(result); } }); }); }, 0L, 60L * 1000); //  - 60000 , 0    .
      
      







「長期にわたる」タスクがUIへのアクセスを必要としない場合、すべてが簡素化されることに注意してください。 この場合、ハンドラもAsyncTaskも必要ありません。



ところで、タイマーにはscheduleAtFixedRate()メソッドもあります。 それとスケジュール()の違いは、ドキュメントに記載されています。



より柔軟な方法。 ScheduledThreadPoolExecutor。



Timerを使用する代わりに、 ScheduledThreadPoolExecutorクラスが推奨されています。 このクラスを使用すると、スレッドのプールを整理し、それに関して実行するタスクを計画できます。 つまり タスクキューを整理するためのクラスは1つだけですが、利用可能なスレッドの数が許せば、複数のタスクを同時に実行できます。 利点の詳細については、ドキュメントを参照してください



スケジュールされたタスクごとに、その「記述子」が利用可能です-たとえば、プールの残りの部分に触れることなく特定のタスクの実行をキャンセルすることができるScheduledFuture



アスタリスク付きのタスク。 自転車。 スレッド、ルーパー、ハンドラー。



また、キューを使用して自転車のストリームを組み立てることもできます。



 public class LoopingThread extends Thread { private CountdownLatch syncLatch = new CountdownLatch(1); private Handler handler; public LoopingThread() { super(); start(); } @Override public void run() { try { Looper.prepare(); handler = new Handler(); syncLatch.countDown(); Looper.loop(); } catch(Exception e) { Log.d("LoopingThread", e.getMessage()); } } public Handler getHandler() { syncLatch.await(); return handler; } } Thread loopThread = new LoopingThread(); //    loopThread.getHandler().post(new Runnable() { @Override public void run() { doLongAndComplicatedTask(); } });
      
      





このスレッドは、一度起動すると、永久に実行されます。 彼と通信する(タスクを送信する)には、getHandler()メソッドを使用してハンドラーを取得し、「イベント」を送信できます。 CountdownLatchは同期に使用されるため、ハンドラーを受信するスレッドは、ワーカースレッドが開始されてハンドラーが作成される前にハンドラーを受信しません。



All Articles