onAttachedToWindow和onDetachedFromWindow调用时机源码解析

来源:互联网 发布:中国省份城市数据库 编辑:程序博客网 时间:2024/06/07 02:46

先上测试代码:

MyView.java

import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.widget.TextView;public class MyView extends TextView {    public MyView(Context context) {        super(context);    }    public MyView(Context context, AttributeSet attrs) {        super(context, attrs);        Log.e("test","view constructor");    }    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        Log.e("test", "onAttachedToWindow");    }    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        Log.e("test", "onDetachedFromWindow");    }}

MainActivity.java

import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.e("test", "before setContextView");        setContentView(R.layout.activity_main);        Log.e("test", "after setContextView");    }    @Override    protected void onResume() {        super.onResume();        Log.e("test", "onResume");    }    @Override    protected void onDestroy() {        super.onDestroy();        Log.e("test", "onDestroy");    }}
运行后输出的Log如下:

运行Log
点击返回键退出后,输出的Log如下:

退出后输出的Log

根据Log的onAttachedToWindow和onDetachedFromWindow的输出情况一目了然。

下面通过源码分析下,他两的调用时机到底在哪。

首先看下onAttachedToWindow的调用时机,在Android源码中onResume调用前会先调用了ActivityThread中的handleResumeActivity,下面是相应的代码:

ActivityThread.java

final void handleResumeActivity(IBinder token,            boolean clearHide, boolean isForward, boolean reallyResume) {        // If we are getting ready to gc after going to the background, well        // we are back active so skip it.        unscheduleGcIdler();        mSomeActivitiesChanged = true;        // TODO Push resumeArgs into the activity for consideration        ActivityClientRecord r = performResumeActivity(token, clearHide);        if (r != null) {            final Activity a = r.activity;            if (localLOGV) Slog.v(                TAG, "Resume " + r + " started activity: " +                a.mStartedActivity + ", hideForNow: " + r.hideForNow                + ", finished: " + a.mFinished);            final int forwardBit = isForward ?                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;            // If the window hasn't yet been added to the window manager,            // and this guy didn't finish itself or start another activity,            // then go ahead and add the window.            boolean willBeVisible = !a.mStartedActivity;            if (!willBeVisible) {                try {                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(                            a.getActivityToken());                } catch (RemoteException e) {                }            }            if (r.window == null && !a.mFinished && willBeVisible) {                r.window = r.activity.getWindow();                View decor = r.window.getDecorView();                decor.setVisibility(View.INVISIBLE);                ViewManager wm = a.getWindowManager();                WindowManager.LayoutParams l = r.window.getAttributes();                a.mDecor = decor;                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;                l.softInputMode |= forwardBit;                if (a.mVisibleFromClient) {                    a.mWindowAdded = true;                    wm.addView(decor, l);//这里调用了ViewManager中的addView方法。                }            // If the window has already been added, but during resume            // we started another activity, then don't yet make the            // window visible.            } else if (!willBeVisible) {                if (localLOGV) Slog.v(                    TAG, "Launch " + r + " mStartedActivity set");                r.hideForNow = true;            }            // Get rid of anything left hanging around.            cleanUpPendingRemoveWindows(r);            // The window is now visible if it has been added, we are not            // simply finishing, and we are not starting another activity.            if (!r.activity.mFinished && willBeVisible                    && r.activity.mDecor != null && !r.hideForNow) {                if (r.newConfig != null) {                    r.tmpConfig.setTo(r.newConfig);                    if (r.overrideConfig != null) {                        r.tmpConfig.updateFrom(r.overrideConfig);                    }                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "                            + r.activityInfo.name + " with newConfig " + r.tmpConfig);                    performConfigurationChanged(r.activity, r.tmpConfig);                    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));                    r.newConfig = null;                }                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="                        + isForward);                WindowManager.LayoutParams l = r.window.getAttributes();                if ((l.softInputMode                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)                        != forwardBit) {                    l.softInputMode = (l.softInputMode                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))                            | forwardBit;                    if (r.activity.mVisibleFromClient) {                        ViewManager wm = a.getWindowManager();                        View decor = r.window.getDecorView();                        wm.updateViewLayout(decor, l);                    }                }                r.activity.mVisibleFromServer = true;                mNumVisibleActivities++;                if (r.activity.mVisibleFromClient) {                    r.activity.makeVisible();                }            }            ....    }
看代码中的wm.addView(devor,l);通过该方法将View添加到Window当中(在当前Window也就是Activity,不过Window也可以是Dialog或Toast),而wm是ViewManager类型的,查看对应代码是:

package android.view;/** Interface to let you add and remove child views to an Activity. To get an instance  * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.  */public interface ViewManager{    /**     * Assign the passed LayoutParams to the passed View and add the view to the window.     * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming     * errors, such as adding a second view to a window without removing the first view.     * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a     * secondary {@link Display} and the specified display can't be found     * (see {@link android.app.Presentation}).     * @param view The view to be added to this window.     * @param params The LayoutParams to assign to view.     */    public void addView(View view, ViewGroup.LayoutParams params);    public void updateViewLayout(View view, ViewGroup.LayoutParams params);    public void removeView(View view);}
该类是一个接口,在他下面还有一个WindowManager继承于ViewManager,而真正的实现代码在WindowManagerImpl类中,代码如下:

WindowManagerImpl.java

 /* * @see WindowManager * @see WindowManagerGlobal * @hide */public final class WindowManagerImpl implements WindowManager {    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();    private final Display mDisplay;    private final Window mParentWindow;    private IBinder mDefaultToken;    public WindowManagerImpl(Display display) {        this(display, null);    }    private WindowManagerImpl(Display display, Window parentWindow) {        mDisplay = display;        mParentWindow = parentWindow;    }    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {        return new WindowManagerImpl(mDisplay, parentWindow);    }    public WindowManagerImpl createPresentationWindowManager(Display display) {        return new WindowManagerImpl(display, mParentWindow);    }    /**     * Sets the window token to assign when none is specified by the client or     * available from the parent window.     *     * @param token The default token to assign.     */    public void setDefaultToken(IBinder token) {        mDefaultToken = token;    }    @Override    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {        applyDefaultToken(params);        mGlobal.addView(view, params, mDisplay, mParentWindow);    }    @Override    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {        applyDefaultToken(params);        mGlobal.updateViewLayout(view, params);    }    private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {        // Only use the default token if we don't have a parent window.        if (mDefaultToken != null && mParentWindow == null) {            if (!(params instanceof WindowManager.LayoutParams)) {                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");            }            // Only use the default token if we don't already have a token.            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;            if (wparams.token == null) {                wparams.token = mDefaultToken;            }        }    }    @Override    public void removeView(View view) {        mGlobal.removeView(view, false);    }    @Override    public void removeViewImmediate(View view) {        mGlobal.removeView(view, true);    }    @Override    public Display getDefaultDisplay() {        return mDisplay;    }}

从中可以看到addView又调用了 WindowManagerGlobal.java类中的addView,下面看看WindowManagerGlobal.java类的源码:

WindowManagerGlobal.java

    public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");        }        if (display == null) {            throw new IllegalArgumentException("display must not be null");        }        if (!(params instanceof WindowManager.LayoutParams)) {            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");        }        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;        if (parentWindow != null) {            parentWindow.adjustLayoutParamsForSubWindow(wparams);        } else {            // If there's no parent, then hardware acceleration for this view is            // set from the application's hardware acceleration setting.            final Context context = view.getContext();            if (context != null                    && (context.getApplicationInfo().flags                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;            }        }        ViewRootImpl root;        View panelParentView = null;        synchronized (mLock) {            // Start watching for system property changes.            if (mSystemPropertyUpdater == null) {                mSystemPropertyUpdater = new Runnable() {                    @Override public void run() {                        synchronized (mLock) {                            for (int i = mRoots.size() - 1; i >= 0; --i) {                                mRoots.get(i).loadSystemProperties();                            }                        }                    }                };                SystemProperties.addChangeCallback(mSystemPropertyUpdater);            }            int index = findViewLocked(view, false);            if (index >= 0) {                if (mDyingViews.contains(view)) {                    // Don't wait for MSG_DIE to make it's way through root's queue.                    mRoots.get(index).doDie();                } else {                    throw new IllegalStateException("View " + view                            + " has already been added to the window manager.");                }                // The previous removeView() had not completed executing. Now it has.            }            // If this is a panel window, then find the window it is being            // attached to for future reference.            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {                final int count = mViews.size();                for (int i = 0; i < count; i++) {                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {                        panelParentView = mViews.get(i);                    }                }            }            root = new ViewRootImpl(view.getContext(), display);            view.setLayoutParams(wparams);            mViews.add(view);            mRoots.add(root);            mParams.add(wparams);        }        // do this last because it fires off messages to start doing things        try {            root.setView(view, wparams, panelParentView);//这里调用ViewRootImpl类中的setView方法,在该方法中触发了ViewRootImpl.performTraversals()        } catch (RuntimeException e) {            // BadTokenException or InvalidDisplayException, clean up.            synchronized (mLock) {                final int index = findViewLocked(view, false);                if (index >= 0) {                    removeViewLocked(index, true);                }            }            throw e;        }    }

在该方法中的root.setView(view,wparams,panelParentView)方法,调用的是ViewRootImpl类中的setView方法,正是该setView方法触发了ViewRootImpl.performTraversals()方法,也就是View绘制的起点,之后会进行measure,layout,draw三个步骤从而完成一个View的显示工作。

ViewRootImpl.java

    /**     * We have one child     */    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {            if (mView == null) {                mView = view;                mAttachInfo.mDisplayState = mDisplay.getState();                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);                ...                mSoftInputMode = attrs.softInputMode;                mWindowAttributesChanged = true;                mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;                mAttachInfo.mRootView = view;                mAttachInfo.mScalingRequired = mTranslator != null;                mAttachInfo.mApplicationScale =                        mTranslator == null ? 1.0f : mTranslator.applicationScale;                if (panelParentView != null) {                    mAttachInfo.mPanelParentWindowToken                            = panelParentView.getApplicationWindowToken();                }                mAdded = true;                int res; /* = WindowManagerImpl.ADD_OKAY; */                // Schedule the first layout -before- adding to the window                // manager, to make sure we do the relayout before receiving                // any other events from the system.                requestLayout();//这里开始请求view的绘制                if ((mWindowAttributes.inputFeatures                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {                    mInputChannel = new InputChannel();                }                try {                    mOrigWindowType = mWindowAttributes.type;                    mAttachInfo.mRecomputeGlobalAttributes = true;                    collectViewAttributes();                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                            getHostVisibility(), mDisplay.getDisplayId(),                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,                            mAttachInfo.mOutsets, mInputChannel);                } catch (RemoteException e) {                    mAdded = false;                    mView = null;                    mAttachInfo.mRootView = null;                    mInputChannel = null;                    mFallbackEventHandler.setView(null);                    unscheduleTraversals();                    setAccessibilityFocus(null, null);                    throw new RuntimeException("Adding window failed", e);                } finally {                    if (restore) {                        attrs.restore();                    }                }                ....            }        }    }
在setView的requestLayout方法中开始View的绘制。

ViewRootImpl.java

        void scheduleTraversals() {        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            if (!mUnbufferedInputDispatch) {                scheduleConsumeBatchedInput();            }            notifyRendererOfFramePending();            pokeDrawLockIfNeeded();        }    }    void scheduleTraversals() {        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            if (!mUnbufferedInputDispatch) {                scheduleConsumeBatchedInput();            }            notifyRendererOfFramePending();            pokeDrawLockIfNeeded();        }    }    final class TraversalRunnable implements Runnable {        @Override        public void run() {            doTraversal();        }    }    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();    void doTraversal() {        if (mTraversalScheduled) {            mTraversalScheduled = false;            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);            if (mProfile) {                Debug.startMethodTracing("ViewAncestor");            }            performTraversals();            if (mProfile) {                Debug.stopMethodTracing();                mProfile = false;            }        }    }
 在scheduleTraversals()方法中向mChoreographer中postCallback,而具体的Runable内容在TraversalRunnable类中,该类在run函数中直接执行doTraversal()方法,可以看到在该方法中最终调用了performTraversals()开启View的绘制工作。

查看ViewRootImpl.java中的performTraversals()的源码如下:

ViewRootImpl.java中的performTraversals()方法

private void performTraversals() {        // cache mView since it is used so much below...        final View host = mView;        ...        if (host == null || !mAdded)            return;        mIsInTraversal = true;        mWillDrawSoon = true;        boolean windowSizeMayChange = false;        boolean newSurface = false;        boolean surfaceChanged = false;        WindowManager.LayoutParams lp = mWindowAttributes;        int desiredWindowWidth;        int desiredWindowHeight;        final int viewVisibility = getHostVisibility();        boolean viewVisibilityChanged = mViewVisibility != viewVisibility                || mNewSurfaceNeeded;        WindowManager.LayoutParams params = null;        if (mWindowAttributesChanged) {            mWindowAttributesChanged = false;            surfaceChanged = true;            params = lp;        }        CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();        if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {            params = lp;            mFullRedrawNeeded = true;            mLayoutRequested = true;            if (mLastInCompatMode) {                params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;                mLastInCompatMode = false;            } else {                params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;                mLastInCompatMode = true;            }        }        mWindowAttributesChangesFlag = 0;        Rect frame = mWinFrame;        if (mFirst) {            mFullRedrawNeeded = true;            mLayoutRequested = true;            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {                // NOTE -- system code, won't try to do compat mode.                Point size = new Point();                mDisplay.getRealSize(size);                desiredWindowWidth = size.x;                desiredWindowHeight = size.y;            } else {                DisplayMetrics packageMetrics =                    mView.getContext().getResources().getDisplayMetrics();                desiredWindowWidth = packageMetrics.widthPixels;                desiredWindowHeight = packageMetrics.heightPixels;            }            // We used to use the following condition to choose 32 bits drawing caches:            // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888            // However, windows are now always 32 bits by default, so choose 32 bits            mAttachInfo.mUse32BitDrawingCache = true;            mAttachInfo.mHasWindowFocus = false;            mAttachInfo.mWindowVisibility = viewVisibility;            mAttachInfo.mRecomputeGlobalAttributes = false;            viewVisibilityChanged = false;            mLastConfiguration.setTo(host.getResources().getConfiguration());            mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;            // Set the layout direction if it has not been set before (inherit is the default)            if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {                host.setLayoutDirection(mLastConfiguration.getLayoutDirection());            }            host.dispatchAttachedToWindow(mAttachInfo, 0);//这里调用了View的dispatchAttachedToWindow,也就是这里回调了onAttachedToWindow方法。            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);            dispatchApplyInsets(host);            //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);        } else {            desiredWindowWidth = frame.width();            desiredWindowHeight = frame.height();            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {                if (DEBUG_ORIENTATION) Log.v(TAG,                        "View " + host + " resized to: " + frame);                mFullRedrawNeeded = true;                mLayoutRequested = true;                windowSizeMayChange = true;            }        }        ...    }

在该方法中调用了host.dispatchAttachedToWindow(mAttachInfo, 0);方法。host是上面传下来的DecodView,该类继承与FrameLayout类,也就是ViewGroup的子类,所以先调用的是ViewGroup中的dispatchAttachedToWindow,其代码如下:

ViewGroup.java

    @Override    void dispatchAttachedToWindow(AttachInfo info, int visibility) {        mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;        super.dispatchAttachedToWindow(info, visibility);//这里先调用父类,也就是View的dispathcAttachedToWindow。        mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;        final int count = mChildrenCount;        final View[] children = mChildren;        for (int i = 0; i < count; i++) {            final View child = children[i];            child.dispatchAttachedToWindow(info,                    combineVisibility(visibility, child.getVisibility()));//这里调用子View的dispatchAttachedToWindow        }        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();        for (int i = 0; i < transientCount; ++i) {            View view = mTransientViews.get(i);            view.dispatchAttachedToWindow(info,                    combineVisibility(visibility, view.getVisibility()));        }    }

下面查看对应的View类中的dispatchAttacToWindow。代码如下:

View.java

void dispatchAttachedToWindow(AttachInfo info, int visibility) {        //System.out.println("Attached! " + this);        mAttachInfo = info;        if (mOverlay != null) {            mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);        }        mWindowAttachCount++;        // We will need to evaluate the drawable state at least once.        mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;        if (mFloatingTreeObserver != null) {            info.mTreeObserver.merge(mFloatingTreeObserver);            mFloatingTreeObserver = null;        }        if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {            mAttachInfo.mScrollContainers.add(this);            mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;        }        performCollectViewAttributes(mAttachInfo, visibility);        onAttachedToWindow();//快看,快看,在这里!终于找到这个方法调用的位置了        ListenerInfo li = mListenerInfo;        final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =                li != null ? li.mOnAttachStateChangeListeners : null;        if (listeners != null && listeners.size() > 0) {            // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to            // perform the dispatching. The iterator is a safe guard against listeners that            // could mutate the list by calling the various add/remove methods. This prevents            // the array from being modified while we iterate it.            for (OnAttachStateChangeListener listener : listeners) {                listener.onViewAttachedToWindow(this);//            }        }        int vis = info.mWindowVisibility;        if (vis != GONE) {            onWindowVisibilityChanged(vis);        }        // Send onVisibilityChanged directly instead of dispatchVisibilityChanged.        // As all views in the subtree will already receive dispatchAttachedToWindow        // traversing the subtree again here is not desired.        onVisibilityChanged(this, visibility);        if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {            // If nobody has evaluated the drawable state yet, then do it now.            refreshDrawableState();        }        needGlobalAttributesUpdate(false);    }
从上面代码可以看出一个布局的onAttachedToWindow会先调用自己的,然后再调用自己孩子的。而且从View.java的代码中也可以看出onAttachedToWindow和View自身的visibility无关,即使visibility==GONE,该方法也会调用。

好,下面来分析下onDetachedFromWindow方法的调用时机。在onDestory调用前会调用ActivityThread.java中的handleDestroyActivity方法,贴出代码:

ActivityThread.java

    private void handleDestroyActivity(IBinder token, boolean finishing,            int configChanges, boolean getNonConfigInstance) {        ActivityClientRecord r = performDestroyActivity(token, finishing,                configChanges, getNonConfigInstance);        if (r != null) {            cleanUpPendingRemoveWindows(r);            WindowManager wm = r.activity.getWindowManager();            View v = r.activity.mDecor;            if (v != null) {                if (r.activity.mVisibleFromServer) {                    mNumVisibleActivities--;                }                IBinder wtoken = v.getWindowToken();                if (r.activity.mWindowAdded) {                    if (r.onlyLocalRequest) {                        // Hold off on removing this until the new activity's                        // window is being added.                        r.mPendingRemoveWindow = v;                        r.mPendingRemoveWindowManager = wm;                    } else {                        wm.removeViewImmediate(v);//看这里,看这里                    }                }                if (wtoken != null && r.mPendingRemoveWindow == null) {                    WindowManagerGlobal.getInstance().closeAll(wtoken,                            r.activity.getClass().getName(), "Activity");                }                r.activity.mDecor = null;            }            if (r.mPendingRemoveWindow == null) {                // If we are delaying the removal of the activity window, then                // we can't clean up all windows here.  Note that we can't do                // so later either, which means any windows that aren't closed                // by the app will leak.  Well we try to warning them a lot                // about leaking windows, because that is a bug, so if they are                // using this recreate facility then they get to live with leaks.                WindowManagerGlobal.getInstance().closeAll(token,                        r.activity.getClass().getName(), "Activity");            }            // Mocked out contexts won't be participating in the normal            // process lifecycle, but if we're running with a proper            // ApplicationContext we need to have it tear down things            // cleanly.            Context c = r.activity.getBaseContext();            if (c instanceof ContextImpl) {                ((ContextImpl) c).scheduleFinalCleanup(                        r.activity.getClass().getName(), "Activity");            }        }        if (finishing) {            try {                ActivityManagerNative.getDefault().activityDestroyed(token);            } catch (RemoteException ex) {                // If the system process has died, it's game over for everyone.            }        }        mSomeActivitiesChanged = true;    }
看代码中的wm.removeViewImmediate方法,还是走到WindowManagerImpl类中的removeViewImmediate,代码如下:

WindowManagerImpl.java

    @Override    public void removeViewImmediate(View view) {        mGlobal.removeView(view, true);    }
好熟悉啊,还是走到了WindowManagerGlobal类中的removeView,代码如下:
WindowManagerGlobal.java
    public void removeView(View view, boolean immediate) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");        }        synchronized (mLock) {            int index = findViewLocked(view, true);            View curView = mRoots.get(index).getView();            removeViewLocked(index, immediate);            if (curView == view) {                return;            }            throw new IllegalStateException("Calling with view " + view                    + " but the ViewAncestor is attached to " + curView);        }    }    private void removeViewLocked(int index, boolean immediate) {        ViewRootImpl root = mRoots.get(index);        View view = root.getView();        if (view != null) {            InputMethodManager imm = InputMethodManager.getInstance();            if (imm != null) {                imm.windowDismissed(mViews.get(index).getWindowToken());            }        }        boolean deferred = root.die(immediate);        if (view != null) {            view.assignParent(null);            if (deferred) {                mDyingViews.add(view);            }        }    }
跟着代码继续走,到了ViewRootImpl类中的die,代码如下:

ViewRootImpl.java

/**     * @param immediate True, do now if not in traversal. False, put on queue and do later.     * @return True, request has been queued. False, request has been completed.     */    boolean die(boolean immediate) {        // Make sure we do execute immediately if we are in the middle of a traversal or the damage        // done by dispatchDetachedFromWindow will cause havoc on return.        if (immediate && !mIsInTraversal) {            doDie();            return false;        }        if (!mIsDrawing) {            destroyHardwareRenderer();        } else {            Log.e(TAG, "Attempting to destroy the window while drawing!\n" +                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());        }        mHandler.sendEmptyMessage(MSG_DIE);        return true;    }    void doDie() {        checkThread();        if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);        synchronized (this) {            if (mRemoved) {                return;            }            mRemoved = true;            if (mAdded) {                dispatchDetachedFromWindow();//看这里,看这里            }            if (mAdded && !mFirst) {                destroyHardwareRenderer();                if (mView != null) {                    int viewVisibility = mView.getVisibility();                    boolean viewVisibilityChanged = mViewVisibility != viewVisibility;                    if (mWindowAttributesChanged || viewVisibilityChanged) {                        // If layout params have been changed, first give them                        // to the window manager to make sure it has the correct                        // animation info.                        try {                            if ((relayoutWindow(mWindowAttributes, viewVisibility, false)                                    & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {                                mWindowSession.finishDrawing(mWindow);                            }                        } catch (RemoteException e) {                        }                    }                    mSurface.release();                }            }            mAdded = false;        }        WindowManagerGlobal.getInstance().doRemoveView(this);    }
在doDie里面调用了dispatchDetachedFromWindow()方法,代码如下:

ViewRootImpl.java

void dispatchDetachedFromWindow() {        if (mView != null && mView.mAttachInfo != null) {            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);            mView.dispatchDetachedFromWindow();//看这里,看这里        }        mAccessibilityInteractionConnectionManager.ensureNoConnection();        mAccessibilityManager.removeAccessibilityStateChangeListener(                mAccessibilityInteractionConnectionManager);        mAccessibilityManager.removeHighTextContrastStateChangeListener(                mHighContrastTextManager);        removeSendWindowContentChangedCallback();        destroyHardwareRenderer();        setAccessibilityFocus(null, null);        mView.assignParent(null);        mView = null;        mAttachInfo.mRootView = null;        mSurface.release();        if (mInputQueueCallback != null && mInputQueue != null) {            mInputQueueCallback.onInputQueueDestroyed(mInputQueue);            mInputQueue.dispose();            mInputQueueCallback = null;            mInputQueue = null;        }        if (mInputEventReceiver != null) {            mInputEventReceiver.dispose();            mInputEventReceiver = null;        }        try {            mWindowSession.remove(mWindow);        } catch (RemoteException e) {        }        // Dispose the input channel after removing the window so the Window Manager        // doesn't interpret the input channel being closed as an abnormal termination.        if (mInputChannel != null) {            mInputChannel.dispose();            mInputChannel = null;        }        mDisplayManager.unregisterDisplayListener(mDisplayListener);        unscheduleTraversals();    }
还记着在WindowManagerGlobal里面的root.setView(view, wparams, panelParentView);调用吧,这里的mView.dispatchDetachedFromWindow();这个mView也即是上面传过来的view。也就是先看DecorView即ViewGroup里面的dispatchDetachedFromWindow,代码如下:

ViewGroup.java

    @Override    void dispatchDetachedFromWindow() {        // If we still have a touch target, we are still in the process of        // dispatching motion events to a child; we need to get rid of that        // child to avoid dispatching events to it after the window is torn        // down. To make sure we keep the child in a consistent state, we        // first send it an ACTION_CANCEL motion event.        cancelAndClearTouchTargets(null);        // Similarly, set ACTION_EXIT to all hover targets and clear them.        exitHoverTargets();        // In case view is detached while transition is running        mLayoutCalledWhileSuppressed = false;        // Tear down our drag tracking        mDragNotifiedChildren = null;        if (mCurrentDrag != null) {            mCurrentDrag.recycle();            mCurrentDrag = null;        }        final int count = mChildrenCount;        final View[] children = mChildren;        for (int i = 0; i < count; i++) {            children[i].dispatchDetachedFromWindow();//这里会先调子类的dispatchDetachedFromWindow        }        clearDisappearingChildren();        final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();        for (int i = 0; i < transientCount; ++i) {            View view = mTransientViews.get(i);            view.dispatchDetachedFromWindow();        }        super.dispatchDetachedFromWindow();//然后这里才调用自己的。    }
这之后又到View的dispatchDetachedFromWindow了,代码如下:

View.java

void dispatchDetachedFromWindow() {        AttachInfo info = mAttachInfo;        if (info != null) {            int vis = info.mWindowVisibility;            if (vis != GONE) {                onWindowVisibilityChanged(GONE);            }        }        onDetachedFromWindow();//绕了一大圈,还是找到你了。快看快看,揪出来了。        onDetachedFromWindowInternal();        InputMethodManager imm = InputMethodManager.peekInstance();        if (imm != null) {            imm.onViewDetachedFromWindow(this);        }        ListenerInfo li = mListenerInfo;        final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =                li != null ? li.mOnAttachStateChangeListeners : null;        if (listeners != null && listeners.size() > 0) {            // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to            // perform the dispatching. The iterator is a safe guard against listeners that            // could mutate the list by calling the various add/remove methods. This prevents            // the array from being modified while we iterate it.            for (OnAttachStateChangeListener listener : listeners) {                listener.onViewDetachedFromWindow(this);            }        }        if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0) {            mAttachInfo.mScrollContainers.remove(this);            mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;        }        mAttachInfo = null;        if (mOverlay != null) {            mOverlay.getOverlayView().dispatchDetachedFromWindow();        }    }
看代码终于找到了onDetachedFromWindow的调用地方了。

这里总结下:

1.onAttachedToWindow调用顺序:ActivityThread.handleResumeActivity->WindowManagerImpl.addView->WindowManagerGlobal.addView->ViewRootImpl.performTraversals->ViewGroup.dispatchAttachedToWindow->View.dispatchAttachedToWindow->onAttachedToWindow

2.onDetachedFromWindow调用顺序:ActivityThread.handleDestroyActivity->WindowManagerImpl.removeViewImmediate->WindowManagerGlobal.removeView->ViewRootImpl.die->ViewRootImpl.doDie->ViewRootImpl.dispatchDetachedFromWindow->ViewGroup.dispatchDetachedFromWindow->View.dispatchDetachedFromWindow->onDetachedToWindow

3.onAttachedToWindow和onDetachedFromWindow调用与visibility无关。

4.onAttachedToWindow是先调用自己,然后调用儿子View的。onDetachedFromWindow是先调用儿子View的,然后再调用自己的。












阅读全文
1 0