AndroidのタブアクティビティでSupportMapFragmentを利用する

タブ・アクティビティはSectionsPagerAdapterを利用したタブによるページ移動を指しています。
移動する各ページを個別のFragmentにし、そのうちの1のFragmentでSupportMapFragment(GoogleMap)を利用したのですが、SupportMapFragment#getMapAsyncを呼び出すタイミングとページ移動時にエラーが発生する事象にハマりました。
上記を回避するための方法を記載します。（これで合っているのかなーという気がしますが．．．）

# まずはタブ・アクティビティでページごとに個別のfragmentを表示する

プロジェクト作成時にタブ・アクティビティを選択すると、MainActivityはAppCompatActivityを継承して作成されます。
Activityのinner classとして定義されるSectionsPagerAdapter#getItemで表示する画面のfragmentを生成しますが、このうちの１つでSupportMapFragmentを利用することにしました。
※UserMapFragmentとします。

```
public class MainActivity extends AppCompatActivity 
・
・
・
中略
・
・
    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        @Override
        public Fragment getItem(int position) {
            // getItem is called to instantiate the fragment for the given page.
            // Return a TestResultFragment (defined as a static inner class below).

            Fragment fragment = null;

            // ここで選択されたTabの番号に合わせてFragmentを生成
            switch (position) {
                case 0:
                    fragment = UserMapFragment.newInstance(); // Mapを利用するFragment
                    break;
                case 1:
                    fragment = YyyyFragment.newInstance();
                    break;
                default:
                    fragment = ZzzzzFragment.newInstance();
                    break;
            }
            return fragment;
        }
```

# fragmentのonStartでSupportMapFragment#getMapAsyncを実行する。

UserMapFragmentのonStartでgetMapAsyncします。
SupportMapFragment#getMapAsyncするにはActivityが必要になるので、getActivityで取得して渡します。（これでいいのか．．．その１）

```
public class UserMapFragment extends Fragment
    @Override
    public void onStart() {

        // Activityの取得とMapの生成
        MainActivity activity = (MainActivity) this.getActivity();

        if (activity.mMap == null) {
            // SupportMapFragmentを取得
            SupportMapFragment mapFragment = (SupportMapFragment) this.getChildFragmentManager().findFragmentById(R.id.fragment_map);
            mapFragment.getMapAsync(activity);
        }
        super.onStart();
    }
```

そしてgetMapAsyncのcallbackとListenerはActivity側に実装します。
OnMapReadyCallbackはFragmantにimplements出来ない為です。
（これでいいのか．．．その２）

```
// implements OnMapReadyCallback, GoogleMap.OnCameraIdleListenerを追加
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, GoogleMap.OnCameraIdleListener {

    // Mapの定義
    public GoogleMap mMap;

    /**
     * SupportMapFragment#getMapAsyncのcallback
     *
     * @param googleMap
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
      // Mapの生成
    }

    @Override
    public void onCameraIdle() {
      // Mapを移動したときの処理を実装
    }
```

# ページ移動した際にExceptionが発生するのを防ぐ

これでタブ移動したページにGoogleMapが表示されるのですが、何回かページを移動しているとGoogleMapのあるFragmentのonCreateViewでExceptionが発生します。

厳密なエラーの発生理由は調べていないのですが、どうもページ移動に伴いSupportMapFragment#getMapAsyncが2回以上実行されるとExceptionが発生するようです。

getMapAsyncを複数回実行させないことで対応したいのですが、タブを利用したページ移動は、表示されていない隣のページを同時に生成し、隣のページ以外を破棄してるようです。
SupportMapFragmentを持つFragmentがページ移動により破棄され、再度キャッシュされるときにエラーが発生するようです。

これはページの移動時にSectionsPagerAdapter#getItemが2回実行されるので確認できます。

Tab position:0 ← ここを表示（Fragmentを生成）すると、position:1 もFragmentを生成する
Tab position:1
Tab position:2 ← ここを表示（Fragmentを生成）すると、position:3 のFragmentを生成し、position:0のFragmentを破棄する。
Tab position:3

Fragmentが破棄されないようにすることで回避しました。（これでいいのか．．．その３）

```

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

        // ----------- Tabの初期化

        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());


        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);


        // setOffscreenPageLimitに3(Tabの総数)を設定
        mViewPager.setOffscreenPageLimit(3);

```

setOffscreenPageLimitでキャッシュするページ数が決められるので、3=全てのページをキャッシュすることでページの破棄が発生しなくなりました。