AsyncTask が非推奨になったので代わりに、Executorを使った非同期処理はどう実装するのか見てみます
2021.2.1
非同期処理
非同期処理が必要なケースは、メインスレッドでアプリがUIを表示させたり、ユーザーが入力したりしている裏でいろいろと処理をして欲しい場合です
Google AsyncTask によれば、代わりに
java.util.concurrent or Kotlin concurrency utilities が推奨されています。
ここでは、java.util.concurrent.Executor を使っていきます。
画像などの大きなファイルをダウンロードする場合も非同期を使います
尚、AsyncTask はこちらにしばらく残しておきます
非同期処理 AsyncTaskの使い方 (API 30から非推奨)
AsyncTaskの基本的構造
AsynTaskの構造はこうなっていました
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class TestTask extends AsyncTask<Integer, Integer, Integer> { // 前処理 @Override protected void onPreExecute() { // ... } // 非同期処理 @Override protected Integer doInBackground(Integer... params) { return params[0] ; } // 途中経過をメインスレッドに返す @Override protected void onProgressUpdate(Integer... progress) { // ... } // 非同期処理が終了後、結果をメインスレッドに返す @Override protected void onPostExecute(Integer result) { // ... } } |
これをExecutorに当てはめていきますが、
java.util.concurrent.Executorを継承した
ExecutorService から、単一のワーカー・スレッドだけを設定する
newSingleThreadExecutor()
のメソッドを使っていきます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
... ExecutorService executorService; executorService = Executors.newSingleThreadExecutor(); ... private class TaskRun implements Runnable { @Override public void run() { Log.d("debug", "doInBackground()"); // 非同期処理 new Handler(Looper.getMainLooper()) .post(() -> onPostExecute()); } } void execute() { onPreExecute(); executorService.submit(TaskRun()); } void onPreExecute() { Log.d("debug", "onPreExecute()"); // 前処理 } void onPostExecute() { Log.d("debug","onPostExecute()"); // 非同期処理が終了後、結果をメインスレッドに返す } |
サンプルコード
重いTaskを非同期で実行させ結果を表示させるサンプルです
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
//package your.package.name; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.widget.Button; import android.widget.TextView; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MainActivity extends AppCompatActivity { private MyTask asyncProgress; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.textview); Button button1 = findViewById(R.id.button); button1.setOnClickListener( v -> { textView.setText("0"); asyncProgress = new MyTask(textView); asyncProgress.execute(); }); } private static class MyTask { ExecutorService executorService; TextView textView; double num; public MyTask(TextView textView) { super(); this.textView = textView; executorService = Executors.newSingleThreadExecutor(); } private class TaskRun implements Runnable { @Override public void run() { Log.d("debug", "doInBackground()"); for (int i = 0; i < 1000000000; i++) { num = (double) i / 7 + 0.3; } new Handler(Looper.getMainLooper()) .post(() -> onPostExecute(num)); } } void execute() { onPreExecute(); executorService.submit(new TaskRun()); } void onPreExecute() { Log.d("debug", "onPreExecute()"); num = 0; } void onPostExecute(double number) { String str = "Total= " +number; textView.setText(str); Log.d("debug","onPostExecute() "+str); } } } |
レイアウトです
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.3" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.5" /> </androidx.constraintlayout.widget.ConstraintLayout> |
リソースです
strings.xml
1 2 3 4 |
<resources> <string name="app_name">TestExecutor</string> <string name="button">button</string> </resources> |
実行してみます
関連ページ:
- カスタム Listener を interface を使って実装してみる
- 非同期処理 Executorの使い方
- HttpURLConnection POST でデータを送信する
- HttpURLConnection GET で画像をダウンロードする
References:
GoogleのReference
ThreadPoolExecutor | Android Developers