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(); } } }