OnPause 時に setResult で設定した値が onActivityResult で読み出せない


なんかえらく嵌ったのでメモ。
#2011.05.26 説明内の誤記を一部修正(startActivityForResult → onActivityResult)
#2011.05.26 frameworkのソースを読んでわかったことと、「参考」へリンクを追記

嵌ったときの状況詳細

アプリの設定画面用として PreferenceActivityのサブクラス A を作り、アプリ自体の Activityであるクラス B から startActivityForResult をコールして、設定画面のクラス A を起動する。
その後、戻るボタン押下時にコールされるクラス Aの OnPause内で、Intentをnewし、putExtraでクラス Bに伝えたい値を設定、最後にsetResultの引数へ戻り値 RESULT_OKと先ほどnewしたIntentを指定してコールした。
しかし、クラス B の onActivityResult がコールされた時に getIntExtraで値を取得しようとしたところ、onActivityResult に渡される引数のIntentがnullになっており、そのためクラス A の OnPause内で設定したはずの Intent内の値も取得できなかった。

わかったこと

OnPause内での setResultは、onActivityResult に渡る引数にまったく反映されない模様。デバッガで追っても、クラス AのOnPauseがコールされた後に、クラス Bの onActivityResult が呼ばれるので、順序的にはあってるとおもうのだが、何故か反映されない。
setResultを呼ぶタイミングとして、OnPause は遅すぎるのかもしれない。というよりは、そもそも OnPause で setResult 使用としてること自体が間違っているように思われる。


(以下、2011.05.26 追記)

少し frameworkのソースを読んでみた。
・Activity.setResult時に、第2引数の Intentをメンバ変数 mResultDataに保存。
 ちなみに引数1つ版の setResultを呼ぶと、Intent型の mResultData は nullにクリアされてしまうので注意(戻り値だけセットされる)
・戻るボタンが押された時、
 onBackPressed() → finish()
 とコールされ、finish()内で ActivityManagerNative.getDefault().finishActivity という関数を呼び出している。この関数の中で、終了時のデータとして Intent等を登録している模様。
 おそらく、OnPauseがコールされるのはこの後なので、OnPause時点で setResult等しても反映されないのだと思われる。

対処法

まず、クラス Aの OnCreate時に、Intentをnewしメンバ変数へ保存後、OnCreate内で setResultをコールし、ダミーの戻り値とIntentを設定してしまう。
その後、OnPauseではなく、onPreferenceChange 等の設定が変更されたタイミングで、メンバ変数に保存してある Intentに対し putExtraで値を設定しておく。
こうすることで、onActivityResult での getIntExtraによる値の読み出しが可能となる。

参考

インテントによるアプリケーションとアクティビティの呼出し
http://ascii.jp/elem/000/000/542/542800/


フレームワークのソース
http://android.git.kernel.org/?p=platform/frameworks/base.git;a=summary
※上記ページの「tags」配下より、欲しいバージョンタグ右側のリンク「commit」をクリック、その後開いたページにあるリンク「snapshot」をクリックすると、tar.gzのソースアーカイブが落とせる。
※上記の落とし方は古い情報で、現在は repo を使って落とす方法に変わっているので注意。