源码剖析Fragment的Pause生命周期

Android进阶之路系列:http://blog.csdn.net/column/details/16488.html



近期遇到一个问题,与Fragment的Pause生命周期有关,所以就研究了一下Fragment的Pause生命周期特点。

我们知道Fragment的生命周期是依赖Activity的,所以想探究Fragment的Pause过程需要从Activity的Pause下手。
在FragmentActivity的onPause可以看到相关代码,如下:
@Override
protected void  onPause() {
    super.onPause() ;
    mResumed false;
    if ( mHandler.hasMessages( MSG_RESUME_PENDING)) {
        mHandler.removeMessages( MSG_RESUME_PENDING) ;
        onResumeFragments() ;
    }
    mFragments.dispatchPause() ;
}

可以看到最后一行代码启动了Fragment的Pause过程,mFragments是一个FragmentControler对象,它的dispatchPause方法只有一行代码
public void  dispatchPause() {
    mHost. mFragmentManager.dispatchPause() ;
}

它调用了FragmentManager的 dispatchPause方法
public void dispatchPause() {
    moveToState(Fragment.STARTED, false);
}
也只有一行代码,调用moveToState。在FragmentManager中,这个方法最终会调用另外一个重载的 moveToState方法
void  moveToState( int newState , boolean always) {
    moveToState(newState 0 0 always) ;
}

void  moveToState( int newState , int transit , int transitStyle , boolean always) {
    if ( mHost ==  null && newState != Fragment. INITIALIZING) {
        throw new IllegalStateException( "No host") ;
    }

    if (!always &&  mCurState == newState) {
        return;
    }

    mCurState = newState ;
    if ( mActive !=  null) {
        boolean loadersRunning =  false;
        for ( int i= 0 i< mActive.size() i++) {
            Fragment f =  mActive.get(i) ;
            if  (f != null) {
                moveToState(f newState transit transitStyle , false) ;
                if (f. mLoaderManager !=  null) {
                    loadersRunning |= f. mLoaderManager.hasRunningLoaders() ;
                }
            }
        }

        if (!loadersRunning) {
            startPendingDeferredFragments() ;
        }

        if ( mNeedMenuInvalidate &&  mHost !=  null &&  mCurState == Fragment. RESUMED) {
            mHost.onSupportInvalidateOptionsMenu() ;
            mNeedMenuInvalidate false;
        }
    }
}

先判断是否有fragment,如果有则最终调用下面的函数
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive)
这个方法很复杂,将近300行,就不将所有源码都贴出来了。
重点关注这些代码
} 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();
            }
        case Fragment.STARTED:
            if (newState < Fragment.STARTED) {
                if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                f.performStop();
            }
        case Fragment.STOPPED:
            if (newState < Fragment.STOPPED) {
                if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                f.performReallyStop();
            }
        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();
                if (f.mView != null && f.mContainer != null) {
                    Animation anim = null;
                    if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
                        anim = loadAnimation(f, transit, false,
                                transitionStyle);
                    }
                    if (anim != null) {
                        final Fragment fragment = f;
                        f.mAnimatingAway = f.mView;
                        f.mStateAfterAnimating = newState;
                        final View viewToAnimate = f.mView;
                        anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
                                viewToAnimate, anim) {
                            @Override
                            public void onAnimationEnd(Animation animation) {
                                super.onAnimationEnd(animation);
                                if (fragment.mAnimatingAway != null) {
                                    fragment.mAnimatingAway = null;
                                    moveToState(fragment, fragment.mStateAfterAnimating,
                                            0, 0, false);
                                }
                            }
                        });
                        f.mView.startAnimation(anim);
                    }
                    f.mContainer.removeView(f.mView);
                }
                f.mContainer = null;
                f.mView = null;
                f.mInnerView = null;
            }
        case Fragment.CREATED:
几个state如下
static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3;          // Fully created, not started.
static final int STARTED = 4;          // Created and started, not resumed.
static final int RESUMED = 5;          // Created started and resumed.
可以看到RESUMED最大
当一个页面已经展示,那么当前状态就是RESUMED,Puase时可以在 dispathPause方法中看到newState是STARTED,那么上面代码的第一行判断就成立,进入这层逻辑中。

由于mState是RESUMED,会依次执行每一个case(因为 switch中每个case都没有break,所以会顺序执行下去 )。但是当执行到第一个case时,调用了Fragment的performPause方法,

在Fragment源码中可以看到performPause这个方法
void performPause() {
    if (mChildFragmentManager != null) {
        mChildFragmentManager.dispatchPause();
    }
    mState = STARTED;
    mCalled = false;
    onPause();
    if (!mCalled) {
        throw new SuperNotCalledException("Fragment " + this
                + " did not call through to super.onPause()");
    }
}
如果没有childFragment,那么直接调用onPause。
如果有childFragment,会先调用它的fragmentManager的dispatchPause方法,这样就进入了 childFragment的Pause过程。这个过程与上面的一致,其实就是一个递归的过程。

总结起来,调用顺序如图
源码剖析Fragment的Pause生命周期_第1张图片

以上就是Fragment的Pause整个过程。是从Activity的onPause开始的,而Fragment的onPause是最后被调用的。



回到最初,我们遇到的问题到底是什么呢?

首先注意在moveToState函数(最后调用的那个)中有两个带有下划线的代码,表示当执行Pause时fragment会destory和remove页面上的view。

这里就会存在一个问题!!!在Activity中切换 Fragment时, Fragment的Pause周期完全结束之前view就已经被移除了, 而这时Fragment还显示在屏幕上。如果activity的theme是transparent,且Fragment里有SurfaceView或者MapView这类view,在remove时就会发生透视现象(遮盖在下面的Activity的内容会显示出来)。而且为装载Fragment的容器设置背景色没有任何用处!

注意这个现象虽然发生在Pause阶段,但是由于返回桌面这个操作会瞬间完成,所以这时没有问题。主要发生在Fragment切换的时候,即getFragmentManager().beginTransaction().replace(...)

如果只是TextView这类普通的就不会存在上面的现象。经过反复测试发现,remove时(在第二个划线的代码处)如果TextView这类的view虽然remove了但是还会留存在页面上,并且会参与转场动画(如果有转场动画);而SurfaceView这类,remove的时候那块区域会立刻消失!这样就导致了一瞬间的透视现象。

进过调查,这是由于 SurfaceView的特殊性,实际上remove时SurfaceView与其他类型的View一样留存,但是它的绘制内容被全部回收了(包括SurfaceView的默认背景 - 黑色),没有内容则呈现出了一种透明现象。

解决SurfaceView透明的方法,可以为其设置一下format,如
view. surfaceview.setZOrderOnTop( true)
view. surfaceview. holder.setFormat(PixelFormat. RGBA_8888)

而MapView(百度)没有找到相关方法,但是有一个取巧的办法,在切换前,即 getFragmentManager().beginTransaction().replace(...)之前 前将MapView或其容器设为INVISIBLE,这样由于没有MapView的影响,背景色就可以正常显示了,不会出现透视现象。注意恢复时要记得重新设为VISIBLE。

另外一个彻底解决透视方法是将activity的theme设置为不透明,但是要保证不影响这个activity其他的显示效果。

至于SurfaceView为何没有内容呈现透明状态,则容器背景也无效,这个与SurfaceView的绘制有关,引用网上的一段解释:

用来描述SurfaceView的Layer或者LayerBuffer的Z轴位置是小于用来其宿主Activity窗口的Layer的Z轴位置的,但是前者会在后者的上面挖一个“洞”出来,以便它的UI可以对用户可见。实际上,SurfaceView在其宿主Activity窗口上所挖的“洞”只不过是在其宿主Activity窗口上设置了一块透明区域。

由于SurfaceView的绘制也是一个很复杂的过程,具体细节就不在这个章节的说了,等有时间我们可以在一个单独的章节再来探讨。

这里还有一个小问题,第二个划线处注意到f.mContainer,这个其实是我们熟悉的东西,大家能猜出来是什么?我会在另外一个章节来揭晓这个。

Android进阶之路系列:http://blog.csdn.net/column/details/16488.html


你可能感兴趣的:(android,Android进阶之路)