temporaryなめも帳

だらだらと備忘録とか。誰かの為になることをねがって。

ToastをUIThread以外で表示する

ToastはUIスレッド以外でも表示することができます。 まずはToastのソースコードを見てみましょう。

public Toast(Context context) {
    mContext = context;
    mTN = new TN();
    mTN.mY = context.getResources().getDimensionPixelSize(
             com.android.internal.R.dimen.toast_y_offset);
    mTN.mGravity = context.getResources().getInteger(
             com.android.internal.R.integer.config_toastDefaultGravity);
}

private static class TN extends ITransientNotification.Stub {
    // ...省略

    final Handler mHandler = new Handler();

Toast.makeText()を呼び出すと、上記ソースコードコンストラクタが呼び出されます。Toast.show()を呼び出したタイミング内部で生成されたToastのインスタンスがNotificationManagerに登録され、Manager側から(Service側から)TN.show()が呼び出されます。 TN.show()が呼ばれた後は、内部変数に保持しているHandlerにて、処理を行うThreadを変え、Toast出力処理を行なっています。出力処理自体はWindowManagerServiceに依存しているため、実行中のThreadがUIThreadである必要がありません。

しかし、単純なThread上でToastを呼び出すと、「Can't create handler inside thread that has not called Looper.prepare()」が出力されてしまいます。 エラーの内容と以下サンプルの動作を見る限り、UIThread以外でもLooperの動いているThreadであればToastは表示できます。

public class MainActivity extends Activity implements View.OnClickListener {
    private Handler looperThreadHandler;
    private MyLooperThread thread;

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

        ((Button)findViewById(R.id.button)).setOnClickListener(this);
        thread = new MyLooperThread();
        thread.start();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button: {
                looperThreadHandler.sendEmptyMessage(0);
                break;
            }
        }
    }


    class MyLooperThread extends Thread{
        @Override
        public void run() {
            Looper.prepare();
            looperThreadHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    Toast.makeText(getApplicationContext(),"test",Toast.LENGTH_SHORT).show();
                }
            };
            Looper.loop();
        }
    }
}