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 の解説もざっと折りたたみで載せときますか。