Android_スレッド処理

トップページ > Android > スレッド処理

スレッド処理の基本


ネットワークへのアクセスなどは、メインスレッドでは出来ないので、
別スレッドで行う。
こういう処理のときに、AsyncTaskを利用する。

また、AsyncTaskの経過処理を表示するのには、ProgressDialogが利用できる。

 public void goTask() {
   AsyncTask<Void,Integer,Void> task = new AsyncTask<Void,Integer,Void>() {
 
     int total;
     int count;
     ProgressDialog dialog;
 
     protected void createDialog() {
       dialog = new ProgressDialog( this.MainActivity );
       dialog.setTitle( "Working..." );
       dialog.setMessage( "Wait for dummy");
       dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
       dialog.setCancelable(false);
       dialog.setMax( 100 );
       dialog.setProgress(0);
       dialog.show();
     }
       
     @Override
     protected void onPreExecute() {
       super.onPreExecute();
       createDialog();
     }
 
     @Override
     protected void onPostExecute(Void params) {
       super.onPostExecute(params);
       dialog.dismiss();
     }
 
     @Override
     protected Void doInBackground(Void... params) {
       count = 0;
       total = 100;
       for (int i=0;i<100;i++) {
         count += 1;
         try {
           Thread.sleep(100);
         } catch (InterruptedException e) {
           e.printStackTrace();
         }
         publishProgress( count * 100 / total );
           
       }
       
       return null;
     }
     
     @Override
     protected void onProgressUpdate(Integer... values) {
       dialog.setProgress(values[0]);
       super.onProgressUpdate(values);
     }
   };
   task.execute(  );
 }
(このgoTaskメソッドは、何かボタンを押したら呼ばれるようにしておく)

デバイスの回転での問題点


これで動作するのだが、AsyncTaskが動作している間にデバイスを回転させてしまうと、プログレスバーが消えてしまう。
さらに、onPostExecuteのところのdialog.dismiss();のところでハングしてしまう。

この理由は、デバイスの回転にともなってアクティビティが削除/再生成されてしまうため。
アクティビティが再生成された場合でも、動作しているAsyncTaskが設定しているProgressDialogが削除された古い
アクティビティを保持して動作しているため、dismiss出来なずにエラーが発生してしまうからだ。

これに対応するには、Android 3.0以後で導入されたDialogFragmentを使う方がよいようだ。
http://blog.zaq.ne.jp/oboe2uran/article/877/
しかし、DialogFragmentはAndroid 2.3系では利用できないため、ここではProgressDialogのままで対応する方法を考える。

まず、Activityが破棄されたかどうかを知る必要がある。
このため、Activityに静的メンバとして、onCreateされた(最新の)Activityを保持することにする。
さらに、破棄された場合、新しいActivityから再度ProgressDialogを表示する必要があるが、サブスレッドからはGUIを
操作できないので、ハンドラを一つ用意しておく。

MainActivityに追加するフィールド

 public static MainActivity currentActivity;
 public Handler handler;

そして、onCreateメソッド内で、次のように定義しておく

 currentActivity = this;
 handler = new Handler();

また、goTaskで定義しているAsyncTasxk内でもMainActivityを保持しておく。
ループ処理中、もしもactivityがcurrentActivityと違っていたら、handler経由で再度、
ProgressDialogを表示する。

 public void goTask() {
   AsyncTask<Void,Integer,Void> task = new AsyncTask<Void,Integer,Void>() {
 
     MainActivity activity;
     int total;
     int count;
     ProgressDialog dialog;
 
     protected void createDialog() {
       dialog = new ProgressDialog( currentActivity );
       dialog.setTitle( "Working..." );
       dialog.setMessage( "Wait for dummy");
       dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
       dialog.setCancelable(false);
       dialog.setMax( 100 );
       dialog.setProgress(0);
       dialog.show();
     }
     
     @Override
     protected void onPreExecute() {
       super.onPreExecute();
       activity = currentActivity;
       createDialog();
     }
 
     @Override
     protected void onPostExecute(Void params) {
       super.onPostExecute(params);
       if (activity == currentActivity) {
         dialog.dismiss();
       }
     }
 
     @Override
     protected Void doInBackground(Void... params) {
       count = 0;
       total = 100;
       for (int i=0;i<100;i++) {
         count += 1;
         try {
           Thread.sleep(100);
         } catch (InterruptedException e) {
           e.printStackTrace();
         }
         if (activity != currentActivity) {
           activity = currentActivity;
           activity.handler.post(new Runnable() {
 
             @Override
             public void run() {
               createDialog();
             }
             
           });
         }
         
         publishProgress( count * 100 / total );
         
       }
       
       return null;
     }
     
     @Override
     protected void onProgressUpdate(Integer... values) {
       dialog.setProgress(values[0]);
       super.onProgressUpdate(values);
     }
   };
   task.execute(  );
 }

これで一応、画面を回転させても再度プログレスバーを表示して動作継続するようにできた。
ちょっと面倒なので、もう少し良い方法があればいいのだけど。

2013/7/27
最終更新:2013年07月26日 17:21