Androidでスレッドを利用する簡単なサンプルコード
■ Runnableインターフェースのrunメソッドを作成
Eclipseのパッケージ・エクスプローラまたはナビゲーターで、MainActivityクラスを選択した状態で、「ソース」ー「メソッドのオーバーライド/実装」を選択する。
キーボードから“run”と入力すると、インクリメンタルサーチが掛かる。“run”のチェックボックスをONにしてOKボタンを押す。
作成されたrunメソッドに手を加えて完成させる
@SuppressLint("SimpleDateFormat")
@Override
public void run() {
final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
while (!flagThreadStop) {
// 500 ミリ秒待機
try { Thread.sleep(500); }
catch (InterruptedException e) {
// interrupt() がコールされた場合の処理
flagThreadStop = true;
// LogCatにデバッグ用メッセージを表示
Log.d("(thread) interrupt receive", "1");
}
// UI widgetへのアクセスは Runnable() 内で行う
handler.post(new Runnable() {
@Override
public void run() {
// 現在時刻の表示
text.setText(dateFormat.format(new Date()));
}
});
}
// (デバッグ用)動作の明確な確認のため 3秒待つ
try { Thread.sleep(3000); }
catch (InterruptedException e) { }
// LogCatにデバッグ用メッセージを表示
Log.d("(thread) thread run() exit", "1");
}
緑と赤で着色した部分が、手入力した部分。それ以外は、Eclipseの自動作成部分。なお、赤の部分がスレッドのループ処理部分で、500ミリ秒のsleepを入れて定期的に停止条件flagThreadStop
を監視しつつ、画面への時刻表示text.setText(dateFormat.format(new Date()));
を行っている。
■ スレッド内から画面へのアクセスはHandlerを経由させる
スレッド内から画面へ直接アクセスすると、例外が発生する。そのため、Handlerを用いて回避する。
public class MainActivity extends ActionBarActivity implements Runnable, OnClickListener { final int ID_TEXTVIEW = 0x1001; final int ID_STOPBUTTON = 0x1002; private Thread thread = null; private final Handler handler = new Handler(); // スレッド内から書き込むUI Widget TextView text; // スレッド内で使う、スレッド終了命令のフラグ private volatile boolean flagThreadStop = false; 〜 略 〜 @SuppressLint("SimpleDateFormat") @Override public void run() { 〜 略 〜 // UI widgetへのアクセスは Runnable() 内で行う handler.post(new Runnable() { @Override public void run() { // 現在時刻の表示 text.setText(dateFormat.format(new Date())); } }); 〜 略 〜 }
■ スレッドの終了
スレッドの終了は、UIからスレッドに終了を知らせて、内部のループを抜けるようにするコーディングが必要。
今回は、割込処理を用いて通知を行っている
@SuppressLint("SimpleDateFormat") @Override public void run() { final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); while (!flagThreadStop) { // 500 ミリ秒待機 try { Thread.sleep(500); } catch (InterruptedException e) { // interrupt() がコールされた場合の処理 flagThreadStop = true; } 〜 略 (スレッドの実際の処理部分など) 〜 } } @Override protected void onDestroy() { // スレッドが“生存”している場合は、スレッド終了処理を行う if(thread.isAlive()) { // スレッドに interrupt 割り込みを送る thread.interrupt(); // スレッドの終了を待つ try{ thread.join(); } catch(InterruptedException e) { } } // 上位クラスのDestroyをコール super.onDestroy(); }
■ サンプル画面とMainActivityのコード
package com.example.android_thread_1;
import android.support.v7.app.ActionBarActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.os.Handler;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Button;
import android.view.KeyEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.util.Log;
public class MainActivity extends ActionBarActivity implements Runnable, OnClickListener {
final int ID_TEXTVIEW = 0x1001;
final int ID_STOPBUTTON = 0x1002;
private Thread thread = null;
private final Handler handler = new Handler();
// スレッド内から書き込むUI Widget
TextView text;
// スレッド内で使う、スレッド終了命令のフラグ
private volatile boolean flagThreadStop = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
setContentView(layout);
// 終了ボタン
Button button = new Button(this);
button.setText("スレッド終了");
button.setId(ID_STOPBUTTON);
layout.addView(button);
button.setOnClickListener(this);
// 時間を表示するテキスト (スレッドより書き換える部分)
text = new TextView(this);
text.setId(ID_TEXTVIEW);
text.setTextSize((int)(text.getTextSize()*1.5));
layout.addView(text);
thread = new Thread(this);
thread.start();
}
@SuppressLint("SimpleDateFormat")
@Override
public void run() {
final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
while (!flagThreadStop) {
// 500 ミリ秒待機
try { Thread.sleep(500); }
catch (InterruptedException e) {
// interrupt() がコールされた場合の処理
flagThreadStop = true;
// LogCatにデバッグ用メッセージを表示
Log.d("(thread) interrupt receive", "1");
}
// UI widgetへのアクセスは Runnable() 内で行う
handler.post(new Runnable() {
@Override
public void run() {
// 現在時刻の表示
text.setText(dateFormat.format(new Date()));
}
});
}
// (デバッグ用)動作の明確な確認のため 3秒待つ
try { Thread.sleep(3000); }
catch (InterruptedException e) { }
// LogCatにデバッグ用メッセージを表示
Log.d("(thread) thread run() exit", "1");
}
@Override
public void onClick(View v) {
switch(v.getId()){
case ID_STOPBUTTON:
// プログラムを終了する
this.finish();
break;
}
}
// スレッド終了を行うため、onDestroyをフック
@Override
protected void onDestroy() {
// スレッドが“生存”している場合は、スレッド終了処理を行う
if(thread.isAlive())
{
// LogCatにデバッグ用メッセージを表示
Log.d("(from UI) interrupt sent", "1");
// スレッドに interrupt 割り込みを送る
thread.interrupt();
// LogCatにデバッグ用メッセージを表示
Log.d("(UI) waiting thread terminate", "1");
// スレッドの終了を待つ
try{ thread.join(); }
catch(InterruptedException e) { }
}
// 上位クラスのDestroyをコール
super.onDestroy();
}
// キーイベントをフックする (任意!)
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode != KeyEvent.KEYCODE_BACK){
// 「戻る」キー以外
return super.onKeyDown(keyCode, event);
}
else {
// 「戻る」キーを無視する(プログラムを終了させない)
Log.d("(UI) KEYCODE_BACK disabled", "1");
return false;
}
}
}
赤で着色した implements を忘れると、正常動作しない。implements と、そのリスナー・メソッドの追加は、まず、Activityのところで
… extends ActionBarActivity implements Ru…
と入力を始めると、コード補完が働き、挿入できる候補が表示される。implementsの行を完成させると、クラス部分(class MainActivity)に「エラー表示」が出てくるので、そこにカーソルを合わせると「実装されていないメソッドの実装」と表示されるので、それを選択すればメソッドが自動生成される。