1.背景
在內(nèi)存不足的手機(jī)上,某些非前臺(tái)頁(yè)面會(huì)因?yàn)閮?nèi)存不足而銷毀魂挂,此時(shí)再次進(jìn)入會(huì)執(zhí)行reconstruct
的邏輯,也就是 save 马昨、restore 邏輯,此時(shí)界面展示異常
用戶正在瀏覽大眾點(diǎn)評(píng)的團(tuán)購(gòu)詳情頁(yè),然后微信來了一條消息,此時(shí)打開微信,可能點(diǎn)評(píng)的團(tuán)購(gòu)詳情頁(yè)就被銷毀了
目的:不想要當(dāng)前 activity 保留狀態(tài)仓坞,銷毀后和重新進(jìn)入頁(yè)面保持一致
2.問題探索
//偽代碼如下
for (int i = 0, len = Math.min(xx.size(), TAB_MAX_COUNT); i < len; i++) {
Fragment fragment = new Fragment()
fragment.setListener(listener);
mFragments.add(fragment);
}
走一遍主流程后,發(fā)現(xiàn) listener 在回調(diào)的時(shí)候?yàn)榭?/p>
此時(shí)幸好使用了kotlin腰吟,避免了一次明顯的空指針異常无埃,線上沒有 crash
@Override
public Object instantiateItem(ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
//回調(diào)adapter的getItem方法
Fragment fragment = getItem(position);
//下面代碼省略
}
在幾個(gè)關(guān)鍵節(jié)點(diǎn)(隨緣)打斷點(diǎn)后發(fā)現(xiàn)界面上展示的 fragment 不是生成的fragment,然后 debug adapter 的 getItem 方法毛雇,發(fā)現(xiàn)根本就沒有調(diào)用 getItem 方法嫉称,因?yàn)?mFragments 中有fragment
其實(shí)吧,上述代碼里面的注釋已經(jīng)寫得很清楚了
此時(shí)便基本確定不是代碼的問題灵疮,而是 FragmentStatePagerAdapter 源碼實(shí)現(xiàn)的問題了
3. 源碼查看
//Viewpager.java
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.position = mCurItem;
if (mAdapter != null) {
ss.adapterState = mAdapter.saveState();
}
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
if (mAdapter != null) {
mAdapter.restoreState(ss.adapterState, ss.loader);
setCurrentItemInternal(ss.position, false, true);
} else {
mRestoredCurItem = ss.position;
mRestoredAdapterState = ss.adapterState;
mRestoredClassLoader = ss.loader;
}
}
由代碼可知织阅,Viewpager 在 save 時(shí)主要存儲(chǔ)了 mAdapter.saveState();
敲黑板 此時(shí)可以復(fù)習(xí)下 save 和 restore 的調(diào)用時(shí)機(jī)和注意點(diǎn),具體查看 onSaveInstanceState執(zhí)行時(shí)機(jī)
//FragmentStatePagerAdapter.java
@Override
public Parcelable saveState() {
Bundle state = null;
if (mSavedState.size() > 0) {
state = new Bundle();
Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
mSavedState.toArray(fss);
state.putParcelableArray("states", fss);
}
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
return state;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle)state;
bundle.setClassLoader(loader);
Parcelable[] fss = bundle.getParcelableArray("states");
mSavedState.clear();
mFragments.clear();
if (fss != null) {
for (int i=0; i<fss.length; i++) {
mSavedState.add((Fragment.SavedState)fss[i]);
}
}
Iterable<String> keys = bundle.keySet();
for (String key: keys) {
if (key.startsWith("f")) {
int index = Integer.parseInt(key.substring(1));
Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
while (mFragments.size() <= index) {
mFragments.add(null);
}
f.setMenuVisibility(false);
mFragments.set(index, f);
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
}
}
saveState
主要是將 fragment.mIndex
存儲(chǔ)到 bundle 中震捣,然后通過 mFragmentManager.getFragment(bundle, key);
來進(jìn)行 fragment 的恢復(fù)
如果有興趣荔棉,可以繼續(xù)看 Fragment.java-->void restoreChildFragmentState(@Nullable Bundle savedInstanceState) {
4 .解決方式
復(fù)寫 FragmentStatePagerAdapter.saveState()
public class TestAdapter extends FragmentStatePagerAdapter {
@Override
public Parcelable saveState() {//空實(shí)現(xiàn),不傳數(shù)據(jù)
return null;
}
}