Android AsyncTaskでProgressDialogを表示 その2
前回までに作ったProgressBarを表示するAsyncTaskですが
doInBackgroundメソッド内で発生した例外をUIスレッドに通知したいと思います。
バックグラウンドで発生した例外をUIスレッドに通知するにはHandlerクラスを使用します。
詳しい説明や使い方はコチラなどが参考になりました。
愚鈍人 HandlerとMessage - 別スレッドでのGUI操作
ようはUIスレッドでHandlerクラスのインスタンスを生成し、
このインスタンスのsendMessageメソッドでバックグラウンドからUIスレッドに通知できます。
またHandlerクラスのhandleMessageメソッドをオーバライドして、バックグラウンドから通知を受けたときの処理を書きます。
MainActivity.java
ボタンをクリックするとバックグラウンドで処理を実行します。
処理を行うクラス(ReceiveDataクラス)のコンストラクタの引数にHandlerオブジェクトを指定しています。
またHandlerクラスのhandleMessageメソッドをオーバライドして、発生した例外のメッセージを表示します。
※「This Handler class should be static or leaks might occur」と警告が出ますが、今はとりあえず無視します(^^;
package com.example.helloandroid;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
//Activityが破棄されても、オブジェクトを保持できるようにstaticで宣言する。
private static ProgressAsyncTask<Void,Boolean> mTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//プログレスダイアログを再表示する。
if (mTask != null && mTask.getIsShowProgress()){
mTask.showDialog();
}
}
@Override
protected void onPause() {
//プログレスダイアログを閉じる。
if (mTask != null && mTask.getIsShowProgress()) {
mTask.dismissDialog();
}
super.onPause();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
/**
* button1クリック処理
* @param view
* バックグラウンドで処理を行う
*/
public void button1Click(View view){
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Throwable e = (Throwable)msg.obj;
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
};
//final MyHandler handler = new MyHandler(this);
mTask = new ProgressAsyncTask<Void,Boolean>(this){
@Override
protected Boolean doInBackground(Void... params) {
ReceiveData receive = new ReceiveData(mTask,handler);
Boolean ret = receive.getMethod1();
return ret;
}};
mTask.execute();
}
}
ProgressAsyncTask.java
プログレスバーを表示するAsyncTaskクラスです。
package com.example.helloandroid;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
/**
* ProgressAsyncTask
* プログレスダイアログを表示し、バックグラウンドで処理を行う抽象クラスです。
* @param <Param>
* @param <Result>
*
*/
public abstract class ProgressAsyncTask<Param,Result> extends AsyncTask<Param,Bundle,Result>{
private Context mContext;
private ProgressDialog mProgressDialog;
private Boolean isShowProgress;
/**
* コンストラクタ
* @param context
* @param procName
*/
public ProgressAsyncTask(Context context){
mContext = context;
}
/**
* getIsShowProgress
* プログレスダイアログが表示中かどうかを返します。
* @return
*/
public Boolean getIsShowProgress() {
return isShowProgress;
}
/**
* onPreExecute
* 最初にUIスレッドで呼び出されます。
* UIに関わる処理をします。
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
showDialog();
isShowProgress = true;
}
/**
* onProgressUpdate
* doInBackground内でpublishProgressメソッドが呼ばれると、 UIスレッド上でこのメソッドが呼ばれます。
* このメソッドの引数の型はAsyncTaskの二つ目のパラメータです。
* @param values
*/
@Override
protected void onProgressUpdate(Bundle... values) {
super.onProgressUpdate(values);
Bundle bundle = values[0];
if (bundle.containsKey("Message")){
mProgressDialog.setMessage(bundle.getString("Message"));
}
if (bundle.containsKey("Max")){
mProgressDialog.setMax(bundle.getInt("Max"));
}
if (bundle.containsKey("Progress")){
mProgressDialog.setProgress(bundle.getInt("Progress"));
}
}
/**
* onPostExecute
* doInBackground が終わるとそのメソッドの戻り値をパラメータとして渡して onPostExecute が呼ばれます。
* このパラメータの型は AsyncTask を extends するときの三つめのパラメータです。
* バックグラウンド処理が終了し、メインスレッドに反映させる処理をここに書きます。
* @param result
*/
@Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
dismissDialog();
isShowProgress = false;
}
/**
* onCancelled
* cancelメソッドが呼ばれるとonCancelledメソッドが呼ばれます。
*/
@Override
protected void onCancelled() {
super.onCancelled();
dismissDialog();
isShowProgress = false;
}
/**
* showDialog
* プログレスダイアログを表示します。
*/
public void showDialog() {
mProgressDialog = new ProgressDialog(mContext);
mProgressDialog.setMessage(""); //途中でメッセージを変更するために必要
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setIndeterminate(false);
mProgressDialog.setCancelable(true);
mProgressDialog.setButton("キャンセル", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
cancel(true); // 非同期処理をキャンセルする
}
});
mProgressDialog.show();
}
/**
* dismissDialog
* ダイアログを終了します。
*/
public void dismissDialog() {
mProgressDialog.dismiss();
mProgressDialog = null;
}
/**
* setProgressMessage
* プログレスダイアログのメッセージを設定します。
* @param message
*/
public void setProgressMessage(String message){
Bundle data = new Bundle();
data.putString("Message", message);
publishProgress(data);
}
/**
* setMax
* プログレスダイアログの最大値を設定します。
* @param max
*/
public void setProgressMax(int max){
Bundle data = new Bundle();
data.putInt("Max", max);
publishProgress(data);
}
/**
* setProgress
* プログレスダイアログの進捗値を設定します。
* @param max
*/
public void setProgress(int progress){
Bundle data = new Bundle();
data.putInt("Progress", progress);
publishProgress(data);
}
}
ReceiveData.java
実際にバックグラウンドで行う処理です。
ループ変数 i が 7 になったときRuntimeExceptionをスローし、 catch句でHandlerオブジェクトのsendMessageメソッドを実行しています。
package com.example.helloandroid;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
public class ReceiveData {
private ProgressAsyncTask<?,?> mTask;
private Handler mHandler;
/**
* コンストラクタ
* @param task
* @param handler
*/
public ReceiveData(ProgressAsyncTask<?,?> task, Handler handler){
mTask = task;
mHandler = handler;
}
/**
* getMethod1
*/
public Boolean getMethod1(){
try {
mTask.setProgressMessage("Method1_Start");
mTask.setProgressMax(10);
for(int i=0; i<10; i++){
mTask.setProgress(i+1);
SystemClock.sleep(1000);
if (i == 7) {
throw new RuntimeException("エラー! i==7です。");
}
if (mTask.isCancelled()) {
return false;
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
final Message msg = new Message();
msg.obj = e;
new Thread(new Runnable(){
public void run() {
mHandler.sendMessage(msg);
}}).start();
mTask.cancel(true);
return false;
}
}
}
実行するとUIスレッドでメッセージが表示されます。
さて次は「This Handler class should be static or leaks might occur」と警告が出ないようMainActivityを修正します。
修正方法はコチラを参考に
http://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler
HandlerクラスをStaticクラスにします。
MainActivity
package com.example.helloandroid;
import java.lang.ref.WeakReference;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
//Activityが破棄されても、オブジェクトを保持できるようにstaticで宣言する。
private static ProgressAsyncTask<Void,Boolean> mTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//プログレスダイアログを再表示する。
if (mTask != null && mTask.getIsShowProgress()){
mTask.showDialog();
}
}
@Override
protected void onPause() {
//プログレスダイアログを閉じる。
if (mTask != null && mTask.getIsShowProgress()) {
mTask.dismissDialog();
}
super.onPause();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
/**
* button1クリック処理
* @param view
* バックグラウンドで処理を行う
*/
public void button1Click(View view){
final MyHandler handler = new MyHandler(this);
mTask = new ProgressAsyncTask<Void,Boolean>(this){
@Override
protected Boolean doInBackground(Void... params) {
ReceiveData receive = new ReceiveData(mTask,handler);
Boolean ret = receive.getMethod1();
return ret;
}};
mTask.execute();
}
/**
* MyHandlerクラス
* バックグラウンドで例外発生時の処理
*/
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> mActivity;
public MyHandler(MainActivity activity) {
mActivity = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mActivity.get();
if (activity == null){
return;
}
Throwable e = (Throwable)msg.obj;
Toast.makeText(mActivity.get(), e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
0 件のコメント:
コメントを投稿