Activity生成時に別のActivityをさらに起動する場合はHandlerを使わないと不可解な現象に遭遇します(しました)

Activityが生成された時に何らかの条件で別のActivityを開くコードを実装することもあるでしょう。
例えば、初回の起動時のみインストラクションを表示するといったような。


こういう場合にActivity.onCreate(...)()のなかでActivity.startActivity(...)を呼んでしまうと不可解なことが起こります。

悪い例


以下はactionbarsherlockでタブを使った切り替えのあるメイン画面(MainActivity)で、そのonCreate(...)SubActivityを起動しています。

public class MainActivity extends SherlockFragmentActivity implements ActionBar.TabListener {

    public static final class MyFragment extends Fragment {
        public static Fragment newInstance(Context context, int i) {
            Bundle args = new Bundle();
            args.putInt("position", i);
            Fragment f = Fragment.instantiate(context, MyFragment.class.getName(), args);
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            ...
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 別のアクティビティを起動します
        startActivity(new Intent(this, SubActivity.class));

        //Support package の Fragment を使うときの注意点 - http://y-anz-m.blogspot.jp/2013/05/support-package-fragment.html
        setContentView(new FrameLayout(this));

        // タブを作成します
        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        for (int i = 0; i < 3; ++i) {
            actionBar.addTab(actionBar.newTab()
                                      .setText(Integer.toString(i))
                                      .setTabListener(this));
        }
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
        fragmentTransaction.replace(android.R.id.content, MyFragment.newInstance(this, tab.getPosition()));
    }

    ...


このコードをAndroid2.3で実行してみると、タブのコンテンツが真っ白になります。

1. MainActivityが起動する
2. MainActivityのonCreate(...)からSubActivityが起動する
3. SubActivityを閉じる
4. MainActivityに戻ってきてタブを切り替える


悪い例よりは良い例


以下は、正しく動作するコードです。

Activity.onCreate(...)で呼んでいたActivity.startActivity(...)Handlerで実行しています。


    private static final class MyHandler extends Handler {
        private final WeakReference<Activity> mOwner;

        MyHandler(Activity owner) {
            mOwner = new WeakReference<Activity>(owner);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    Activity owner = mOwner.get();
                    if(owner != null){
                        owner.startActivity(new Intent(owner, SubActivity.class));
                    }
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    }

    private final Handler mHandler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 別のアクティビティを起動します
        mHandler.sendEmptyMessage(0);

        // //Support package の Fragment を使うときの注意点 - http://y-anz-m.blogspot.jp/2013/05/support-package-fragment.html
        setContentView(new FrameLayout(this));

        // タブを作成します
        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        for (int i = 0; i < 3; ++i) {
            actionBar.addTab(actionBar.newTab()
                                      .setText(Integer.toString(i))
                                      .setTabListener(this));
        }
    }


実行してみると、コンテンツが正しく表示されます。



以上です。