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 件のコメント:
コメントを投稿