Androidプログラマへの道 〜 Moonlight 明日香 〜 - スレッドからUIを操作する
他のスレッドからUIを操作する場合には, ハンドラ(Handler)クラスを利用する.
Handlerにpostしたメッセージをハンドルしないのであれば, Activity#runOnUiThreadを使用する方がスッキリする.

誤ったUI操作

AndroidアプリのUIはシングル・スレッドモデルであり, 他のスレッドからUIの操作を行うと例外が発生する.
  btn = (Button)findViewById(R.id.button_id);
  btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      // TODO Auto-generated method stub
      new Thread(new Runnable() {
        @Override
        public void run() {
          // TODO Auto-generated method stub
          btn.setText("ファイル読み込み中.....");
          loadFile(); // 時間のかかる処理
          btn.setText("ファイル読み込み");
        }
      }).start();
    }
  });

実行する, 以下のような例外が発生する.

正しいUI操作(1)

Handlerクラスを使って, UIスレッドの持つキューにジョブを登録することで, UIスレッドでUI操作を行う.
    • ウィジェットの属するスレッドで, Handlerのインスタンスを生成する.
    • Handler.postメソッドで, ウィジェットにアクセスするコードを記述したRunnableクラスを指定する.
package com.moonlight_aska.android.thread01;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class Thread01 extends Activity {
  private Button btn;
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    btn = (Button)findViewById(R.id.button_id);
    btn.setOnClickListener(new View.OnClickListener() {
      // ハンドラを生成
      Handler handler = new Handler();
      @Override
      public void onClick(View v) {
        // TODO Auto-generated method stub
        new Thread(new Runnable() {
          @Override
          public void run() {
            // TODO Auto-generated method stub
            handler.post(new Runnable() {
              @Override
              public void run() {
                // TODO Auto-generated method stub
                btn.setText("ファイル読み込み中.....");
              }
            });
            loadFile(); // 時間のかかる処理
            handler.post(new Runnable() {
              @Override
              public void run() {
                // TODO Auto-generated method stub
                btn.setText("ファイル読み込み");
              }
            });
          }
        }).start();
      }
    });
  }

  private void loadFile() {
    // ファイル読み込み処理の代わりに, sleepを実行
    try {
      Thread.sleep(10000);
    } catch (InterruptedException ie) {
      Log.v("loadFile", "error");
    }
  }
}

正しいUI操作(2)

Activity#runOnUiThreadメソッドを使用することで, UIスレッドでUI操作を行う.
    • Activity#runOnUiThreadメソッドで, ウィジェットにアクセスするコードを記述したRunnableクラスを指定する.
      (省略)
       :
    btn.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        // TODO Auto-generated method stub
        new Thread(new Runnable() {
          @Override
          public void run() {
            // TODO Auto-generated method stub
            runOnUiThread(new Runnable() {
              @Override
              public void run() {
                // TODO Auto-generated method stub
                btn.setText("ファイル読み込み中.....");
              }
            });
            loadFile(); // 時間のかかる処理
            runOnUiThread(new Runnable() {
              @Override
              public void run() {
                // TODO Auto-generated method stub
                btn.setText("ファイル読み込み");
              }
            });
          }
        }).start();
      }
    });