Handlerについて

目次へ

Handlerとは

Androidでは、ユーザがアプリを起動すると、UIスレッド(メインスレッド)が作成され、 ユーザがタップするなどのイベントが発生すると、メッセージキューにそのイベントが並び、順番に実行されていきます。
そのため、UIスレッド上で実行する処理が非常に長いと、メッセージキューの次に並んでいる処理が実行できなくなるため、 UIスレッドでは、長い処理を行ってはいけないことになっており、通信などの長い時間のかかる処理は 別のスレッドを作成してその中で実行することになります。
しかし、画面の操作などは、UIスレッドでしか実行することができません。 UIスレッド以外のスレッドで、UIオブジェクトを参照したり変更したりするとクラッシュ、その他の不正動作が発生する可能性があります。
そのため、別スレッドからその結果を、画面に表示するなどの場合に使うのが、HandlerあるいはAsyncTaskです。 同じことができますが、ここではHandlerを説明しますので。AsyncTaskについてはこちらをご覧ください。

Handlerはどのように動作するのか

GUIのアプリケーションでは、メッセージキューにたまっている処理を順番に実行していくループがあります。
Androidでも同じで、スレッドごとにメッセージキューを持ち、Looperというクラスのloopメソッドが、 呼び出されたスレッド上で、キューの内容を実行します。 Looperオブジェクトはスレッドに関連付けられており、関連付けられたスレッド上のメッセージループを実行します。
Handlerはどのように動作するのかというとHandlerのコンストラクタで、Looperオブジェクトから そのLooperオブジェクトが関連付いているスレッドのメッセージキューを取得し、それに対してメッセージを送ります。 HandlerのインスタンスはUIスレッド上で作成する必要があります。そうすることにより、 UIスレッドに関連付いたLooperオブジェクトを使うことができ、UIスレッド上でメッセージキューの内容を実行することができます。 つまり、Handlerのインスタンスは、新しく作るスレッドで作成するのではなく、UIスレッドで作成しなければなりません。
Handlerは結局、別のスレッドからViewなどを操作するために使うわけですが、次のような方法があります。

Handlerのpostメソッドを使う

Handlerのpost()にRunnableを渡すと、postが実行された時、その中のrunメソッドの処理がメッセージキューにはいるようになります。 そのため、(1)の部分はUIスレッドで実行されることになります。

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity{
    TextView txt;
    Handler handler;

    //============================================= onCreate
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txt = findViewById(R.id.text1);     // 長い処理の終了を表示するためのTextView

        //------------------------- 別スレッドからUIスレッドへアクセスするためのHandler作成
        handler = new Handler();
        //------------------------- スレッドスタート (長い処理はUIスレッドで行ってはいけない)
        new MyThread().start();
    }
    //============================================= 別スレッドでやりたい長い処理とその後のUIスレッド上での処理
    class MyThread extends Thread {
        public void run() {
            try {
                sleep(10000);  //長い処理
                //------------------------- txt表示をhandlerに頼む
                handler.post(new Runnable() {
                    public void run() {     //(1)
                        txt.setText("10秒たちました");  //UIスレッドでやらなければいけない処理
                    }
                });
            } catch (InterruptedException ex) {}
        }
    }
}

HandlerのhandleMessageメソッドを使う

次の例はhandleMessageをオーバーライドしメッセージキューから取り出された時の処理を記述します。
  • Handlerが別スレッドでMessageオブジェクトを作成し(obtainMessage)
  • HandlerのhandleMessageメソッドを呼び出す(sendToTarget)
  • HandlerのhandleMessageメソッドをオーバーライドし、受信したMessageにより異なる処理を記述
handleMessageはMessageオブジェクトの中の、whatとobjを使ってメッセージが取り出された時に実行する処理を書いています。
whatとobjはobtainMessageでMessageオブジェクトを作成した時に指定しています。
こうすることにより、条件ごとにいろいろな処理に分けることを楽に行えるようにしています。

import android.os.Handler;
import android.os.Message;

public class SecondActivity extends AppCompatActivity{
    TextView txt;

    //============================================= HandlerのhandleMessageをオーバーライド
    Handler handler = new Handler(){
        //sendMessage,sendEmptyMessage,sendToTargetなどが呼ばれたときに実行される
        public void handleMessage(Message msg) {
            if(msg.what == 1) {
                txt.setText((String)msg.obj);
            }
        }
    };
    //============================================= onCreate
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        txt = findViewById(R.id.text2);
        //------------------------- スレッドスタート (長い処理はUIスレッドで行ってはいけない)
        new MyThread2().start();
    }
    //============================================= 別スレッドでやりたい長い処理
    class MyThread2 extends Thread {
        public void run() {
            try {
                sleep(11000);
                //------------------Messageを作成(obtainMessage第1引数what、第2引数obj)し
                //------------------MessageクラスのsendToTargetにより、HandlerのhandleMessage呼び出し
                handler.obtainMessage(1,"11秒たちました").sendToTarget();
            } catch (InterruptedException ex) {}
        }
    }
}

コメント

このブログの人気の投稿

ListViewの使い方

AsyncTaskについて

ListViewとSimpleAdapter使い方