文章大纲
引言
Fragment中文翻译为碎片,在实际的开发中我们常把它看成是Activity的一个模块,更多的时候作用当成一个View,常常忽略了它是具备和Activity息息相关的生命周期管理,从而没有真正的掌握和了解Fragment,导致在听说一个Activity+N个Fragement的架构(比如知乎App)的App时觉得似乎意义不大或在碰到Glide 使用Fragemnt进行生命周期管理时无法理解,希望这篇文章能给一些新的启迪。
一、Fragment概述
源码分析基于androidx.fragment.app.Fragment
1、生命周期
以上是Fragment的标准流程图,在不同情况下当Activity 进入Destroyed状态时,onDestroyView、onDestroy、onDetach并不会每次都会顺序执行。
方法 | 功能说明 |
---|---|
onAttach | 在Fragment与Activity关联之后被调用。虽然初始化Fragment参数可以通过getArguments()获得,但当Fragment关联到Activity之后,就无法再调用setArguments(),所以除了在最开始时,其它时间都无法向初始化参数添加内容 |
onCreate | Fragment初次创建时调用。但此刻Activity还没有创建完成(此时无法获取Activity的一些资源),因为我们的Fragment也是Activity创建的一部分。 |
onCreateView | 主要用于创建Fragment自身的UI,只做一件事:那就是把xml映射为ViewGroup并返回,若是无界面的可返回Null,此时findViewById可能是找不到的 |
onViewCreated | Fragemnt的View创建完毕之后,通常是在onViewCreated方法中去findViewById的 |
onActivityCreated | 在Activity的OnCreate()结束后,会调用此方法。即此时Fragment和Activity均已创建完毕。所以我们可以在这个方法里使用Activity的所有资源 |
onStart | 当Fragment对用户就是可见时,但用户还未开始与Fragment交互。因为是与Activity的OnStart()绑定的。写在Activity的OnStart()中处理的逻辑,换成用Fragment来实现时,依然可以放在OnStart()中来处理。 |
onResume | 当Fragment对用户可见并且正在运行时调用。即Fragment与用户交互之前的最后一个回调。与onStart类似,也是与Activity的OnResume是相互绑定的,它依赖于包含它的Activity的Activity.onResume。当OnResume()运行完毕之后,就可以正式与用户交互了 |
onPause | 与Activity的OnPause()绑定,意义也一样。当用户离开Fragment时第一个调用这个方法(但并不总意味着Fragment将被销毁)。按照经验,当用户结束会话之前,我们可以在这提交一些变化 |
onStop | 与Activity绑定,已停止的Fragment可以直接返回到OnStart()回调,然后调用OnResume()。 |
onDestroyView | 当Fragment将被结束或者保存,那么下一个回调将是onDestoryView(),将会删除在onCreateView创建的视图。下次这个Fragment若要显示,将会重新创建新视图。这会在onStop之后和onDestroy之前调用。经测试这个方法的调用同onCreateView是否返回非null视图无关。它会在的在这个视图状态被保存之后以及它被它的父Activity回收之前调用 |
onDestory | 当Fragment不再使用时被调用。但即使调用了onDestroy()阶段,仍可以从其依附的父Activity中找到,因为它还没有Detach。 |
onDetach | 当Fragment和Activity分离的时候调用,Fragment就不再与Activity相绑定,它也不再拥有视图层次结构,它的所有资源都将被释放 |
3、生命周期的管理概述
在Fragment内部中是通过五个常量定义不同的状态,默认状态设置为INITIALIZING。
static final int INITIALIZING = 0; // Not yet created.
static final int CREATED = 1; // Created.
static final int ACTIVITY_CREATED = 2; // Fully created, not started.
static final int STARTED = 3; // Created and started, not resumed.
static final int RESUMED = 4; // Created started and resumed.
当Fragment的状态不是INITIALIZING时,通过FragmentManagerImpl来调用对应的周期方法(比如androidx.fragment.app.FragmentManagerImpl#dispatchCreate就是触发onCreate的回调),然后传入当前状态并通过FragmentManagerImpl#dispatchStateChange进行统一改变,进入到androidx.fragment.app.FragmentManagerImpl#moveToState(int newState, boolean always),再通过androidx.fragment.app.FragmentManagerImpl#moveFragmentToExpectedState方法进行状态切换,最终进入到Fragment中核心的周期管理方法androidx.fragment.app.FragmentManagerImpl#moveToState(androidx.fragment.app.Fragment, int, int, int, boolean),
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
// Fragments that are not currently added will sit in the onCreate() state.
if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
newState = Fragment.CREATED;
}
if (f.mRemoving && newState > f.mState) {
if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) {
// Allow the fragment to be created so that it can be saved later.
newState = Fragment.CREATED;
} else {
// While removing a fragment, we can't change it to a higher state.
newState = f.mState;
}
}
// Defer start if requested; don't allow it to move to STARTED or higher
// if it's not already started.
if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.ACTIVITY_CREATED) {
newState = Fragment.ACTIVITY_CREATED;
}
// Don't allow the Fragment to go above its max lifecycle state
// Ensure that Fragments are capped at CREATED instead of ACTIVITY_CREATED.
if (f.mMaxState == Lifecycle.State.CREATED) {
newState = Math.min(newState, Fragment.CREATED);
} else {
newState = Math.min(newState, f.mMaxState.ordinal());
}
if (f.mState <= newState) {
// For fragments that are created from a layout, when restoring from
// state we don't want to allow them to be created until they are
// being reloaded from the layout.
if (f.mFromLayout && !f.mInLayout) {
return;
}
if (f.getAnimatingAway() != null || f.getAnimator() != null) {
// The fragment is currently being animated... but! Now we
// want to move our state back up. Give up on waiting for the
// animation, move to whatever the final state should be once
// the animation is done, and then we can proceed from there.
f.setAnimatingAway(null);
f.setAnimator(null);
moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
}
switch (f.mState) {
case Fragment.INITIALIZING:
if (newState > Fragment.INITIALIZING) {
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
if (f.mSavedFragmentState != null) {
f.mSavedFragmentState.setClassLoader(mHost.getContext()
.getClassLoader());
f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG);
Fragment target = getFragment(f.mSavedFragmentState,
FragmentManagerImpl.TARGET_STATE_TAG);
f.mTargetWho = target != null ? target.mWho : null;
if (f.mTargetWho != null) {
f.mTargetRequestCode = f.mSavedFragmentState.getInt(
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
}
if (f.mSavedUserVisibleHint != null) {
f.mUserVisibleHint = f.mSavedUserVisibleHint;
f.mSavedUserVisibleHint = null;
} else {
f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
}
if (!f.mUserVisibleHint) {
f.mDeferStart = true;
if (newState > Fragment.ACTIVITY_CREATED) {
newState = Fragment.ACTIVITY_CREATED;
}
}
}
//f 为Fragment ,mHost 是在FragemntActivity 中实现的一个回调类
f.mHost = mHost;
f.mParentFragment = mParent;
f.mFragmentManager = mParent != null
? mParent.mChildFragmentManager : mHost.mFragmentManager;
// If we have a target fragment, push it along to at least CREATED
// so that this one can rely on it as an initialized dependency.
if (f.mTarget != null) {
if (mActive.get(f.mTarget.mWho) != f.mTarget) {
throw new IllegalStateException("Fragment " + f
+ " declared target fragment " + f.mTarget
+ " that does not belong to this FragmentManager!");
}
if (f.mTarget.mState < Fragment.CREATED) {
moveToState(f.mTarget, Fragment.CREATED, 0, 0, true);
}
f.mTargetWho = f.mTarget.mWho;
f.mTarget = null;
}
if (f.mTargetWho != null) {
Fragment target = mActive.get(f.mTargetWho);
if (target == null) {
throw new IllegalStateException("Fragment " + f
+ " declared target fragment " + f.mTargetWho
+ " that does not belong to this FragmentManager!");
}
if (target.mState < Fragment.CREATED) {
moveToState(target, Fragment.CREATED, 0, 0, true);
}
}
dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
f.performAttach();
if (f.mParentFragment == null) {
mHost.onAttachFragment(f);
} else {
f.mParentFragment.onAttachFragment(f);
}
dispatchOnFragmentAttached(f, mHost.getContext(), false);
if (!f.mIsCreated) {
dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
f.performCreate(f.mSavedFragmentState);
dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
} else {
f.restoreChildFragmentState(f.mSavedFragmentState);
f.mState = Fragment.CREATED;
}
}
// fall through
case Fragment.CREATED:
// We want to unconditionally run this anytime we do a moveToState that
// moves the Fragment above INITIALIZING, including cases such as when
// we move from CREATED => CREATED as part of the case fall through above.
if (newState > Fragment.INITIALIZING) {
ensureInflatedFragmentView(f);
}
if (newState > Fragment.CREATED) {
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
if (f.mContainerId == View.NO_ID) {
throwException(new IllegalArgumentException(
"Cannot create fragment "
+ f
+ " for a container view with no id"));
}
container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
String resName;
try {
resName = f.getResources().getResourceName(f.mContainerId);
} catch (Resources.NotFoundException e) {
resName = "unknown";
}
throwException(new IllegalArgumentException(
"No view found for id 0x"
+ Integer.toHexString(f.mContainerId) + " ("
+ resName
+ ") for fragment " + f));
}
}
f.mContainer = container;
f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
f.mInnerView = f.mView;
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
container.addView(f.mView);
}
if (f.mHidden) {
f.mView.setVisibility(View.GONE);
}
f.onViewCreated(f.mView, f.mSavedFragmentState);
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
false);
// Only animate the view if it is visible. This is done after
// dispatchOnFragmentViewCreated in case visibility is changed
f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
&& f.mContainer != null;
} else {
f.mInnerView = null;
}
}
f.performActivityCreated(f.mSavedFragmentState);
dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
// fall through
case Fragment.ACTIVITY_CREATED:
if (newState > Fragment.ACTIVITY_CREATED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
f.performStart();
dispatchOnFragmentStarted(f, false);
}
// fall through
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
f.performResume();
dispatchOnFragmentResumed(f, false);
f.mSavedFragmentState = null;
f.mSavedViewState = null;
}
}
} else if (f.mState > newState) {
switch (f.mState) {
case Fragment.RESUMED:
if (newState < Fragment.RESUMED) {
if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
f.performPause();
dispatchOnFragmentPaused(f, false);
}
// fall through
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
f.performStop();
dispatchOnFragmentStopped(f, false);
}
// fall through
case Fragment.ACTIVITY_CREATED:
if (newState < Fragment.ACTIVITY_CREATED) {
if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
if (f.mView != null) {
// Need to save the current view state if not
// done already.
if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
saveFragmentViewState(f);
}
}
f.performDestroyView();
dispatchOnFragmentViewDestroyed(f, false);
if (f.mView != null && f.mContainer != null) {
// Stop any current animations:
f.mContainer.endViewTransition(f.mView);
f.mView.clearAnimation();
AnimationOrAnimator anim = null;
// If parent is being removed, no need to handle child animations.
if (f.getParentFragment() == null || !f.getParentFragment().mRemoving) {
if (mCurState > Fragment.INITIALIZING && !mDestroyed
&& f.mView.getVisibility() == View.VISIBLE
&& f.mPostponedAlpha >= 0) {
anim = loadAnimation(f, transit, false,
transitionStyle);
}
f.mPostponedAlpha = 0;
if (anim != null) {
animateRemoveFragment(f, anim, newState);
}
f.mContainer.removeView(f.mView);
}
}
f.mContainer = null;
f.mView = null;
// Set here to ensure that Observers are called after
// the Fragment's view is set to null
f.mViewLifecycleOwner = null;
f.mViewLifecycleOwnerLiveData.setValue(null);
f.mInnerView = null;
f.mInLayout = false;
}
// fall through
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (mDestroyed) {
// The fragment's containing activity is
// being destroyed, but this fragment is
// currently animating away. Stop the
// animation right now -- it is not needed,
// and we can't wait any more on destroying
// the fragment.
if (f.getAnimatingAway() != null) {
View v = f.getAnimatingAway();
f.setAnimatingAway(null);
v.clearAnimation();
} else if (f.getAnimator() != null) {
Animator animator = f.getAnimator();
f.setAnimator(null);
animator.cancel();
}
}
if (f.getAnimatingAway() != null || f.getAnimator() != null) {
// We are waiting for the fragment's view to finish
// animating away. Just make a note of the state
// the fragment now should move to once the animation
// is done.
f.setStateAfterAnimating(newState);
newState = Fragment.CREATED;
} else {
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
boolean beingRemoved = f.mRemoving && !f.isInBackStack();
if (beingRemoved || mNonConfig.shouldDestroy(f)) {
boolean shouldClear;
if (mHost instanceof ViewModelStoreOwner) {
shouldClear = mNonConfig.isCleared();
} else if (mHost.getContext() instanceof Activity) {
Activity activity = (Activity) mHost.getContext();
shouldClear = !activity.isChangingConfigurations();
} else {
shouldClear = true;
}
if (beingRemoved || shouldClear) {
mNonConfig.clearNonConfigState(f);
}
f.performDestroy();
dispatchOnFragmentDestroyed(f, false);
} else {
f.mState = Fragment.INITIALIZING;
}
f.performDetach();
dispatchOnFragmentDetached(f, false);
if (!keepActive) {
if (beingRemoved || mNonConfig.shouldDestroy(f)) {
makeInactive(f);
} else {
f.mHost = null;
f.mParentFragment = null;
f.mFragmentManager = null;
if (f.mTargetWho != null) {
Fragment target = mActive.get(f.mTargetWho);
if (target != null && target.getRetainInstance()) {
// Only keep references to other retained Fragments
// to avoid developers accessing Fragments that
// are never coming back
f.mTarget = target;
}
}
}
}
}
}
}
}
if (f.mState != newState) {
Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
+ "expected state " + newState + " found " + f.mState);
f.mState = newState;
}
}
并且在把每一次的操作(add、replace、remove等)封装为一个指令存到androidx.fragment.app.FragmentTransaction.Op中来维护状态栈BackStackRecord,
2、使用步骤
- getSupportFragmentManager()获取FragmentManager管理对象
- 使用FragmentManager实例调用beginTransaction方法开启一个FragmentTransaction 事务
- 通过事务实例调用add方法向FrameLayout帧布局容器添加Fragment对象实例(此时并未真正执行完添加)
public FragmentTransaction add(@NonNull Fragment fragment, @Nullable String tag) {
//只是添加到FragmentTransaction #ArrayList<Op> mOps里
doAddOp(0, fragment, tag, OP_ADD);
return this;
}
- 通过事务实例调用commit方法提交FragemntTransaction 事务
本质上是执行androidx.fragment.app.BackStackRecord#commit的方法,把对应的操作存入队列,
androidx.fragment.app.BackStackRecord#commitInternal
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", pw);
pw.close();
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
再在主线程中执行androidx.fragment.app.FragmentManagerImpl#generateOpsForPendingActions方法(即执行androidx.fragment.app.FragmentManagerImpl.OpGenerator#generateOps接口方法)维护状态栈,最后进入**androidx.fragment.app.FragmentManagerImpl#moveToState(androidx.fragment.app.Fragment, int, int, int, boolean)**执行对应的生命周期方法。
以上是动态使用Fragement的一般步骤也是主流方式。
三、Fragment的应用
1、当Fragment嵌套Fragment时,内部Fragment接收不到onActivityResult()方法的回调
-
首先在嵌套子fragment直接通过**getParentFragment().startActivityForResult(intent, requestCode)**去开启新Activity
-
在最外层的fangment的onActivityResult(int requestCode, int resultCode, Intent data)去获取对用子fragment的应用去执行对应的逻辑处理方法
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data{
super.onActivityResult(requestCode, resultCode, data);
onActivityResult(requestCode, resultCode, data);
ChildFragment cf=mFragments.get(index);
//方法一 调用子Fragment的onActivityResult,然后子Fragment去处理自己的onActivityResult逻辑
cf.onActivityResult(requestCode, resultCode, data);
//方法2 直接在子fragment中提供对应的业务处理的public方法,一步到位
cf.doSomething(data);
}
N层嵌套也是一样的思路。
2、Can not perform this action after onSaveInstanceState
在Fragment中有时候(比如Activity中的onSaveInstance方法执行完毕之后,再次启动这个Fragment时)再次启动这个Fragment时候可能会报这个错误,原因就是因为使用commit方法提交事务,默认的allowStateLoss 是false就会进行状态检查时失败:
private void checkStateLoss() {
if (isStateSaved()) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
}
解决方案有两种:
-
该事务使用commitAllowingStateLoss方法替代commit方法,但是有可能导致某次提交无效(宿主Activity)对于popBackStack方法没有对应的popBackStackAllowingStateLoss方法,所以可以在下次可见时再次提交事务
-
利用onActivityForResult方法/onNewIntent就可以做到事务的完整性,不会丢失事务。
3、Fragment的异常销毁、状态保持和恢复机制
3.1、Fragment的异常销毁
所谓Fragment异常销毁指不是因为用户主动退出(比如按BACK键后Activity被主动销毁)Activity导致其关联的Fragment被销毁,可能发生在以下的情况中:
- 按Home 键返回Launcher时
- 按菜单键回到系统后台并启动了其他应用时
- 按电源键时
- 屏幕方向切换时
Activity的异常销毁也会导致Fragment的异常销毁,当然可能还有其他特殊情况也会导致。
3.2、异常销毁的Fragment恢复机制
当被异常销毁Fragment尝试恢复时(onSaveInstanceState方法中接到的Bundle对象不为null),会自动触发Activity和Fragment的onSaveInstanceState方法来恢复原来的状态(有一个前提条件是对应的View必须设置了Id),对于自定View的恢复还需要开发者自行去重写onSaveInstanceState去保存自定义属性的值和onRestoreInstanceState去还原。但是如果处理不好可能导致Fragment重叠异常。
因为通常FragmentActivity 会自动保持了Fragment的状态(FragmentState),重启后恢复时,View的可见状态并没有保存,但是Fragment默认的是show状态,在使用show、hide时都是通过add方式加载Fragment的,当add配合hide使Fragment的View变为GONE时(并没有被保持),页面重启时,add的Fragment会走全部的生命周期方法,创建View;而replace的非栈顶Fragment是不会走的,只有Back时才会触发,因此在使用replace加载Fragment时,页面重启后,Fragment的View还诶创建,而在使用add加载时视图是存在的并且重叠在一起。
4、利用无界面的Fragment保持Activity需要持久化的数据
当在Fragment的onCreate方法里执行 setRetainInstance(true)时,就可以让这个Fragment的实例被持久化存储。比如在屏幕旋转是,Activity被异常销毁后,如果不设置setRetainInstance(true)则Fragment也会被销毁,但是设置了setRetainInstance(true)之后这个Fragment实例就被保存起来了,Activity在重建时就可以获取到这个Fragment实例,自然可以得到里面的数据,就可以间接实现Activity数据的持久化,比如对Bitmap、AsyncTask、ProgressDialog等等的持久化就可以快速还原。
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). If set, the fragment lifecycle will be slightly different when an activity is recreated:
/**
* @author : Crazy.Mo
*/
public class RetainDataFragment extends Fragment {
/**
* 使用不带界面的Fragment为Activity保持的大数据
*/
private Bitmap bigData;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//TODO 设置为true,可以在异常时(比如屏幕旋转是)不被销毁,而一直保持其引用,在Activity 重建时还能获取这个引用,自然里面的变量也没有被释放
setRetainInstance(true);
}
public void setBigData(Bitmap data){
this.bigData=data;
}
public Bitmap getBigData(){
return this.bigData;
}
public static RetainDataFragment newInstance(String flag) {
RetainDataFragment myFragment = new RetainDataFragment();
Bundle args = new Bundle();
args.putString("flag", flag);
myFragment.setArguments(args);
return myFragment;
}
}
四、Fragment的一些工程代码
首先是BaseFragment,考虑到可能很多Fragment在周期方法执行时需要做一些共同的任务,我定了一个接受委托的角色,同遍历给View设置了OnClick事件。
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.crazymo.fragmentpro.FragmentDelegater;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
/**
* TODO 子类Fragment
* @author : Crazy.Mo
*/
public abstract class BaseFragment extends Fragment implements View.OnClickListener {
private FragmentDelegater mDelegater;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if(mDelegater!=null){
mDelegater.onAttach(context);
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(mDelegater!=null){
mDelegater.onCreate(savedInstanceState);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if(mDelegater!=null){
mDelegater.onCreateView(inflater, container, savedInstanceState);
}
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if(mDelegater!=null){
mDelegater.onViewCreated(view, savedInstanceState);
}
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(mDelegater!=null){
mDelegater.onActivityCreated(savedInstanceState);
}
}
@Override
public void onStart() {
super.onStart();
if(mDelegater!=null){
mDelegater.onStart();
}
}
@Override
public void onResume() {
super.onResume();
if(mDelegater!=null){
mDelegater.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if(mDelegater!=null){
mDelegater.onPause();
}
}
@Override
public void onStop() {
super.onStop();
if(mDelegater!=null){
mDelegater.onStop();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
if(mDelegater!=null){
mDelegater.onDestroyView();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if(mDelegater!=null){
mDelegater.onDestroy();
}
}
@Override
public void onDetach() {
super.onDetach();
if(mDelegater!=null){
mDelegater.onDetach();
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
}
public void setDelegater(FragmentDelegater delegater){
this.mDelegater=delegater;
}
protected void setOnClickListener(View view) {
if (view != null) {
view.setOnClickListener(this);
}
}
/**
*
* @param views 不定参数传递
*/
protected void setOnClickListener(View... views) {
if (views != null && views.length > 0) {
for (View view : views) {
view.setOnClickListener(this);
}
}
}
}
我这里为了解耦采用了一种“委托”的思想,定义了一个委托类(此处我是以打印各周期的执行日志,为了节省篇幅我把其他周期的委托调用省略了)
/**
* 接收其他Fragment的委托,统一负责帮助其他Fragment打印他们的生命周期的日志
* @author : Crazy.Mo
*/
public class FragmentDelegater extends Fragment {
private Fragment mFragment;
private IDelegate<Object> mDelegate;
public FragmentDelegater(Fragment fragment,IDelegate<Object> delegate){
super();
this.mFragment=fragment;
this.mDelegate=delegate;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
trackLife("onCreateView");
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
trackLife("onViewCreated");
}
...
private void trackLife(final String methodName){
if(mFragment!=null) {
mDelegate.delegate(methodName);
}
}
}
为了进一步解耦, 还对“委托”要做具体事进行了抽象,定义了一个接口
public interface IDelegate<T> {
void delegate(T... params);
}
/**
* @author : Crazy.Mo
*/
public class DelegateImpl<T> implements IDelegate {
@Override
public void delegate(Object... param) {
if(param[0]!=null && param[1]!=null) {
Log.e("crazymo", param[0].getClass().getSimpleName() + "->" + param[1]);
}
}
}
小结
单纯从性能来说Fragment的切换肯定是要比Activity之间的跳转性能要高得多的,因为Activity的切换是需要跨进程调用AMS,而Fragment的切换仅仅是内部的状态切换,不过Fragment的使用起来有很多逻辑需要进行处理和优化,我想这也是单Activity架构的劣势所在,希望这篇文章能给你们一些更多的思路,比如説在持久化时,考虑下Fragment。