例如activity放在后台一段时间,当activity被回收之后,再重新进入activity的时候,会重新调用onCreate(),并且savedInstanceState不再是null,因此可以判断出是否被回收。

onCreate中不要重复创建fragment,通过tag去获得figment即可。

从谷歌开源项目中得到的的启示,源码:

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

        if (getIntent().hasExtra(Intent.EXTRA_TITLE)) {
            setTitle(getIntent().getStringExtra(Intent.EXTRA_TITLE));
        }

        final String customTitle = getIntent().getStringExtra(Intent.EXTRA_TITLE);
        setTitle(customTitle != null ? customTitle : getTitle());

        if (savedInstanceState == null) {
            mFragment = onCreatePane();
            mFragment.setArguments(intentToFragmentArguments(getIntent()));
            getFragmentManager().beginTransaction()
                    .add(R.id.root_container, mFragment, "single_pane")
                    .commit();
        } else {
            mFragment = getFragmentManager().findFragmentByTag("single_pane");
        }
    }
源码摘自:

com.google.samples.apps.iosched.ui.SimpleSinglePaneActivity



自己精简一下就是:

public class MainActivity extends AppCompatActivity {
    private BlankFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            mFragment = new BlankFragment();
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.root_container, mFragment, "single_pane")
                    .commit();

        } else {
            mFragment = (BlankFragment) getSupportFragmentManager().findFragmentByTag("single_pane");
        }
        Log.e("MainActivity", "savedInstanceState=" + savedInstanceState + " mFragment=" + mFragment);
    }
}

打印了两个Log,一个是首次创建,另一个是回收了之后创建。

11-22 12:55:43.940 24433-24433/com.example.baidu.test E/MainActivity: savedInstanceState=null mFragment=BlankFragment{585c98a id=0x7f0b0055 single_pane}


11-22 12:57:36.576 24433-24433/com.example.baidu.test E/MainActivity: savedInstanceState=Bundle[{android:viewHierarchyState=Bundle[mParcelledData.dataSize=680], android:support:fragments=android.support.v4.app.FragmentManagerState@ea89abf}] mFragment=BlankFragment{baa458c #0 id=0x7f0b0055 single_pane}


测试的时候可以设置手机【不保留活动】,这样可以快速的模拟回收的情况,小米手机有这个功能。





Activity被回收,系统为什么会保存Fragment呢?来分析一下FragmentActivity源码:

/**
     * Save all appropriate fragment state.
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);//这里保存了Fragment数据
        }
        if (mPendingFragmentActivityResults.size() > 0) {
            outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);

            int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
            String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
            for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
                requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
                fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
            }
            outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
            outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
        }
    }
从源码中得知,当Activity调用onSaveInstanceState方法的时候,会保存当前Activity里面的所有Fragment,保存在了一个Bundle里,key就是FRAGMENTS_TAG。

其实这个Bundle会在Activity恢复的时候传给onCreate,这就是为什么onSaveInstanceState不是null了。

有人会问什么时候会调用onSaveInstanceState方法?用最简洁的一句话:当Activity不可见的时候就会执行nSaveInstanceState。例如被另一个Activity覆盖,按Home键回到桌面等等。当然你主动结束Activity不会执行。调用onSaveInstanceState保存的不只是fragment,还保存了View的状态,只保存了带id的View的状态,不带id不会保存的。


其实回收之后恢复是很麻烦的,得到了Fragment后,你会发现成员变量并没有恢复,只有View某些状态被恢复了,如果完全重现被回收那一刻的所有状态,还需要考虑在Fragment里面重写onSaveInstanceState方法,来保存一些数据,呵呵,工作量不小啊,非特殊情况下,我不建议这么做。

我一般的做法都是重新创建Activity和Fragment:

public class MainActivity extends AppCompatActivity {
    static final String FRAGMENTS_TAG = "android:support:fragments";
    private BlankFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //删除保存的Fragment,也可以重写onSaveInstanceState方法不让其保存
        if (savedInstanceState != null) {
            savedInstanceState.putParcelable(FRAGMENTS_TAG, null);
        }
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mFragment = new BlankFragment();
        getSupportFragmentManager().beginTransaction()
                .add(R.id.root_container, mFragment, "single_pane")
                .commit();
    }
}

你可以记住重要的关键信息,来恢复这个Activiy。例如当前Activity是一篇文章,你只需要在onSaveInstanceState保存文章id,等恢复的时候直接根据id从服务器拉取就好了。

如果你的Activity在启动时的Intent里面有文章id,那就简单了,在回收恢复的时候,直接这样仍然得到文章id:String id = getIntent().getStringExtra("article_id");,这样根本不用你主动保存。使用Intent的时候必须序列化的原因:只有被序列化的对象和基本数据类型才可以保存在磁盘上,这样内存被干掉,仍然可以将他们从磁盘上恢复到内存中。


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐