AsyncTaskの終了待ち

7,109 views
Skip to first unread message

Mar Hiro

unread,
Feb 24, 2014, 5:18:03 AM2/24/14
to android-g...@googlegroups.com
データーベースからデータを取得する際 AsyncTaskでバックグラウンド処理をしています。
タイムアップ時間監視と結果待ちをループ処理で待ちたいのですが、ループをまわすとAsyncTaskのonPostExecuteが呼び出されません。
何か良い方法はないでしょうか?
 
コード:
cUser user = null;
// main
cUser get_user(string id)
{
    int tim=0;
    sqlAsyncTask at=new cAsyncTask();
    at.execute("Select * From tbl_User Where UserID = '"+id+"'");
    while(true)
    {
        if(user != null)break;
        java.lang.Thread.sleep(1);
        tim++;
        if(tim > 1000) return null;
    }
    return user;
}
// 終了時呼び出される処理
public sqlAsyncTaskResult(cUser user)
{
    this.user=user;
}

Makoto Yamazaki

unread,
Feb 24, 2014, 6:10:29 AM2/24/14
to android-g...@googlegroups.com
zaki です。

待つという考え方は無理なのであきらめてください。
onPostExecute は、イベントループから呼ばれるので execute(...) を呼んだメソッドが
return しないとよばれません。

基本的には待つという考え方は忘れましょう。タイムアウトでなにか処理をしたい時は
Handler クラスの sendMessageDelayed などで、アイムアウト時に発行したいメッセージを
投げておいて、タイムアウト前に処理が完了したらremoveMessagesでタイムアウト処理を
キャンセルするような作りが Android 的には自然です。

待つのではなく、イベントが起きたら、もしくは自分からイベントを起こして何かをすると
考えるのがポイントです。

android.os.Handler や android.os.Looper をのコードを読んで Android のUIの根幹になっている
イベントループを理解すると、良いAndroidアプリが作れるようになると思います。



--
このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
このグループから退会し、メールの受信を停止するには、android-group-j...@googlegroups.com にメールを送信します。
このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
http://groups.google.com/group/android-group-japan からこのグループにアクセスしてください。
その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。



--
YAMAZAKI Makoto

Jaken Jarvis

unread,
Feb 24, 2014, 3:43:03 PM2/24/14
to android-g...@googlegroups.com
Jakenです。

一年ぶりぐらいの投稿になります。
変な時間に目が冴えてしまったので、眠たい事言ってたらゴメンナサイ。

※先に念を押しますが、zakiさんの言う設計が正しいです。
イベントドリブンな設計にしたほうが、よっぽど分かりやすくなります。
また、内容が難しいと感じるようなら、以下には浅い理解で手を出さない方が「吉」です。
zakiさんはご存知だけど「あえて答えなかった」ぐらいの内容じゃないかと思います。

Mar Hiroさんがやりたい事は、条件付きではありますが、他の方法で実現可能です。
それは、Thread/Lock/Conditionを使った方法です。CountDownLatchなんてのもありますね。

ただし、メインスレッドで長時間待機(5秒かな)すると、ANRの可能性も出てくるので、
出来るだけ待機処理は別スレッドで行わなければなりません。
テストプログラムで、メインスレッドで待機しても軽い処理なら動作することは確認してます。
ですが、この使い方は正直ちょっと怖いです・・・。

出来れば、待機処理を含む「順次処理を行わなければならない部分」を
ごっそり全部「別スレッド」にして、終わったらHandlerでpostするような
仕組みにした方がよいでしょう。(あれ、やっぱりイベントドリブンにw)

キャンセル処理が必要なほど、時間がかかる処理なら、
やっぱりzakiさんが紹介された方法になると思います。
SQL文見た感じだと、UserID1件の取得っぽいし、大丈夫かな?

まあ、非推奨だけど「こんな方法もあるよ」という事で。
それでは、頑張ってください(^^

Mar Hiro

unread,
Feb 24, 2014, 6:59:20 PM2/24/14
to android-g...@googlegroups.com
zakiさん Jakenさん ありがとうございます。
今回 初めてandroidアプリを作成するに際して データーベース処理を関数化してメインタスクのみのプログラムを書けばよいように考えてましたが 難しそうですね。
ご意見を参考にしてもう少し考えて見ます。 

kenji matsuoka

unread,
Feb 24, 2014, 10:04:24 PM2/24/14
to android-g...@googlegroups.com
スレッドの概念を理解されていないような気がします。
同じスレッドで動くということは、処理の順番が決まっているということです。
whileが動いている間に別の処理(sqlAsyncTaskResult)が同じスレッドで動くことはあり得ません。
onClickもonPauseも決して動きません。
言い換えるとユーザーが何か操作してもアプリは一切反応しません。
そのためレスポンスよく動くためにUIスレッドは最小の動作を最小の時間で返す必要があります。

SQLの処理は時間がかかるので、UIスレッドを”待たせない”ために別スレッドで実行するわけです。
もし、単に終了を待ちたいなら(SQLの速度が高速と担保されている場合とか)同一スレッドに掛けば良い話です。

別スレッドにする場合、UIスレッドと別スレッドのやりとりは以下のようになります。
1.UIスレッドから別スレッドに処理を発行
2.別スレッドが時間のかかる処理を行う。(その間UIスレッドは他のUI処理を行うことが出来る)
3.別スレッドはがIスレッドに処理完了を通知。
4.UIスレッドで処理結果を反映
という流れになります。UIスレッド側は処理の開始と処理の終了が別々になっていることに注意して下さい。
これを別に「出来る」ことで別スレッド実行中でもUIスレッドをシステムへ返すことが出来ます。
AsyncTaskはこの流れを簡単に実装するためのお助けクラスです。
AsyncTaskを使うことで別スレッドの処理やスレッド間通信をかなり簡略して書くことが出来ます。
しかし、AsyncTaskを使おうと使うまいと、スレッドの動いているタイミング自体は変わらないことに注意して下さい。


2014年2月24日月曜日 19時18分03秒 UTC+9 Mar Hiro:

kenji matsuoka

unread,
Feb 24, 2014, 10:24:42 PM2/24/14
to android-g...@googlegroups.com
データベース処理を関数化したいのであれば、
void onPostExecute(User user)メソッドを持つインターフェイス OnPostListenerを作って
get_userメソッドはreuqestDb(string id, OnPostListener listener)メソッドにしてやればいいんじゃないかなーと
requestDb内でAsyncTaskを作ってそのonPostExecuteでlistener.onPostExecute(bar)
を呼んでやれば呼び出し側は引数に匿名クラスを使って
foo.requestDb(userId, new  UserManager.OnPostListener(){
@Override void onPostExecute(User value){
//  ユーザーが取得された時の処理
});
みたいな感じで作ってあげると良いんじゃないかと思います。
ちょうどAndroidのonClickListenerのようにつかえて便利じゃないでしょうか

2014年2月25日火曜日 8時59分20秒 UTC+9 Mar Hiro:

kenji matsuoka

unread,
Feb 24, 2014, 10:37:48 PM2/24/14
to android-g...@googlegroups.com
此処から先は余談ですが、
Userクラスの親クラスをDBに格納されているテーブルを表すDbTableクラスとかにして
User部分をジェネリクスにしてやると他のテーブルも使いまわせてなかなかライブラリー風になります。

2014年2月25日火曜日 12時24分42秒 UTC+9 kenji matsuoka:

Mar Hiro

unread,
Feb 25, 2014, 12:56:00 AM2/25/14
to android-g...@googlegroups.com
松岡様
いつもご指導ありがとうございます。
 
とりあえず
・AsyncTaskを継承したクラスにSQL分を渡す。
 このクラスのパブリックメンバーとして
   ResultSet rs              // 取得データ
      int            updateCnt  // 更新件数
      Boolean    endFlag     // 処理終了フラグ
 を準備する。
・呼び出した後Loopで処理終了フラグが立つのを待ち、データを取得
  ResulSetと親StatementをCloseする
ようなコードで なんとか動くようになりました。
 
教えていただいた方法を使って改良していきたいと思います。
(最後の余談の部分は早速組み込みたいと思います)
 
 
 

 

kenji matsuoka

unread,
Feb 25, 2014, 1:13:11 AM2/25/14
to android-g...@googlegroups.com
くりかえしですけど、
その方法だとUIスレッドが止まってしまうのでAsyncTaskにした意味がありません。
>Loopで処理終了フラグが立つのを待ち
このロジックを捨てて下さい。

上記の方法だととりあえず動くでしょうが、無駄にマルチスレッド処理を行っているぶんリスキーかつ低速なだけです。
どうしても元スレッドを待たせたいのであればAsyncTaskを使わずにUIスレッドで処理を行って下さい。
UIスレッド上でのDB処理は推奨されていませんが、AsyncTaskを待つよりは安全かつ高速です。

2014年2月25日火曜日 14時56分00秒 UTC+9 Mar Hiro:

Mar Hiro

unread,
Feb 25, 2014, 4:14:05 AM2/25/14
to android-g...@googlegroups.com
松岡様
 
アドバイスありがとうございます。
イベントドリブンで動かすよう 頑張ります。
 
Reply all
Reply to author
Forward
0 new messages