fc2ブログ

ProgressDialogとAsyncTaskの(おそらく)正しい使い方

プログレスダイアログの使い方として、
よく、以下の様な紹介がされています。
・・・が、これには実は問題が隠されています。


結論だけ先に言ってしまえば、
「表示中に画面を回転させるとエラーが発生する可能性がある」
です。
その2も追加しました




public class AsyncDemo extends Activity {


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new DemoAsync().execute();
}



class DemoAsync extends AsyncTask<Void, Void, Void> {
private ProgressDialog mDialog;

public DemoAsync() {
this.mDialog = new ProgressDialog(AsyncDemo.this);
}
@Override
protected Void doInBackground(Void... params) {
try {
TimeUnit.SECONDS.sleep(5l);
} catch (InterruptedException e) {
}
return null;
}

@Override
protected void onCancelled() {
Log.d("", "onCancel");
if(mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
super.onCancelled();
}

@Override
protected void onPostExecute(Void result) {
Log.d("", "onPostExecute");
super.onPostExecute(result);
if(mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}

@Override
protected void onPreExecute() {
super.onPreExecute();
mDialog.setIndeterminate(true);
mDialog.setMessage("Dialog");
mDialog.show();
}
}
}

※AsyncTaskが非staticなのは、静的に利用しない(常にActivityとワンセット)の為。
※その辺は用途に合わせて、Contextを渡して汎用的に使えるようにとかでもいいかとおもいます。



ダイアログ表示中に画面をローテートしてみてください。


バージョンによってはエラーが発生すると思います。



java.lang.IllegalArgumentException: View not attached to window manager


Dialog#dismiss()


でエラーになります。


Dialog自体は、エンクロージングActivityにアタッチされていますが、


このActivityは画面切替時に破棄(だか再利用だか)されてしまいます。


この際ダイアログもまとめて処理してくれればいいのですが、


残念ながら対応してくれません。





じゃぁ、ちゃんとActivityのonPauseあたりでハンドリングしてやりましょう。


きっとキャンセル呼んであげればいいんだ・・・って事でこんな感じにしてみます。



private DemoAsync mAsync;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAsync = new DemoAsync();
mAsync.execute();
}

@Override
protected void onPause() {
if(mAsync != null && !mAsync.isCancelled()) {
mAsync.cancel(true);
}
super.onPause();
}


はい。残念ながら駄目だったりします。





じゃぁどうするの?ということでひとまずの解はこんな感じですかね。


※実際に、不要な処理になっている箇所もあります。


※ただ、このサンプルだと、onCreateでダイアログ表示しているので、切替後も表示されているように見えますけど、


これは、再度表示されているだけです。



public class AsyncDemo extends Activity {
private DemoAsync mAsync;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAsync = new DemoAsync();
mAsync.execute();
}

@Override
protected void onPause() {
if(mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
if(mAsync != null && !mAsync.isCancelled()) {
mAsync.cancel(true);
}
super.onPause();
}

private ProgressDialog mDialog;
class DemoAsync extends AsyncTask<Void, Void, Void> {

public DemoAsync() {
mDialog = new ProgressDialog(AsyncDemo.this);
}
@Override
protected Void doInBackground(Void... params) {
try {
TimeUnit.SECONDS.sleep(5l);
} catch (InterruptedException e) {
}
return null;
}

@Override
protected void onCancelled() {
Log.d("", "onCancel");
if(mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
super.onCancelled();
}

@Override
protected void onPostExecute(Void result) {
Log.d("", "onPostExecute");
super.onPostExecute(result);
if(mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
}

@Override
protected void onPreExecute() {
super.onPreExecute();
mDialog.setIndeterminate(true);
mDialog.setMessage("Dialog");
mDialog.show();
}
}
}


とりあえず対策してみましたが、そもそもローテート禁止にする


などという対応やら色々あると思いますので、


識者の方々などからの突っ込みをお待ちしています。



(補足)
android:configChangesを指定してやり
onConfigurationChangedをオーバーライドしてやるとインスタンスの再生成とかはなくなるので、
それで代替してもいいと思いますが、色々ライフサイクルのやり直しによって担保されている部分が
大丈夫なのか?というのはちゃんと確認してください。
関連記事

コメントの投稿

非公開コメント

スポンサードリンク
開発中に一息

アプリ一覧
最新記事
最新コメント
最新トラックバック
検索フォーム
月別アーカイブ
カテゴリ
RSSリンクの表示
リンク
QRコード
QR