LoginSignup
3
4

More than 3 years have passed since last update.

51歳からのプログラミング 備忘 AsyncTask - reference [写経]

Last updated at Posted at 2019-09-10

https://developer.android.com/reference/android/os/AsyncTask

個人的な写経的なまとめ的なものです。誤記誤訳があったらごめんね。

マルチスレッドまとめ的なのはここ

public abstract class AsyncTask extends Object
android.os.AsyncTask<Param,Progress,Result>

非同期通信後の処理や、AsyncTaskの汎用化といか共通化?について

AsyncTask概要

AsyncTaskは簡単かつ適切にUIスレッドを扱ます。ThreadやHandlerを使わずに、バックグラウンドで処理し、結果をUIスレッドに戻します。

AsyncTaskはThreadやHandlerなどを扱うヘルパークラス(インスタンス化して使う:staticメソッドはない)としてデザインされてます。一般的なスレッドフレームワークになるようには構成されてません。

AsyncTaskは数秒程度の処理に適してます。長い間スレッド処理させたい場合には、Exesutor,ThreadPoolExecuter,FutureTaskなど、java.util.concurrentパッケージで提供されるAPIsを使ってね。

非同期のタスクは、バックグラウンドスレッドで実行されてUIスレッドに渡される結果によって定義されます?。つまり、以下の引数のタイプと、4つのステップで定義します。

  • AsyncTaskの引数

    • Param
    • Progress
    • Result
  • 4つのステップ

    • onPreExecute
    • doInBackground
    • onProgressUpdate
    • onPostExecut

使い方

AsyncTaskはサブクラス化して使い、少なくとも1つ以上のメソッドをオーバーライドします(doInBackground(Param...))、またほとんどの場合、2つ目もオーバーライドします(onPostExecute(Result))。

以下はサブクラス化の例

 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }

一度生成すれば、タスクの実行は実に簡単!

 new DownloadFilesTask().execute(url1, url2, url3);

AsyncTaskの引数<総称型タイプ>

AsyncTaskM<Param,Progress,Result>

1.Param
実行時にタスクに渡すパラメータタイプ
2.Progress
バックグランド処理の間に表示させる進捗ユニットのタイプ
3.Result
バックグラウンド処理の結果のタイプ

全てのタイプを使う必要はなく、voidで済ますこともできます。
こんな感じで

 private class MyTask extends AsyncTask<Void,Void,Void>

4つのステップ

非同期タスクが実行されるとき、タスクは4つのステップを踏みます。

  1. onPreExecute()
    タスクが処理される前にUIスレッドに呼び出す準備的なメソッド。例えば、Execute()の実行時に、プログレスバーを表示させたい時などに、onPreExecute()でプログレスバーの表示を準備させたりするって感じ。
  2. doInBackground(Params...):オーバーライド必須
    onPreExecute()処理後、すぐにバックグラウンドスレッドで呼び出されます。バックグラウンド処理に時間を要してもOKだよっていう処理に使うステップです。このステップで、非同期タスクのパラメータが引き渡されます。処理結果は必ずこのステップで返され、最後のステップに結果を渡します。このステップは、プログレスのユニットを表示するpublishProgress(Progress)にも使え、そのケースではonProgressUpdate(Progress...)のステップでUIスレッドに表示されます。
  3. onProgressUpdate(Progress...)
    publishProgress(Progress...)の実行後にUIスレッドに呼び出されます。実行のタイミングは定義されてません。このメソッドは色んなプログレスフォームを表示するのに使われます。プログレスのフォームは、バックグラウンド処理の間、UIに表示されます。例えばアニメーションプログレスバーとか、テキストフィールドでのログ表示とか。
  4. onPostExecute(Result)
    バックグラウンド処理の終了後にUIスレッドに呼ばれます。バックグラウンド処理の結果はこのステップにパラメーターとして返されます。

タスクキャンセル

タスクキャンセルはcancel(boolea)で実行してね。cancel()を実行すると(成功すると)、後でisCancelled()が呼ばれてtrueを返します。通常ならdoInBackground(java.lang.Object[])を実行するとonPostExecute(java.lang.Object)が実行されますが、cannel()を実行すると、onPostExecute()の代わりにonCannelled(java.lang.Object)が実行されるって感じの流れ。タスクがちゃんとキャンセルされてるか確認するために、doInBackground(java.lang.Object[])からisCancelled()の戻り値(キャンセルされてればtrue!)が発行されるので、可能な限り素早くチェック!


スレッドルール

スレッドを適切に使うために、幾つかのルールがあります。

  • AsyncTaskはUIスレッド側で呼び出します。AndroidのバージョンがJELLY_BEAN以降は自動的にそうなります。
  • タスクインスタンスはUIスレッド側で作成
  • UI側でexecute(Params...)を実行
  • 直接、onPreExesute(),onPostExecute(Result),doInBackground(Prams...),onProgressUpdate(Progress...)を呼び出さないでね。Androidシステムに任せてください!
  • タスクは1回だけ実行できます。もし2回目が実行されると、例外を投げられます。

メモリの確認

AsyncTaskは、明示的に同期するケースを除き、以下を確認するために、全てのコールバック関数の呼び出しが同期されることを保証します。

  • onPreExecute()のメモリへの影響、execute(Params...)の呼び出し前に実行されたメソッド等(AsyncTaskオブジェクトの構築)のメモリへの影響は、doInBackground(Params...)に表示される
  • doInBackground(Params...) メモリへの影響は、onPostExecute(Result)に表示される
  • publishProgress(Progres...)を呼び出す前のdoInBackgrund(Params...)のメモリへの影響は、対応するonProgressUpdate(Progress..)で表示される。ただし、doInBackground(Params...)は引き続き実行されるため、doInBackground(Params...)の更新が、進行中のonProgressUpdata(Progress...) の呼び出しに干渉しないように注意が必要。
  • cancel(boolean)のメモリへの影響は、calcek(boolean)に先行して呼び出されるisCancelled()(実行結果でtrueを返す)の呼び出しの後とか、onCancelled()の呼び出し中や実行後に表示されます。

AsyncTaskの生い立ち

当初、AsyncTasksはバックグラウンドのシングルスレッドで順次処理されてました。AndroidのバージョンがBuild.VERSION_CODES.DONUTからは、並行処理するマルチタスクを扱えるように、スレッドプールに変化してきた。AndroidバージョンがBuild.VERSION_CODES.HONEYCOMBからは、並行処理が原因で発生するアプリケーションエラーを避けるため、タスクはシングルスレッドで実行するようになっています。

もし、並行処理をするのならTHREAD_POOL_EXECUTORとともにexecuteOnExecutor(java.util.concurrent.Executor,java.lang.Object[])を実行しよう!。

概要

■ Nested classes
enum
AsiyncTask.Status
タスクの現在のステータスを示す
■ Fields

public static final Executor

SERIAL_EXECUTOR
タスクを一つずつ順次処理させるExecutor
HREAD_POOL_EXECUTOR
並列処理時に使われるExecutor
■ Public constructors
AsyncTask<>
新しい同期タスクを生成
■ Public methods
final boolean cancel(boolean mayInterruptIfRunning)
タスクをキャンセル処理< /td>
final AsyncTask execute(Params... param)
パラメーターを指定してタスクを処理
static void execute(Runnable runnable)
シンプルなRunnableオブジェクトを、手軽に処理するためのexecute(java.lang.Object)
final AsyncTask executeOnExecutor(Executor exec,Params... param>
パラメータを指定してタスクを処理
final Result get(long timeout,TimeUnit unit)
処理を完遂するのに時間がかかる場合に待機して結果を取得する
final Result get()
処理を完遂するのに必要なら待機して結果を取得する
final AsyncTask.Status getStatus
タスクの現在の状態を返す
final boolean isCancelled()
タスクが正常に終了する前にキャンセルが成功したらtrueを返す

Protected methods
abstract Result doInBackground(Params... params)
バックグラウンドスレッドで処理を実行するのにオバーライドするメソッド
void onCancelled()
できればonCancelled(java.lang.Object)をオーバーライドしよう!
void onCancelled(Result result)
cancel(boolean)を実行し、doInBackground(java.lang.Object[])が終了した後で、UIスレッドで稼働する
void onPostExecute(Result result)
doInBackground(Params...)の後でUIスレッドで稼働する
void onPreExecute()
doInBackground(Params...)の前にUIスレッドで稼働する
void onProgressUpdate(Progress... values)
publishProgress(Progress...)が呼び出された後に、UIスレッドで稼働する
final void publishProgress(Progress... values)
このメソッドは、バックグラウンドで処理中にUIスレッドでアップデートするためにdoInBackground(Params...)から呼び出される
継承されたメソッド
From class java.lang.Object

AsyncTaskまとめ的な

AsyncTaskはサブクラス化して使う
AsyncTask<Params,Progress,Result>

  • Params
    doInBackground()で使う引数型を指定
  • Progress
    実行中の情報をユーザーに表示するメソッドの型
    publishProgress(),onProgressUpdate()で使う引数型を指定
  • Result
    結果表示のメソッド
    onPostExecute()で使う引数型を指定

AsyncTaskが引数不要なら、AsyncTask<void,void,void>

主なメソッド

  • execute()
  • publishProgress()

  • @Overrideメソッド(軌道はAndroidシステムに任せてね)

    • onPreExecute()
    • doInBackground()
    • onProgressUpdate()
    • onPostExecute()
・execute(Params... params)

UIスレッド側で実行するメソッド。オーバーライドメソッドのdoInBackground()に引数を渡してdoInBackgroundを実行するよ!。

引数の型はAsyncTaskの引数型を参照してね
引数はdoInBackground()を実行するに使います。

・onPreExecute

Execute()実行前にしたい処理があればここで!

・doInBackground()

オーバーライド必須のメソッド!
execute()から引数を受け取って、バックグラウンドでタスク処理。
doInBackground()処理後にonPostExecute()を呼び、doInBackground()処理の結果をonPostExecute()に渡します。もしcancel()を使うとonPostExecute()の代わりに、onCannelled()が呼ばれるよ。
doInBackground()はタスク処理専用。UIスレッドの更新は他のメソッドで!

・onPostExecute()

doInBackground()内で使って、doInBackground()の結果を、UIスレッド側で表示するメソッド。AsyncTaskの引数Resultを使うのです。

・publishProgress()

進捗表示を使いたいときにどうぞ!
doInBackground()でpublishProgress()を実行すると、Androidシステムは、UIスレッドでonProgressUpdate()を実行します。

・onProgressUpdate()

doInBackground()でpublishProgress()を実行すると、Androidシステムは、UIスレッドでonProgressUpdate()を実行
引数はAsyncTaskの引数を参照


サンプルコード:execute()とdoInBackground()

  • MainActivity.java
    AsyncTask<>をサブクラス化し、execute()を実行する
  • AsyncTask<>クラスを拡張して、MyAsyncTaskクラスを生成
    doInBackground()は、MainActivity.javaのexecute()によって処理を実行する(todo)。doInBackground()はexecute()から引数を受け取るって処理に使うよ。
public class MainActivity extends AppCompatActivity {

    public static TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // タスクインスタンスを生成。execute()で引数をonInBackground()に渡す
        // new MyAsyncTask().execute();  でもOK
        MyAsyncTask myAsyncTask = new MyAsyncTask();
        myAsyncTask.execute();

    }
}

class MyAsyncTask extends AsyncTask<Void,Void,Void> {
    @Override
    protected Void doInBackground(Void... aVoid) {
        // todo
        Log.d("msg","In MyAsyncTask");
        return null;
    }
}

サンプルコード:幾つかメソッドを入れてみる

3秒毎にtextViewの表示を変えるコード

  • MyActivity.onCreate()
    myAsncTask.excute("In doInBackground")
  • class MyAsyncTask
    • onPreExecute()
      textView.setText("onPreExecute")
    • doInBackground()
      sleep(1000*3) -> publishProgress(string[0]) -> sleep(1000*3) -> return "End doInBackground"
    • onProgressUpdate()
      textView.setText(string[0])
    • onPostExecute()
      textView.setText(s)
public class MainActivity extends AppCompatActivity {

    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);

        // タスクインスタンスを生成。execute()で引数をonInBackground()に渡す
        // new MyAsyncTask<>.execute();  でもOK
        MyAsyncTask myAsyncTask = new MyAsyncTask();
        // doInBackground()に文字列("In doInBackground")を渡して
        // onPreExecute()の処理後に、doInBackground()を実行
        myAsyncTask.execute("In doInBackground");

    }

    class MyAsyncTask extends AsyncTask<String,String, String> {

        // execute("In doInBackground")処理前に実行。UIスレッドのtextViewに表示
        @Override
        protected void onPreExecute(){
            textView.setText("onPreExecute");
        }

        // execute()から文字列"In doInBackground"を受け取る
        @Override
        protected String doInBackground(String... string) {

            // 3秒待機
            try {
                sleep(1000*3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // doInBackground処理中に、textViewを表示させるonPublishUpdate()を呼ぶ
            publishProgress(string[0]);

            // 3秒待機
            try {
                sleep(1000*3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // onPostExecute()に文字列"End doInBackground"を渡す
            return "End doInBackground";
        }

        // doInBackground内のpublishProgress()から文字列を受け取り、UIスレッドのtextViewに表示
        protected void onProgressUpdate(String... string){
            textView.setText(string[0]);
        }

        // doInBackground()からreturn値("End doInBackground")を受け取り、UIスレッドのtextViewに表示
        @Override
        protected void onPostExecute(String s){
            textView.setText(s);
        }
    }
}

ActivityからAsyncTaskにパラメータを渡す

複数のパラメータを一つ一つ渡す

MainActivity
...
new MyAsyncTask().execute("param1","param2");
MyAsyncTask
...
@Override
protected String doInBackground(String... string){
   String param1 = string[0];
   String param2 = string[1];
return null
}

複数のパラメータを配列にして渡す

MainActivity
Map<String,String> map = new HashMap<String,String>();
   map.put("param1","data1");
   map.put("param2","data2");
   map.put("param3","data3");

new MyAsyncTask().execute(map);
MyAsyncTask
public class MyAsyncTask extends AsyncTask<Map<String,String>,Void,Map<String,String>>{

   @Override
   protected Map<String,String> doInBackground(Map<String,String>... map){
      Map<String,String> mp = map[0];
      // mpをJsonにするのなら
      // JSONObject jsonMap = new JSONObject(mp);
      return mp;
   }

   @Override
   protected void onPostExecute(Map<String,String> mp){
      super.onPostExecute(mp);
      String val1 = mp.get("param1"); // val1にdata1が格納された
      String val2 = mp.get("param1"); // val2にdata2が格納された
      String val3 = mp.get("param3"); // val3にdata3が格納された
   }
}

ちょっと時間かかったね。いつも通り亀の歩み。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4