ActivityでのFragmentページ重複異常
9395 ワード
なお、ここではv 4-24.0.0以下のバージョンで発生した問題を紹介し、v 4-24.0.0+以降、以下の問題を公式に修正しました.
Fragmentを使用する場合、Activityに関連付けます.システムリソースが不足している場合、私たちのアプリケーションリソースが回収されたり、プログラムにエラーが発生した後、システムがページを再ロードしたりすると、インタフェースにFragmentが重なる異常現象が発生します.
Activityには、Activityがkillされるときにコールバックする
このとき、システムはBundleタイプのデータを保存してくれます.私たちは自分のニーズに応じて、手動で再生の進捗などのデータを保存することができます.その後、ページの再起動が発生した場合、
Fragmentが重なる原因はこの保存状態メカニズムに関係している.大体の原因は、システムがページを再起動する前に、Fragmentの状態を保存してくれたが、再起動するとビューの可視状態が保存されず、Fragmentのデフォルトはshow状態だったため、Fragmentが重なる現象が発生した.
適用されるActivityの親FragmentActivityで
保存してくれたFragmentは最終的にFragmentStateとして存在します.これにより、ページが再起動されると、Activityは前回保存したFragmentの状態に応じて、前のFragmentを自動的に表示します.同時に現在表示するFragmentと重複します.
Activityで
ユーザが離れたときのFragmentインタフェースに復元するには,離れたときの可視のtagまたは下付き文字を
onAttachFragmentを書き換え、新しいFragmentを破棄されたfragmentに向けた.
参照先:
https://blog.csdn.net/whitley_gong/article/details/51987911
https://www.jianshu.com/p/d9143a92ad94
文章は個人記録として勉強するだけで、不適切な点があれば指摘してください.ありがとうございます.
情景再現
Fragmentを使用する場合、Activityに関連付けます.システムリソースが不足している場合、私たちのアプリケーションリソースが回収されたり、プログラムにエラーが発生した後、システムがページを再ロードしたりすると、インタフェースにFragmentが重なる異常現象が発生します.
原因を分析する
onSaveInstanceState()保存メカニズム
Activityには、Activityがkillされるときにコールバックする
onSaveInstanceState()
メソッドがあることを知っています(たとえば、バックグラウンドに入る、画面が回転する前、次のActivityをジャンプするなどの場合に呼び出されます).このとき、システムはBundleタイプのデータを保存してくれます.私たちは自分のニーズに応じて、手動で再生の進捗などのデータを保存することができます.その後、ページの再起動が発生した場合、
onRestoreInstanceState()
またはonCreate()
で保存したデータを取得することができます.例えば、再生の進行度を回復するなどの状態です.Fragmentが重なる原因はこの保存状態メカニズムに関係している.大体の原因は、システムがページを再起動する前に、Fragmentの状態を保存してくれたが、再起動するとビューの可視状態が保存されず、Fragmentのデフォルトはshow状態だったため、Fragmentが重なる現象が発生した.
関連ソース
FragmentActivity
適用されるActivityの親FragmentActivityで
@SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
...
/**
* Save all appropriate fragment state.
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
...
FragmentManagerImpl
Parcelable saveAllState() {
...
FragmentManagerState fms = new FragmentManagerState();
fms.mActive = active;
fms.mAdded = added;
fms.mBackStack = backStack;
...
return fms;
}
void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
...
FragmentManagerState fms = (FragmentManagerState)state;
...
}
saveAllState()
法により重要な保存コードが見られた.restoreAllState()
の方法では、FragmentManagerStateによって以前に保存されていたデータが得られる.FragmentState
final class FragmentState implements Parcelable {
final String mClassName;
final int mIndex;
final boolean mFromLayout;
final int mFragmentId;
final int mContainerId;
final String mTag;
final boolean mRetainInstance;
final boolean mDetached;
final Bundle mArguments;
// final boolean mHidden;
Bundle mSavedFragmentState;
Fragment mInstance;
...
public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
Fragment parent, FragmentManagerNonConfig childNonConfig) {
if (mInstance == null) {
final Context context = host.getContext();
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}
if (container != null) {
mInstance = container.instantiate(context, mClassName, mArguments);
} else {
mInstance = Fragment.instantiate(context, mClassName, mArguments);
}
if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(context.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
}
mInstance.setIndex(mIndex, parent);
mInstance.mFromLayout = mFromLayout;
mInstance.mRestored = true;
mInstance.mFragmentId = mFragmentId;
mInstance.mContainerId = mContainerId;
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
mInstance.mHidden = mHidden;
mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);
}
mInstance.mChildNonConfig = childNonConfig;
return mInstance;
}
...
}
保存してくれたFragmentは最終的にFragmentStateとして存在します.これにより、ページが再起動されると、Activityは前回保存したFragmentの状態に応じて、前のFragmentを自動的に表示します.同時に現在表示するFragmentと重複します.
解決策
方法一findFragmentByTag
Activityで
add()
またはreplace()
でfragmentを追加する場合は、tagをバインドします.一般的にはfragmentのクラス名をtagとして使用し、メモリ回収が発生してページが再ロードされると、findFragmentByTag()
で対応するFragmentを見つけ、hide()
で隠す必要があるfragmentを見つけます.@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “ ”
targetFragment = getSupportFragmentManager().findFragmentByTag(TargetFragment.class.getName);
hideFragment = getSupportFragmentManager().findFragmentByTag(HideFragment.class.getName);
//
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ //
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
getFragmentManager().beginTransaction()
.add(R.id.container, targetFragment, targetFragment.getClass().getName())
.add(R.id,container,hideFragment,hideFragment.getClass().getName())
.hide(hideFragment)
.commit();
}
}
ユーザが離れたときのFragmentインタフェースに復元するには,離れたときの可視のtagまたは下付き文字を
onSaveInstanceState(Bundle outState)
メソッドに保存し,onCreate(Bundle savedInstanceState)
でtag/下付き文字を取り出して復元する必要がある.メソッド2 onAttachFragment(推奨)
onAttachFragmentを書き換え、新しいFragmentを破棄されたfragmentに向けた.
@Override
public void onAttachFragment(Fragment fragment) {
if (tab1 == null && fragment instanceof Tab1Fragment)
tab1 = fragment;
if (tab2 == null && fragment instanceof Tab2Fragment)
tab2 = fragment;
if (tab3 == null && fragment instanceof Tab3Fragment)
tab3 = fragment;
if (tab4 == null && fragment instanceof Tab4Fragment)
tab4 = fragment;
}
参照先:
https://blog.csdn.net/whitley_gong/article/details/51987911
https://www.jianshu.com/p/d9143a92ad94
文章は個人記録として勉強するだけで、不適切な点があれば指摘してください.ありがとうございます.