API LEVEL 11以上のAsyncTaskの振る舞いについて

閲覧: 4,028 回
最初の未読メッセージにスキップ

トムキャット

未読、
2012/01/17 6:36:542012/01/17
To: 日本Androidの会
トムキャットです。
API LEVEL 11以上のAsyncTaskの振る舞いについて納得できないことがあります。どなたかこうなる理由を説明して頂けませんでしょう
か。

私はよくAsyncTaskのコンストラクターまたはonPreExecuteでプログレスダイアログを表示させています。

progressDialog = new ProgressDialog(activity);
progressDialog.setIndeterminate(true);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setMessage(getString(R.string.reading_calendar));
progressDialog.setCancelable(false);
progressDialog.show();

API LEVEL 7~10まではこれで問題ありませんでした。が、15で試すと、プログレスダイアログは表示されますが(そして進行状況を示すサー
クルがぐるぐる回りますが)、doInBackgroundが呼ばれません。まさか、API LEVEL 11から仕様変更になったスレッドの扱いによ
るものかと思い、試しに、executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)を使ったら、
doInBackgroundが呼ばれました。

あのサークルをぐるぐる回すのは勝手に行われるので、そのために内部でスレッド使っているから?かとも思いましたが、納得がいかないです。
STYLE_SPINNERのプログレスダイアログもAsyncTaskもよく使われますよね。それで問題があるのは変だと思うのです。

よろしくお願いします。

Makoto Yamazaki

未読、
2012/01/18 2:06:272012/01/18
To: android-g...@googlegroups.com
zaki です。

http://developer.android.com/reference/android/os/AsyncTask.html#execute(Params...)
を見ると、Honeycomb 以降のどこかでAsyncTask のバックグラウンド処理をバックグラウンド用の
単一スレッドで逐次実行するように変更することを計画していると書かれています。これが 4.0.3 で
実際に変更されたのではないかと予想しています。

そこで想像なのですが、今回取り上げている AsyncTask 以外にも別の AsyncTask が実行中だったり
しないでしょうか。もしそうだとすれば、先に動き始めた doInBackground が完了するまで次の
doInBackground は呼ばれません。

もしこのような状況なのであれば、 API Level 11 で追加された AsyncTask#executeOnExecutor を使って
独自の Executor を指定することで回避できるのではないかと思います。


2012/1/17 トムキャット <daily...@gmail.com>:

> --
> このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
> このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
> このグループから退会するには、android-group-j...@googlegroups.com にメールを送信してください。
> 詳細については、http://groups.google.com/group/android-group-japan?hl=ja からこのグループにアクセスしてください。
>

--
YAMAZAKI Makoto

Makoto Yamazaki

未読、
2012/01/18 2:15:202012/01/18
To: android-g...@googlegroups.com
zaki です。

URL がうまくリンクされてないので短縮したものも

http://goo.gl/VQpsV


2012/1/18 Makoto Yamazaki <makot...@gmail.com>:

--
YAMAZAKI Makoto

Makoto Yamazaki

未読、
2012/01/18 3:27:202012/01/18
To: android-g...@googlegroups.com
zaki です。

もう少ししらべてみました。

Gingerbread と ICS で AsyncTask#execute のコードを比べてみたところ、前者では
ThreadPoolExecutor で実行され
後者では SerialExecutor で実行されるように変更されていました。ただし、 ICS では、ActivityThread クラスで、
targetSdkVersion が 12 以下の時は @hide な API を使ってAsyncTask#execute が
ThreadPoolExecutor で実行する
ように変えるコードも入っていました。

つまり、単純に端末のOSのバージョンで決まるのではなく、アプリの targetSdkVersion(指定してなければ minSdkVersion)
が何になっているかも AsyncTask#execute の挙動に影響します。

Honeycomb のソースコードがないので実際に試してみないと分かりませんが、
予想では API 13(もしかしたら 11かも)以上の OS && 13 <= targetSdkVersion
の時にSerialExecutor が使われるんではないかと
思っています。

--
YAMAZAKI Makoto

トムキャット

未読、
2012/01/18 4:30:062012/01/18
To: 日本Androidの会
トムキャットです。

zakiさん、コメントありがとうございます。

・minSdkVersionを15にしています。targetSdkVersionは指定していません。
・動かしているAsyncTaskは1つです。

あるバージョンからデフォルトでSerialExecutorが使われるという仕様はいいのですが、STYLE_SPINNERのプログレスダイアログ
と一緒に使うと問題が起こる→executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)を使わないといけ
ない、というのが奇妙に思えます。

1)そういうことはない。私が何かを間違えている。
2)その振る舞いは正しい。なぜかそうなっている。

どっちでしょう?

On 1月18日, 午後5:27, Makoto Yamazaki <makoto1...@gmail.com> wrote:
> zaki です。
>
> もう少ししらべてみました。
>
> Gingerbread と ICS で AsyncTask#execute のコードを比べてみたところ、前者では
> ThreadPoolExecutor で実行され
> 後者では SerialExecutor で実行されるように変更されていました。ただし、 ICS では、ActivityThread クラスで、
> targetSdkVersion が 12 以下の時は @hide な API を使ってAsyncTask#execute が
> ThreadPoolExecutor で実行する
> ように変えるコードも入っていました。
>
> つまり、単純に端末のOSのバージョンで決まるのではなく、アプリの targetSdkVersion(指定してなければ minSdkVersion)
> が何になっているかも AsyncTask#execute の挙動に影響します。
>
> Honeycomb のソースコードがないので実際に試してみないと分かりませんが、
> 予想では API 13(もしかしたら 11かも)以上の OS && 13 <= targetSdkVersion
> の時にSerialExecutor が使われるんではないかと
> 思っています。
>
> 2012/1/18 Makoto Yamazaki <makoto1...@gmail.com>:
>
>
>
>
>
>
>
>
>
> > zaki です。
>
> > URL がうまくリンクされてないので短縮したものも
>
> >http://goo.gl/VQpsV
>
> > 2012/1/18 Makoto Yamazaki <makoto1...@gmail.com>:
> >> zaki です。
>
> >>http://developer.android.com/reference/android/os/AsyncTask.html#exec......)
> >> を見ると、Honeycomb 以降のどこかでAsyncTask のバックグラウンド処理をバックグラウンド用の
> >> 単一スレッドで逐次実行するように変更することを計画していると書かれています。これが 4.0.3 で
> >> 実際に変更されたのではないかと予想しています。
>
> >> そこで想像なのですが、今回取り上げている AsyncTask 以外にも別の AsyncTask が実行中だったり
> >> しないでしょうか。もしそうだとすれば、先に動き始めた doInBackground が完了するまで次の
> >> doInBackground は呼ばれません。
>
> >> もしこのような状況なのであれば、 API Level 11 で追加された AsyncTask#executeOnExecutor を使って
> >> 独自の Executor を指定することで回避できるのではないかと思います。
>
> >> 2012/1/17 トムキャット <dailyti...@gmail.com>:

Makoto Yamazaki

未読、
2012/01/18 5:23:542012/01/18
To: android-g...@googlegroups.com
zaki です。

API Level 15 の端末で実際に動かしてみましたが、特に問題なく doInBackground が呼ばれています。
以下のようなプロジェクトを作って試しました。
https://github.com/zaki50/Async15

このプロジェクトを実機で実行すると、画面にはプログレスが表示され、 doInBackground で書いている
ログもlogcatに出ています。

この状況で一番怪しいのは、やはり AsyncTask が複数動いていることなんではないかと思います。
クラスは一つでも複数回実行されたりしていないでしょうか。

2012/1/18 トムキャット <daily...@gmail.com>:

高見知英

未読、
2012/01/18 5:40:002012/01/18
To: android-g...@googlegroups.com

高見知英です。

> あるバージョンからデフォルトでSerialExecutorが使われるという仕様はいいのですが、STYLE_SPINNERのプログレスダイアログ
> と一緒に使うと問題が起こる→executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)を使わないといけ
> ない、というのが奇妙に思えます。

これですが、本当にProgressBarは関係していますか?
わたしもAndroid4.0(API Level14、Galaxy Nexus実機)でおなじことをしてみましたが動いています(ProgressBarは期待通り表示され、doInBackgroundは実行された)。

・ProgressBarは本当に事象に関係しているのか
・自分の見落とした部分になにか別の問題はないか

以上二点、今一度確認してみてはいかがでしょうか。

ご確認のほど、よろしくお願いいたします。

--
横浜IT勉強会 高見知英
メール:ch...@tamago.soup.jp
Web:http://Onpu.jpn.ch/
Tw: @TakamiChie ハマっち:16872
Skype:TakamiChie
2012/01/18 18:30 "トムキャット" <daily...@gmail.com>:

トムキャット

未読、
2012/01/18 8:24:032012/01/18
To: 日本Androidの会
zakiさん、高見知英さん、コメントありがとうございます。

zakiさんの作成されたコードを実行したところ正常に動作しました。(エミュレーターです。実機は持ってません。)

プログレスダイアログは無関係であることが分かりました。申し訳ございません。プログレスダイアログを表示しないようにしても、ただの
execute()だとdoInBackgroundが呼ばれません。
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)だと呼ばれます。

何かを間違えているのだと思うのですが、まだ分かっていません。他にAsyncTaskがいるはずのない状態なのに、この問題が起きています。
継続して調べます。


On 1月18日, 午後7:40, 高見知英 <c...@tamago.soup.jp> wrote:
> 高見知英です。
>
> > あるバージョンからデフォルトでSerialExecutorが使われるという仕様はいいのですが、STYLE_SPINNERのプログレスダイアログ
> > と一緒に使うと問題が起こる→executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)を使わないといけ
> > ない、というのが奇妙に思えます。
>
> これですが、本当にProgressBarは関係していますか?
> わたしもAndroid4.0(API Level14、Galaxy
> Nexus実機)でおなじことをしてみましたが動いています(ProgressBarは期待通り表示され、doInBackgroundは実行された)。
>
> ・ProgressBarは本当に事象に関係しているのか
> ・自分の見落とした部分になにか別の問題はないか
>
> 以上二点、今一度確認してみてはいかがでしょうか。
>
> ご確認のほど、よろしくお願いいたします。
>
> --
> 横浜IT勉強会 高見知英
> メール:c...@tamago.soup.jp
> Web:http://Onpu.jpn.ch/
> Tw: @TakamiChie ハマっち:16872
> Skype:TakamiChie
> 2012/01/18 18:30 "トムキャット" <dailyti...@gmail.com>:
> からこのグループにアクセスしてください。

nandai

未読、
2012/01/18 10:39:132012/01/18
To: 日本Androidの会
トムキャットさんこんばんわ。

もしかしてexecute()に渡している引数はRunnableのオブジェクトではありませんか?
というのもICSのAsyncTaskでは下記の通りexecute()が2つ定義されているため、

(A) public final AsyncTask<Params, Progress, Result> execute(Params...
params)
(B) public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}

もしそうなら(B)のメソッドがコールされるのでdoInBackground()がコールされることも
ないのではないでしょうか。

(A)ならAsynTask.mWorker.call()でdoInBackground()がコールされますよね。

トムキャット

未読、
2012/01/19 6:21:542012/01/19
To: 日本Androidの会
トムキャットです。

原因が分かりました。全く着目していなかったところでAsyncTaskが動き続けていました。エミュレーターで、カレンダーが存在しない状態で無理に
動かしたために想定していない振る舞いになっていました。

お騒がせして申し訳ございませんでした。特にzakiさんには検証用のコードをわざわざ作って頂き、非常に申し訳なく思います。

反省します。

Makoto Yamazaki

未読、
2012/01/19 10:16:332012/01/19
To: android-g...@googlegroups.com
zaki です。

解決してよかったです。
Android 3.2以降の端末で targetSdkVersion(minSdkVersion) を13以降にすると
AsyncTask#execute の呼び出しでは doInBackground が並行実行されないということが
わかったので私も収穫ありでした。

検証用コードには大して時間かかってないのでだいじょうぶです。


2012/1/19 トムキャット <daily...@gmail.com>:

全員に返信
投稿者に返信
転送
新着メール 0 件