【Android】安全にonActivityResultを実行する


アクティビティから他のアクティビティを呼び出して、その結果を貰い何かを実行する場合がある。定石としてはonActivityResultメソッドをオーバーライドして、その中で処理を実行するというものだが、この方法だと稀にわけの分からん例外が発生する。

📄BaseActivity.java
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=null} to activity {your_class}: java.lang.NullPointerException

この例外を発生させずに安全にコードを実行するサンプルを書いてみてる。

例外の原因

いろいろ調べてみた結果、次のような記事を発見した。

“Failure Delivering Result ” – onActivityForResult

この回答によると、

At the time that onActivityResult() is called, the activity/fragment’s state may not yet have been restored, and therefore any transactions that happen during this time will be lost as a result.

らしい。要するにonActivityResultが呼ばれたときにアクティビティ/フラグメントが完全にリストアされているかどうか保証されていないということ。

解決策としては復帰後に呼ばれるonPostResumeメソッドでやりたい処理を実行すればよいとのこと。これは順序的にはonActivityResult => onPostResume => onResume にとなっており、onPostResumeメソッドは、アクティビティ/フラグメントがリストア済で呼び出されるようだ。

解決コード

というわけでStackoverflowの回答を発展させて、次のようなコードを書いた。

public abstract class BaseActivity extends AppCompatActivity {

    private class ActivityResult {
        private final int requestCode;
        private final int resultCode;
        private final Intent data;

        private ActivityResult(int requestCode, int resultCode, Intent data) {
            this.requestCode = requestCode;
            this.resultCode = resultCode;
            this.data = data;
        }
    }

    private ActivityResult activityResult;

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        activityResult = new ActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        if (activityResult != null) {
            onPostResumeWithActivityResult(activityResult.requestCode, activityResult.resultCode, activityResult.data);
            activityResult = null;
        }
    }

    protected void onPostResumeWithActivityResult(int requestCode, int resultCode, Intent data) {
        // override if necessary
    }
}

大して難しいことはしていなくて、単にonActivityResultメソッドで結果を保持しておいて、onPostResumeメソッドが呼び出されたときにonPostResumeWithActivityResultメソッドを呼び出してあげる。

各アクティビティクラスは次のような感じになる。

📄MainActivity.java
public class MainActivity extends BaseActivity {

...

    @Override
    protected void onPostResumeWithActivityResult(int requestCode, int resultCode, Intent data) {
        super.onPostResumeWithActivityResult(requestCode, resultCode, data);
        // ここで煮るなり焼くなりする
    }

...

}

各アクティビティのonPostResumeWithActivityResultメソッドには、従来onAcitivityResultメソッドに書いていた処理を書くことになるわけだ。

とりあえずこれで上手くいっている。なんか問題あったら教えてください。

関連する記事


コメントする

メールアドレスが公開されることはありません。

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください