Activity#runOnUiThread
Activity#runOnUiThreadの実装がこちら。
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
内部的にHandler#postしていますね。
ここでちょっと気になるのが
Thread.currentThread() != mUiThreadこの比較です。runOnUiThreadを実行しているスレッドがUIスレッドかどうかチェックしています。
そしてUIスレッドである場合は
action.run();指定したRunnableをrunOnUiThreadの中で即実行しています。つまりrunOnUiThreadはRunnableを実行する為にブロッキングされるという事です。
どんな時問題になるか
runOnUiThreadをHandler#postと同じモノだと考えて利用しているとハマる可能性があります。
まぁ多分大丈夫と思いますが以下のhello2()様な実行順に依存性があるような実装をしちゃうとヤバイですね。
Handler mHandler = new Handler(); String mMessage = "hello!"; private void hello(){ mHandler.post(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), mMessage, Toast.LENGTH_SHORT).show(); } }); mMessage = "good bye!"; } private void hello2(){ runOnUiThread(new Runnable(){ @Override public void run() { Toast.makeText(getApplicationContext(), mMessage, Toast.LENGTH_SHORT).show(); } }); mMessage = "good bye!"; }
別スレッドからhello(),hello2()、UIスレッドからhello(),hello2()を実行するとToastに何が表示されるでしょうか?
呼び出しパターン | 結果 |
---|---|
別スレッドからHandler#postを呼ぶ(hello()) | "good bye!"が表示される |
UIスレッドからHandler#postを呼ぶ(hello()) | "good bye!"が表示される |
別スレッドからrunOnUiThreadを呼ぶ(hello2()) | "good bye!"が表示される |
UIスレッドからrunOnUiThreadを呼ぶ(hello2()) | "hello!"が表示される |
なんか一個動きが違うのがいるーーーーー。
はいそういう事ですね。
結論
色々省略しますが
・Handler#post, runOnUiThreadに渡すRunnableが触る変数にはHandler#post, runOnUiThread呼び出し後触らない。
・Handler#post, runOnUiThreadはなるべくメソッドの最後に呼ぶ
・Handler#post, runOnUiThreadに渡すRunnableの中では呼び出し元のメンバ変数などを触らない。
Handler#post, runOnUiThreadを呼び出すメソッド内でfinalな変数を宣言しておいてそれにアクセスしたりする様にする。
とかやってるといいんじゃないでしょかー
追記
といったリプライが。確かに。(@atsushienoさんありがとうございました!)
実際hello(), hello2()のmMessage = "good bye!";の直前にThread.sleep(1000);を追加して実行してみるとどうなるか実験してみました。
結果は以下です
呼び出しパターン | 結果 |
---|---|
別スレッドからHandler#postを呼ぶ(hello()) | "hello!"が表示される |
UIスレッドからHandler#postを呼ぶ(hello()) | "hello!"が表示される |
別スレッドからrunOnUiThreadを呼ぶ(hello2()) | "good bye!"が表示される |
UIスレッドからrunOnUiThreadを呼ぶ(hello2()) | "hello!"が表示される |
なんか一個動きが違うのがいるーーーーー。
はいそういう事ですね。
以下の様な事になります。
呼び出しパターン | 動き |
---|---|
別スレッドからHandler#postを呼ぶ(hello()) | 呼び出し後の処理とRunnableの実行どちらが先かは判らない |
UIスレッドからHandler#postを呼ぶ(hello()) | 必ず呼び出し後の処理が実行された後にRunnableが実行される |
別スレッドからrunOnUiThreadを呼ぶ(hello2()) | 呼び出し後の処理とRunnableの実行どちらが先かは判らない |
UIスレッドからrunOnUiThreadを呼ぶ(hello2()) | 必ずRunnableが実行された後に呼び出し後の処理が実行される |
結論は変わらず。
どちらにせよHandler#post, runOnUiThreadを呼び出した後にHandler#post, runOnUiThread内部で利用されうる値などの書き換えはやばいよ、という事ですね。
多分誰もこういう事はしないとは思いますが・・・。多分。
0 件のコメント:
コメントを投稿