I could not clearly understand life cycle of fragments which is added to back stack .
So, I created sample program at github.
Fragmentを使おうと思ったのであるけど、Fragment自体をstackさせた時の挙動がどうにも理解しにくかった。
Activityもそうなのであるけど、stackした時のlife cycleについてのドキュメントが無いから、自分で調べないとなんとも言えないのだよなー。
特に、よく分からなかったのはFragmentを含んだActivityがメモリ不足等の理由でシステムによりdestroyされた際にFragment内で作成したViewはdestroyされるっぽいんだけど、Fragmentのインスタンス自体は削除されないし、View自体も実態は削除されていないことが非常に不思議だ。Activityに紐付いたまま。
なので、メモリ不足等でActivityが削除されて、またonCreateがコールされた際にFragmentをnewしてaddするとFragmentがどんどん増えてしまう。
1: public class MainActivity extends FragmentActivity {
2: private static final String TAG = "FragmentTest";
3:
4: @Override
5: public void onCreate(Bundle savedInstanceState) {
6: Log.v(TAG, "Activity Enter onCreate");
7: super.onCreate(savedInstanceState);
8: setContentView(R.layout.activity_main);
9:
10: if (savedInstanceState == null) {
11: FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
12: Fragment newFragment = FirstFragment.newInstance("http://www.google.co.jp");
13: ft.add(R.id.fragment_container, newFragment, "first");
14: ft.commit();
15: }
16: Button btn = (Button) findViewById(R.id.button);
17: btn.setOnClickListener(new OnClickListener() {
18: @Override
19: public void onClick(View v) {
20: FragmentManager fm = MainActivity.this.getSupportFragmentManager();
21: FragmentTransaction ft = fm.beginTransaction();
22: Fragment newFragment = SecondFragment.newInstance("http://www.goo.ne.jp");
23: ft.replace(R.id.fragment_container, newFragment, "second");
24: ft.addToBackStack(null);
25: ft.commit();
26: }
27: });
28: Log.v(TAG, "Activity Exit onCreate");
29: }
なので、上記の10行目みたいにonCreateが再生成でコールされたと判断できたら、Fragmentを作らないようにしなければいけない。
このようにしておけば、Fragmentが余計に生成されるのを避ける事が出来て納得の行くlife cycleになると思う。
もう一つ分かりにくかったのは、Fragmentをreplace等でstackさせた時にもonDestroyViewがコールされるのだ。それでbackキーで元のFragmentに戻った際に、新しくViewを作りなおさなければいけないところがなんとも釈然としなかった。stackしているだけなのだからonStopまでで良いと思うのだが。。。
しかも、ViewのInstance自体はRootのViewに紐付けられたままで削除されているわけではないので、元々あったViewのインスタンスをActivityは保持したままであるし。
1: public class FirstFragment extends Fragment {
2: private static final String TAG = "FragmentTest";
3: private WebView mWebview = null;
4:
5: @Override
6: public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
7: Log.d(TAG, "FirstFragment Enter onCreateView");
8: if (mWebview != null) return mWebview;
9: mWebview = (WebView) inflater.inflate(R.layout.first_fragment, container, false);
10: mWebview.setWebViewClient(new WebViewClient());
11: String url = "http://www.yahoo.co.jp";
12: Bundle args = getArguments();
13: if (mLastUrl != null) {
14: url = mLastUrl;
15: } else if (savedInstanceState != null && savedInstanceState.containsKey("url")) {
16: url = savedInstanceState.getString("url");
17: } else if (args != null && args.containsKey("url")) {
18: url = args.getString("url");
19: }
20: mWebview.loadUrl(url);
21: Log.d(TAG, "FirstFragment Exit onCreateView view = " + mWebview.toString());
22: return mWebview;
23: }
などのように、8行目にある感じで、すでにFragment内で保持しているViewを返すとエラーで落ちるし。
FragmentをStackさせる際にreplaceを利用するとonDestroyViewがコールされるの前提でコードを書かなければいけないようだ。どこにもその辺の作法とか無いけれども、確かにサンプルでは必ずViewを新しく生成していますしね。
でも、そうするとWebViewを使った時にとても困る。WebViewを生成してonCreateViewで渡してユーザが操作した後に新しくFragmentを重ねるとViewが破棄されてしまうので、historyが消えてしまう。
historyの仕組みを自分で実装しなければいけなくって非常に面倒くさい。
Stack
0 件のコメント:
コメントを投稿