非同期処理と UI - 基本的な枠組み
非同期で処理したい時間のかかる処理を含むプログラムの骨組みの例を示します。
なぜ Handler を使わなければならないのか? とか、 ExecutorService ってなんだ? とか、 そういった点については別のページを参考にしてください。
ここでは Android でワーカースレッドで処理を行って、 GUI にその結果を戻すような場合の典型的な骨組みを示します。
このプログラムの完成形
何を作っているか見えた方がわかりやすいと思いますので、実行例のスクリーンショットをとりました。
画面の Test ボタンをクリックすると、 "Now Processing..." (処理中です) という文字が表示されます。
が、実際には何も処理をせずに5秒間だけじーっとして、5秒後に "Done!"というメッセージに切り替わります。
この間、画面はフリーズしません。押そうと思えば、 "Now Processing..." と表示中にももう一度 Test ボタンを押すことも出来ます。
コード例
メッセージを表示する部分とボタンは、それぞれ TextView と Button ウィジェットを配置しています。全体はリニアレイアウトになってます。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/result"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
android:textSize="30sp"
/>
<Button
android:id="@+id/test_button"
android:text="Test"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
/>
</LinearLayout>
アプリケーションのコードは次のとおりです。
package com.keicode.android.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class AsyncTest1 extends Activity
implements OnClickListener {
TextView resultTextView;
Button testButton;
Handler guiThreadHandler;
ExecutorService asyncExecutorService;
Runnable starterTask;
@SuppressWarnings("unchecked")
Future taskPending;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViews();
setListeners();
guiThreadHandler = new Handler();
asyncExecutorService = Executors.newSingleThreadExecutor();
starterTask = new Runnable(){
@Override
public void run() {
try{
MyAsyncTask asyncTask =
new MyAsyncTask(AsyncTest1.this);
taskPending =
asyncExecutorService.submit(asyncTask);
}
catch(RejectedExecutionException e){
resultTextView.setText("Error");
}
}
};
}
protected void findViews(){
resultTextView = (TextView)findViewById(R.id.result);
testButton = (Button)findViewById(R.id.test_button);
}
protected void setListeners(){
testButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.test_button:
startAsyncTask();
break;
}
}
protected void startAsyncTask(){
guiThreadHandler.removeCallbacks(starterTask);
guiThreadHandler.postDelayed(starterTask, 200);
}
public void setTextAsync(final String text){
guiThreadHandler.post(new Runnable(){
@Override
public void run() {
resultTextView.setText(text);
}
});
}
}
いろいろなオブジェクトが登場していますが、ここでは説明しません。次のページを参考にしてください。
- 非同期処理と UI の更新 - GUI スレッドと Handler について書いてます。
- ExecutorService によるマルチスレッディング
- Future による処理の中断
長い処理をシミュレートするタスクは次のようになってます。ここでは GUI スレッドに処理をポストするメソッドを呼んでいます。
package com.keicode.android.test;
public class MyAsyncTask implements Runnable {
AsyncTest1 test1;
public MyAsyncTask(AsyncTest1 test1){
this.test1 = test1;
}
@Override
public void run() {
test1.setTextAsync("Now processing...");
String result = doTask();
test1.setTextAsync(result);
}
public String doTask(){
String result = "Done!";
try{
if(Thread.interrupted()){
throw new InterruptedException();
}
Thread.sleep(5 * 1000);
}
catch(InterruptedException e){
result = "Interrupted";
}
return result;
}
}