質問: new Button(this); layout.addView(button); button.setText("ボタン"); final Handler handler=new Handler();{ new Thread(new Runnable() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { try{ Thread.sleep(5000); } catch(InterruptedException e){ } tv.setText("別スレッドよりGUIにアクセス"); } }); } }).start(); } } } ``` 上のプログラムなんですが、これを実行すると5秒後にボタンと文字「別スレッドよりGUIにアクセス」が表示されました。 私は実行すると 「こんにちは」とボタンが表示されて5秒後に「別スレッドよりGUIにアクセス」が表示されると思っていたのですが、なぜこのような動作になるのでしょうか? また 「こんにちは」とボタンが表示されて5秒後に「別スレッドよりGUIにアクセス」が表示される このようにするにはどこを改善すればいいのでしょうか? よろしくお願いします。
回答: ボタンと文字の描画が5秒後になってしまうのは、 描画処理にたどり着く前にThread.sleep(5000)が実行されてしまっているからだと思われます。 処理を順に見ていくと > > layout.addView(tv); > > layout.addView(button); > > > でViewを追加していますが、 この段階では、まだ描画処理等は行われておらず、 onCreate()が終わった後のどこかのタイミングでまとめて描画されたと思います。 詳しくは覚えてないので、興味があったら調べてみてください。 > > final Handler handler=new Handler(); > > > で、Handlerを生成していますが、 Handlerの引数無しコンストラクタを使用して生成した場合、 その処理を呼んだThread(のLooper)に対するHandlerが生成されます。 ActivityのonCreate()は、UIスレッドから呼ばれることになるので、 このHandlerはUIスレッドに対してのHandlerということになります。 > > new Thread(...).start(); > > > で、新しいThreadを生成した後、即座に実装してあるrun()が実行されます、 つまり > > handler.post(...); > > > が実行されることになるのですが、 このときのhandlerは先ほど生成したUIスレッドに対するHandlerです。 また、post()メソッドを使用して処理を登録しているため、処理遅延無しでの実行となり、 UIスレッドで処理が実行可能になり次第、登録されたran()が実行されます。 最終的に > > try{ > > Thread.sleep(5000); > > }catch(InterruptedException e){ > > } > > tv.setText("別スレッドよりGUIにアクセス"); > > > の処理が、スレッドの生成と実行からほとんど遅延無く実行され、 また、実行されるのはもちろんUIスレッドになるので、 UIスレッドでの処理を約5秒間とめた後、文字を更新するということになります。 UIスレッドでは、先ほど追加したViewの描画等の処理が 全てsleep()でとめられてしまっている状態になっており、 sleep()が終わった後、溜まっていた処理が一気に処理され、 同時に処理されているという具合になったのだと思います。 既に回答されている方がいますが、 > > Thread.sleep(5000); > > > の処理を消し、 代わりに > > handler.post(...); > > > を > > handler.postDelayed(..., 5000) > > > に変更することで、想定通りの動作になると思います。 適当に書いたので補足等あればお願いします。