メインスレッド以外でUIを変更する方法
Androidアプリにおいて、メイン(UI)スレッド以外のスレッドで、UIを変更しようとすると、実行時に下記の例外が発生する。
これは「UI部品の操作はUIスレッドから行わなければならない」というAndroidの制約のためである。
今回は、メイン(UI)スレッド以外から、UI操作を行う方法を紹介する。
<発生する例外>
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6566) at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:911) at android.view.View.requestLayout(View.java:18814) at android.view.View.requestLayout(View.java:18814) at android.view.View.requestLayout(View.java:18814) ...
<例外が発生するソースコード>
MainActivity.java
package biz.accele.samplethreadui; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { final Button btn1; final TextView txt1; super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn1 = (Button)findViewById(R.id.btn1); txt1 = (TextView)findViewById(R.id.txt1); btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 別スレッドを実行 new Thread(new Runnable() { @Override public void run() { // 別スレッドでUIを変更しようとしている txt1.setText("本日は晴天なり"); } }).start(); } }); } }
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:orientation="vertical"> <Button android:id="@+id/btn1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Go"/> <TextView android:id="@+id/txt1" android:text="@string/hello_world" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
<例外を回避して、別スレッドからUIを変更する方法>
具体的な方法としては、Handlerを使用して、別スレッドからメイン(UI)スレッドに処理を依頼することになる。
MainActivity.java
package biz.accele.samplethreadui; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { final Button btn1; final TextView txt1; super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn1 = (Button)findViewById(R.id.btn1); txt1 = (TextView)findViewById(R.id.txt1); // メイン(UI)スレッドでHandlerのインスタンスを生成する final Handler handler = new Handler(); btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 別スレッドを実行 new Thread(new Runnable() { @Override public void run() { // Handlerを使用してメイン(UI)スレッドに処理を依頼する handler.post(new Runnable() { @Override public void run() { txt1.setText("本日は晴天なり"); } }); } }).start(); } }); } }
activity_main.xml
変更なし
今回の例の様にUI上のテキストを変更するために別スレッドにすることは無いと思うが、時間のかかる処理を別スレッドで実行し、その結果をUI上に反映したいことは、よくあるケースである。
その際は今回紹介した方法を試して頂きたい。