ProgressDialogとAsyncTaskの(おそらく)正しい使い方
プログレスダイアログの使い方として、
よく、以下の様な紹介がされています。
・・・が、これには実は問題が隠されています。
結論だけ先に言ってしまえば、
「表示中に画面を回転させるとエラーが発生する可能性がある」
です。
その2も追加しました
※AsyncTaskが非staticなのは、静的に利用しない(常にActivityとワンセット)の為。
※その辺は用途に合わせて、Contextを渡して汎用的に使えるようにとかでもいいかとおもいます。
(補足)
android:configChangesを指定してやり
onConfigurationChangedをオーバーライドしてやるとインスタンスの再生成とかはなくなるので、
それで代替してもいいと思いますが、色々ライフサイクルのやり直しによって担保されている部分が
大丈夫なのか?というのはちゃんと確認してください。
Tweet
よく、以下の様な紹介がされています。
・・・が、これには実は問題が隠されています。
結論だけ先に言ってしまえば、
「表示中に画面を回転させるとエラーが発生する可能性がある」
です。
その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をオーバーライドしてやるとインスタンスの再生成とかはなくなるので、
それで代替してもいいと思いますが、色々ライフサイクルのやり直しによって担保されている部分が
大丈夫なのか?というのはちゃんと確認してください。
- 関連記事
-
- Admob SDK 更新
- 原因不明なエラー
- ProgressDialogとAsyncTaskの(おそらく)正しい使い方
- SeekBarの見た目(style)を設定
- 他のアプリと連携(暗黙のIntent)