迷走しました!
過去の関連記事もなんだか分からなかくなってるので、自分用の整理まとめメモ!なので他の記事と内容やコードが重複してます。
#Threadの概説
Androidは、基本的に1つのLinuxプロセスに、一つのスレッドを作って稼働するシングルスレッドモデルです(Manifestで定義すれば、別のプロセスに別のスレッドを作ってプログラムを稼働さたりも!)。
Androidでは、ユーザーエクスペリエンスを確保するため、ユーザー操作に影響を与えるような処理は、別スレッドで処理すべき、ってことになってて、あとUI部品はUIスレッドからしか操作できない、というルールもあります(UIはユーザーインターフェイス)。別スレッドはワーカースレッド(ユーザーインターフェイスを持たない)と呼ばれたりします。
別スレッドで処理させたりすると、UIスレッドとワーカースレッドとか、ワーカースレッド同士で通信する必要が生じます。で、UI部品はUIスレッドからしか操作できないので、ワーカースレッドからUI部品を更新する方法も知っとく必要があったりします。
通信のために、メインスレッド(UIスレッド)には、Looperが備わっていて、Looper内にMessageQueueを持ってます。MessageQueueは、他のスレッドから送られるMessageやRunnableを積み上げ保持し、Looperが取り出して処理します。
<注意>
ワーカースレッドの処理結果を、UIスレッドに渡して、UI部品を更新する作業は、ThreadクラスやRunnableインターフェイスを使ったマルチスレッド処理だと、複雑になりやすく、見通しが悪くなり、メンテナンスも困難になるって皆んなが言ってる。ハイコストなコードになってしまうのですね。
リファレンス的推奨はAsncTaskクラスです。
マルチスレッド間で結果をやり取りできる通信手段はHandlerクラス!
#Threadの生成の概説
詳しくは
###ThreadはThreadクラスとRunnableインターフェイスのどっちを使う?
ThreadクラスはRunnableインターフェイスを継承したクラスで、Runnableと違って色んな定数やメソッドを持ってます。Threadクラスの定数やメソッドを使うのなら、Threadクラスを拡張して、特に使うメソッドとかがないのならRunnableを承継して、ワーカースレッドを作ればいいのかな?
###ワーカースレッドとUIスレッドの通信
ThreadクラスやRunnableインターフェースで作成したワーカースレッドから、処理の結果をUIスレッド(MainActivity)に引き渡すには、Viewクラスの**post(Runnable)**メソッド、**postDelayed(Runnable,long)とかActivityクラスのrunOnUiThread(Runnable)**を使います。HandlerクラスでもOK。
#AnsyTaskとかHandlerとか
ThreadクラスやRunnableクラス以外の方法では、UIスレッドとの通信パッケージとなるAsyncTaskクラスがあります。またスレッド(UIスレッド以外も)間の通信を実現するHandlerクラスがあります。
##AsyncTask
詳しくは!
ThreadクラスやRunnableインターフェイス以外の方法で、別スレッドからUI部品を操作するのなら、リファレンス推奨なAsyncTask!いやむしろAsyncTask!
AsyncTaskは短時間処理に適してます。長時間スレッドで処理をさせたいのならExesutor,ThreadPoolExecuter,FutureTaskなど、java.util.concurrentパッケージで提供されるAPIsを検討してね!
AsyncTaskは並行処理によるエラーを避けるため、シングルスレッドで処理されます。並行処理をするのならTHREAD_POOL_EXECUTORとともに、executeOnExecutor(java.util.concurrent.Executor,java.lang.Object[])を検討しましょう
####Andorid.os.AsyncTaskの用法
□ UIスレッド側
AsyncTask継承クラスをインスタンス化して**Execute()**を実行する
□ AsyncTask継承クラス(青字は必須)
onPreExecute()
doInBackground()
// todo
publishProgress()
onProgressUpdate()
onPostExecute()
#####各メソッド
Execute() | doInBackground()を呼び出すdoInBackgorundが処理するタスクに必要な引数を渡す |
onPreExecute() | Execute()の実行前にしたい処理を実行 |
doInBackgorund() | Execute()で起動し、Execute()から引き受ける引数で処理を実行 |
publichProgress | doInBackground()でタスクを処理してる間に、onProgressUpdate()でユーザーに表示したい文字やProgressを定義 |
onProgressUpdate | UI部品の操作をUIスレッドに渡す。 |
onPostExecute | doInBackground()から処理結果を受け取り、UI部品の操作をUIスレッドに渡す |
##android.os.Handler スレッド間の通信手段
詳しくはこちらの 「Handlerまとめ的な」
Handlerはマルチスレッドで(UIスレッドやワーカースレッド同士)で処理結果を送れます。
Handlerクラスをインスタンス化(handler)すると、インスタンス化したスレッドに、LooperとMessageQueueを備えます。
handlerは他のスレッドから使えるので、**post()、handle.obtainMessage()、handle.sendMessage()**などを使って、handlerを生成したメソッドにメッセージやRunnableのようなオブジェクトを送って、生成スレッドで処理を行います。
#サンプルコード
##Thread
こちら
##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(10003) -> publishProgress(string[0]) -> sleep(10003) -> return "End doInBackground" -
onProgressUpdate()
textView.setText(string[0]) -
onPostExecute()
textView.setText(s)
-
onPreExecute()
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);
}
}
}
##Handler
####HandlerとMessageの事例1
public class MainActivity extends AppCompatActivity {
TextView textView;
// UIスレッド(MainActivity)でHandlerインスタンスを生成します
// これでMessageQueueもLooperもUIスレッドの物だね
// インスタンス handler は別スレッドから参照可能なので
// 別スレッドで handler を参照して、messageを載せれば
// UIスレッド(MainActivity)でLooperが処理してくれるんだぜ!
// カッコいいじゃん!!
// では、handler さんに登場願います!
Handler handler = new MyHandler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView1);
MyLovelyMessage();
}
private void MyLovelyMessage() {
// UIスレッド(MainActivity)で生成した handler にアクセスし
// handler に Messageを載せて sendMessage!
// これでUIスレッドのLooperが上手くやってくれるはず!
Message message = handler.obtainMessage(1);
handler.sendMessage(message);
}
class MyHandler extends Handler{
@Override
public void handleMessage(Message message){
// handle が UIスレッドで何をするかを定義する
// Looperによって自動的に実行されるよ!
String msg = String.valueOf(message.what);
textView.setText(msg);
}
}
}
上のコードを内部関数的にシンプルに
public class MainActivity extends AppCompatActivity {
TextView textView;
Handler handler = new Handler(){
@Override
public void handleMessage(Message message){
String msg = String.valueOf(message.what);
textView.setText(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView1);
MyLovelyRunnable();
}
private void MyLovelyRunnable() {
Message message = handler.obtainMessage(1);
handler.sendMessage(message);
}
}
####HandlerとRunnableを使った事例
public class MainActivity extends AppCompatActivity {
TextView textView;
// HandlerをUIスレッド(MainAvtivity.class)で生成します
// これで、MessageQueue も Looper も、このスレッド(UIスレッド)に関連付けれます
//
// Handlerインスタンスは、他のスレッドからも参照できるので
// 他のスレッドで、インスタンス handler を使って
// Runnableをこのスレッド(UIスレッド)に渡せば
// このスレッド(UIスレッド側)でRunnableを実行できるようになります
// 素敵ですね!
// ではHandlerのインスタンスを生成しましょう!!
Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView1);
MyLovelyHandler();
}
private void MyLovelyHandler(){
new Thread(new Runnable() {
@Override
public void run() {
// UIスレッド(MainActivity)で生成したHandlerのインスタンスは
// 他のスレッドからでも参照可能です
// インスタンスhandlerにRunnableを載せて
// UIスレッド(MainActivity)に渡して処理してもらいましょう!
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("I Love Handler");
}
});
}
}).start();
}
}