2013年11月10日日曜日

AsyncTask の onPostExecute が呼ばれない件について

Android でネットワークに接続して情報を取得したりするときは
UIスレッド(メインスレッド)とは別のスレッドで処理を行う必要があります。

メインスレッドでネットワーク接続しようとすると
例外吐いてアプリが落ちやがります

※どうでもいいけど Android ってビルドでは何も言われないのに
 実行すると落ちるみたいなパターンけっこう多いような。。。


で、非同期でのネットワーク処理には AsyncTask クラスを使用するのが
最もポピュラーなやり方かと思います。

具体的な処理はGoogle先生にでもお尋ねすればいくらでも出てくるので
ざっくり省略しますが、だいたい↓こんな感じの処理となります。

public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  Button button = (Button)findViewById(R.id.button1);
  //ボタンを押したら非同期処理実行
  button.setOnClickListener(new OnClickListener(){
   @Override
   public void onClick(View arg0) {
    //非同期処理を実行させる
    new MyAsyncTask().execute();
   }
  });
 }

 //非同期処理を行うためのクラス
 private class MyAsyncTask extends AsyncTask<Void, Void, Void>{

  @Override
  protected Void doInBackground(Void... params) {
   //別スレッドで実行される非同期処理
   return null;
  }
  
  @Override
  protected void onPostExecute(Void result){
   //メインスレッドで実行される処理
   //(非同期処理の結果をUIに反映する等)
  }
 }
}



で本題。
タイトルにある通り、特定の条件において
非同期処理が終わった後に onPostExecute が呼ばれない
とかいう状況が発生します。

参考)onPostExecute not being called in AsyncTask

要は AsyncTask クラスのスタティックイニシャライザがメインスレッド以外で
実行された場合、結果的に onPostExecute がそのスレッド(非メインスレッド)に
紐付けられるようです。
※当然といえば当然ですが onPreExecute も同様。

という書き方をすると
「そもそも非メインスレッドから AsyncTask なんか呼ばねーよ m9(^Д^)プギャー」
とか思われるかもしれませんが、

↑のリンクにもある通り、
C2DM(プッシュ通知機能)なんかではわりとポピュラーに発生する、
てかサンプルコードがそれを実際にやってやがるみたいです

てか私自身もGCM(C2DMの後継)で見事にハマったワケで。

※GCMIntentService の onRegistered で受け取った regId を Web サービスに
 登録しようとして AsyncTask 使ったら見事にこの状態になったっぽい。
 その AsyncTask 自体はUIいじってるワケでもないのに。。。


回避策としてはアクティビティの onCreate メソッドとか、
・確実にUIスレッドで実行される箇所
・なるべく早い段階
で、例えば以下のようにして AsyncTask クラスに触れてやればいいみたいです。

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  //とりあえず一度 AsyncTask に触れてやる
  try{
   Class.forName("android.os.AsyncTask");
  }catch(ClassNotFoundException e){}

  //以下略
 }



…それにしても Android って標準クラスのバグがまだまだ多いみたいで。
1つのアプリ作るのに既に4つくらい標準クラスのバグに遭遇してるんですが
何すかこの大当たりっぷり。。。



一応↑の StackOverFlow の解説もざっと折りたたみで載せときますか。