Activity的onAttachedToWindow和onDetachedFromWindow调用时机源码解析

终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~

先上测试代码:

MyView.java

  1. import android.content.Context;  
  2. import android.util.AttributeSet;  
  3. import android.util.Log;  
  4. import android.widget.TextView;  
  5.   
  6. public class MyView extends TextView {  
  7.     public MyView(Context context) {  
  8.         super(context);  
  9.     }  
  10.   
  11.     public MyView(Context context, AttributeSet attrs) {  
  12.         super(context, attrs);  
  13.         Log.e(”test”,“view constructor”);  
  14.     }  
  15.   
  16.     @Override  
  17.     protected void onAttachedToWindow() {  
  18.         super.onAttachedToWindow();  
  19.         Log.e(”test”“onAttachedToWindow”);  
  20.     }  
  21.   
  22.     @Override  
  23.     protected void onDetachedFromWindow() {  
  24.         super.onDetachedFromWindow();  
  25.         Log.e(”test”“onDetachedFromWindow”);  
  26.     }  
  27. }  
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

  1. import android.support.v7.app.AppCompatActivity;  
  2. import android.os.Bundle;  
  3. import android.util.Log;  
  4.   
  5. public class MainActivity extends AppCompatActivity {  
  6.   
  7.     @Override  
  8.     protected void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         Log.e(”test”“before setContextView”);  
  11.         setContentView(R.layout.activity_main);  
  12.         Log.e(”test”“after setContextView”);  
  13.     }  
  14.   
  15.     @Override  
  16.     protected void onResume() {  
  17.         super.onResume();  
  18.         Log.e(”test”“onResume”);  
  19.     }  
  20.   
  21.     @Override  
  22.     protected void onDestroy() {  
  23.         super.onDestroy();  
  24.         Log.e(”test”“onDestroy”);  
  25.     }  
  26. }  
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

  1. final void handleResumeActivity(IBinder token,  
  2.             boolean clearHide, boolean isForward, boolean reallyResume) {  
  3.         // If we are getting ready to gc after going to the background, well  
  4.         // we are back active so skip it.  
  5.         unscheduleGcIdler();  
  6.         mSomeActivitiesChanged = true;  
  7.   
  8.         // TODO Push resumeArgs into the activity for consideration  
  9.         ActivityClientRecord r = performResumeActivity(token, clearHide);  
  10.   
  11.         if (r != null) {  
  12.             final Activity a = r.activity;  
  13.   
  14.             if (localLOGV) Slog.v(  
  15.                 TAG, ”Resume ” + r + “ started activity: ” +  
  16.                 a.mStartedActivity + ”, hideForNow: ” + r.hideForNow  
  17.                 + ”, finished: ” + a.mFinished);  
  18.   
  19.             final int forwardBit = isForward ?  
  20.                     WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;  
  21.   
  22.             // If the window hasn’t yet been added to the window manager,  
  23.             // and this guy didn’t finish itself or start another activity,  
  24.             // then go ahead and add the window.  
  25.             boolean willBeVisible = !a.mStartedActivity;  
  26.             if (!willBeVisible) {  
  27.                 try {  
  28.                     willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(  
  29.                             a.getActivityToken());  
  30.                 } catch (RemoteException e) {  
  31.                 }  
  32.             }  
  33.             if (r.window == null && !a.mFinished && willBeVisible) {  
  34.                 r.window = r.activity.getWindow();  
  35.                 View decor = r.window.getDecorView();  
  36.                 decor.setVisibility(View.INVISIBLE);  
  37.                 ViewManager wm = a.getWindowManager();  
  38.                 WindowManager.LayoutParams l = r.window.getAttributes();  
  39.                 a.mDecor = decor;  
  40.                 l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  
  41.                 l.softInputMode |= forwardBit;  
  42.                 if (a.mVisibleFromClient) {  
  43.                     a.mWindowAdded = true;  
  44.                     wm.addView(decor, l);//这里调用了ViewManager中的addView方法。  
  45.                 }  
  46.   
  47.             // If the window has already been added, but during resume  
  48.             // we started another activity, then don’t yet make the  
  49.             // window visible.  
  50.             } else if (!willBeVisible) {  
  51.                 if (localLOGV) Slog.v(  
  52.                     TAG, ”Launch ” + r + “ mStartedActivity set”);  
  53.                 r.hideForNow = true;  
  54.             }  
  55.   
  56.             // Get rid of anything left hanging around.  
  57.             cleanUpPendingRemoveWindows(r);  
  58.   
  59.             // The window is now visible if it has been added, we are not  
  60.             // simply finishing, and we are not starting another activity.  
  61.             if (!r.activity.mFinished && willBeVisible  
  62.                     && r.activity.mDecor != null && !r.hideForNow) {  
  63.                 if (r.newConfig != null) {  
  64.                     r.tmpConfig.setTo(r.newConfig);  
  65.                     if (r.overrideConfig != null) {  
  66.                         r.tmpConfig.updateFrom(r.overrideConfig);  
  67.                     }  
  68.                     if (DEBUG_CONFIGURATION) Slog.v(TAG, “Resuming activity ”  
  69.                             + r.activityInfo.name + ” with newConfig ” + r.tmpConfig);  
  70.                     performConfigurationChanged(r.activity, r.tmpConfig);  
  71.                     freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));  
  72.                     r.newConfig = null;  
  73.                 }  
  74.                 if (localLOGV) Slog.v(TAG, “Resuming ” + r + “ with isForward=”  
  75.                         + isForward);  
  76.                 WindowManager.LayoutParams l = r.window.getAttributes();  
  77.                 if ((l.softInputMode  
  78.                         & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)  
  79.                         != forwardBit) {  
  80.                     l.softInputMode = (l.softInputMode  
  81.                             & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))  
  82.                             | forwardBit;  
  83.                     if (r.activity.mVisibleFromClient) {  
  84.                         ViewManager wm = a.getWindowManager();  
  85.                         View decor = r.window.getDecorView();  
  86.                         wm.updateViewLayout(decor, l);  
  87.                     }  
  88.                 }  
  89.                 r.activity.mVisibleFromServer = true;  
  90.                 mNumVisibleActivities++;  
  91.                 if (r.activity.mVisibleFromClient) {  
  92.                     r.activity.makeVisible();  
  93.                 }  
  94.             }  
  95.             ….  
  96.     }  
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类型的,查看对应代码是:

  1. package android.view;  
  2.   
  3. /** Interface to let you add and remove child views to an Activity. To get an instance 
  4.   * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. 
  5.   */  
  6. public interface ViewManager  
  7. {  
  8.     /** 
  9.      * Assign the passed LayoutParams to the passed View and add the view to the window. 
  10.      * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming 
  11.      * errors, such as adding a second view to a window without removing the first view. 
  12.      * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a 
  13.      * secondary {@link Display} and the specified display can’t be found 
  14.      * (see {@link android.app.Presentation}). 
  15.      * @param view The view to be added to this window. 
  16.      * @param params The LayoutParams to assign to view. 
  17.      */  
  18.     public void addView(View view, ViewGroup.LayoutParams params);  
  19.     public void updateViewLayout(View view, ViewGroup.LayoutParams params);  
  20.     public void removeView(View view);  
  21. }  
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

  1. /* 
  2. * @see WindowManager 
  3. * @see WindowManagerGlobal 
  4. * @hide 
  5. */  
  6. ublic final class WindowManagerImpl implements WindowManager {  
  7.    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();  
  8.    private final Display mDisplay;  
  9.    private final Window mParentWindow;  
  10.   
  11.    private IBinder mDefaultToken;  
  12.   
  13.    public WindowManagerImpl(Display display) {  
  14.        this(display, null);  
  15.    }  
  16.   
  17.    private WindowManagerImpl(Display display, Window parentWindow) {  
  18.        mDisplay = display;  
  19.        mParentWindow = parentWindow;  
  20.    }  
  21.   
  22.    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {  
  23.        return new WindowManagerImpl(mDisplay, parentWindow);  
  24.    }  
  25.   
  26.    public WindowManagerImpl createPresentationWindowManager(Display display) {  
  27.        return new WindowManagerImpl(display, mParentWindow);  
  28.    }  
  29.   
  30.    /** 
  31.     * Sets the window token to assign when none is specified by the client or 
  32.     * available from the parent window. 
  33.     * 
  34.     * @param token The default token to assign. 
  35.     */  
  36.    public void setDefaultToken(IBinder token) {  
  37.        mDefaultToken = token;  
  38.    }  
  39.   
  40.    @Override  
  41.    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {  
  42.        applyDefaultToken(params);  
  43.        mGlobal.addView(view, params, mDisplay, mParentWindow);  
  44.    }  
  45.   
  46.    @Override  
  47.    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {  
  48.        applyDefaultToken(params);  
  49.        mGlobal.updateViewLayout(view, params);  
  50.    }  
  51.   
  52.    private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {  
  53.        // Only use the default token if we don’t have a parent window.  
  54.        if (mDefaultToken != null && mParentWindow == null) {  
  55.            if (!(params instanceof WindowManager.LayoutParams)) {  
  56.                throw new IllegalArgumentException(“Params must be WindowManager.LayoutParams”);  
  57.            }  
  58.   
  59.            // Only use the default token if we don’t already have a token.  
  60.            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;  
  61.            if (wparams.token == null) {  
  62.                wparams.token = mDefaultToken;  
  63.            }  
  64.        }  
  65.    }  
  66.   
  67.    @Override  
  68.    public void removeView(View view) {  
  69.        mGlobal.removeView(view, false);  
  70.    }  
  71.   
  72.    @Override  
  73.    public void removeViewImmediate(View view) {  
  74.        mGlobal.removeView(view, true);  
  75.    }  
  76.   
  77.    @Override  
  78.    public Display getDefaultDisplay() {  
  79.        return mDisplay;  
  80.    }  
 /*
 * @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;
    }
}
  1.   

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

WindowManagerGlobal.java

  1. public void addView(View view, ViewGroup.LayoutParams params,  
  2.         Display display, Window parentWindow) {  
  3.     if (view == null) {  
  4.         throw new IllegalArgumentException(“view must not be null”);  
  5.     }  
  6.     if (display == null) {  
  7.         throw new IllegalArgumentException(“display must not be null”);  
  8.     }  
  9.     if (!(params instanceof WindowManager.LayoutParams)) {  
  10.         throw new IllegalArgumentException(“Params must be WindowManager.LayoutParams”);  
  11.     }  
  12.   
  13.     final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;  
  14.     if (parentWindow != null) {  
  15.         parentWindow.adjustLayoutParamsForSubWindow(wparams);  
  16.     } else {  
  17.         // If there’s no parent, then hardware acceleration for this view is  
  18.         // set from the application’s hardware acceleration setting.  
  19.         final Context context = view.getContext();  
  20.         if (context != null  
  21.                 && (context.getApplicationInfo().flags  
  22.                         & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {  
  23.             wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;  
  24.         }  
  25.     }  
  26.   
  27.     ViewRootImpl root;  
  28.     View panelParentView = null;  
  29.   
  30.     synchronized (mLock) {  
  31.         // Start watching for system property changes.  
  32.         if (mSystemPropertyUpdater == null) {  
  33.             mSystemPropertyUpdater = new Runnable() {  
  34.                 @Override public void run() {  
  35.                     synchronized (mLock) {  
  36.                         for (int i = mRoots.size() - 1; i >= 0; –i) {  
  37.                             mRoots.get(i).loadSystemProperties();  
  38.                         }  
  39.                     }  
  40.                 }  
  41.             };  
  42.             SystemProperties.addChangeCallback(mSystemPropertyUpdater);  
  43.         }  
  44.   
  45.         int index = findViewLocked(view, false);  
  46.         if (index >= 0) {  
  47.             if (mDyingViews.contains(view)) {  
  48.                 // Don’t wait for MSG_DIE to make it’s way through root’s queue.  
  49.                 mRoots.get(index).doDie();  
  50.             } else {  
  51.                 throw new IllegalStateException(“View ” + view  
  52.                         + ” has already been added to the window manager.”);  
  53.             }  
  54.             // The previous removeView() had not completed executing. Now it has.  
  55.         }  
  56.   
  57.         // If this is a panel window, then find the window it is being  
  58.         // attached to for future reference.  
  59.         if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&  
  60.                 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {  
  61.             final int count = mViews.size();  
  62.             for (int i = 0; i < count; i++) {  
  63.                 if (mRoots.get(i).mWindow.asBinder() == wparams.token) {  
  64.                     panelParentView = mViews.get(i);  
  65.                 }  
  66.             }  
  67.         }  
  68.   
  69.         root = new ViewRootImpl(view.getContext(), display);  
  70.   
  71.         view.setLayoutParams(wparams);  
  72.   
  73.         mViews.add(view);  
  74.         mRoots.add(root);  
  75.         mParams.add(wparams);  
  76.     }  
  77.   
  78.     // do this last because it fires off messages to start doing things  
  79.     try {  
  80.         root.setView(view, wparams, panelParentView);//这里调用ViewRootImpl类中的setView方法,在该方法中触发了<span style=”color:rgb(101,123,131);font-family:Menlo, Monaco, Consolas, ‘Courier New’, monospace;line-height:20.4px;white-space:pre-wrap;background-color:rgb(246,246,246);”>ViewRootImpl.performTraversals()</span>  
  81.     } catch (RuntimeException e) {  
  82.         // BadTokenException or InvalidDisplayException, clean up.  
  83.         synchronized (mLock) {  
  84.             final int index = findViewLocked(view, false);  
  85.             if (index >= 0) {  
  86.                 removeViewLocked(index, true);  
  87.             }  
  88.         }  
  89.         throw e;  
  90.     }  
  91. }  
    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

  1. /** 
  2.  * We have one child 
  3.  */  
  4. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {  
  5.     synchronized (this) {  
  6.         if (mView == null) {  
  7.             mView = view;  
  8.   
  9.             mAttachInfo.mDisplayState = mDisplay.getState();  
  10.             mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);  
  11.   
  12.             …  
  13.             mSoftInputMode = attrs.softInputMode;  
  14.             mWindowAttributesChanged = true;  
  15.             mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;  
  16.             mAttachInfo.mRootView = view;  
  17.             mAttachInfo.mScalingRequired = mTranslator != null;  
  18.             mAttachInfo.mApplicationScale =  
  19.                     mTranslator == null ? 1.0f : mTranslator.applicationScale;  
  20.             if (panelParentView != null) {  
  21.                 mAttachInfo.mPanelParentWindowToken  
  22.                         = panelParentView.getApplicationWindowToken();  
  23.             }  
  24.             mAdded = true;  
  25.             int res; /* = WindowManagerImpl.ADD_OKAY; */  
  26.   
  27.             // Schedule the first layout -before- adding to the window  
  28.             // manager, to make sure we do the relayout before receiving  
  29.             // any other events from the system.  
  30.             requestLayout();//这里开始请求view的绘制  
  31.             if ((mWindowAttributes.inputFeatures  
  32.                     & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {  
  33.                 mInputChannel = new InputChannel();  
  34.             }  
  35.             try {  
  36.                 mOrigWindowType = mWindowAttributes.type;  
  37.                 mAttachInfo.mRecomputeGlobalAttributes = true;  
  38.                 collectViewAttributes();  
  39.                 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,  
  40.                         getHostVisibility(), mDisplay.getDisplayId(),  
  41.                         mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,  
  42.                         mAttachInfo.mOutsets, mInputChannel);  
  43.             } catch (RemoteException e) {  
  44.                 mAdded = false;  
  45.                 mView = null;  
  46.                 mAttachInfo.mRootView = null;  
  47.                 mInputChannel = null;  
  48.                 mFallbackEventHandler.setView(null);  
  49.                 unscheduleTraversals();  
  50.                 setAccessibilityFocus(nullnull);  
  51.                 throw new RuntimeException(“Adding window failed”, e);  
  52.             } finally {  
  53.                 if (restore) {  
  54.                     attrs.restore();  
  55.                 }  
  56.             }  
  57.             ….  
  58.         }  
  59.     }  
  60. }  
    /**
     * 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

  1. void scheduleTraversals() {  
  2.     if (!mTraversalScheduled) {  
  3.         mTraversalScheduled = true;  
  4.         mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();  
  5.         mChoreographer.postCallback(  
  6.                 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);  
  7.         if (!mUnbufferedInputDispatch) {  
  8.             scheduleConsumeBatchedInput();  
  9.         }  
  10.         notifyRendererOfFramePending();  
  11.         pokeDrawLockIfNeeded();  
  12.     }  
  13. }  
  14.   
  15. void scheduleTraversals() {  
  16.     if (!mTraversalScheduled) {  
  17.         mTraversalScheduled = true;  
  18.         mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();  
  19.         mChoreographer.postCallback(  
  20.                 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);  
  21.         if (!mUnbufferedInputDispatch) {  
  22.             scheduleConsumeBatchedInput();  
  23.         }  
  24.         notifyRendererOfFramePending();  
  25.         pokeDrawLockIfNeeded();  
  26.     }  
  27. }  
  28. final class TraversalRunnable implements Runnable {  
  29.     @Override  
  30.     public void run() {  
  31.         doTraversal();  
  32.     }  
  33. }  
  34. final TraversalRunnable mTraversalRunnable = new TraversalRunnable();  
  35. void doTraversal() {  
  36.     if (mTraversalScheduled) {  
  37.         mTraversalScheduled = false;  
  38.         mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);  
  39.   
  40.         if (mProfile) {  
  41.             Debug.startMethodTracing(”ViewAncestor”);  
  42.         }  
  43.   
  44.         performTraversals();  
  45.   
  46.         if (mProfile) {  
  47.             Debug.stopMethodTracing();  
  48.             mProfile = false;  
  49.         }  
  50.     }  
  51. }  
    
    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()方法

  1. private void performTraversals() {  
  2.         // cache mView since it is used so much below…  
  3.         final View host = mView;  
  4.         …  
  5.         if (host == null || !mAdded)  
  6.             return;  
  7.   
  8.         mIsInTraversal = true;  
  9.         mWillDrawSoon = true;  
  10.         boolean windowSizeMayChange = false;  
  11.         boolean newSurface = false;  
  12.         boolean surfaceChanged = false;  
  13.         WindowManager.LayoutParams lp = mWindowAttributes;  
  14.   
  15.         int desiredWindowWidth;  
  16.         int desiredWindowHeight;  
  17.   
  18.         final int viewVisibility = getHostVisibility();  
  19.         boolean viewVisibilityChanged = mViewVisibility != viewVisibility  
  20.                 || mNewSurfaceNeeded;  
  21.   
  22.         WindowManager.LayoutParams params = null;  
  23.         if (mWindowAttributesChanged) {  
  24.             mWindowAttributesChanged = false;  
  25.             surfaceChanged = true;  
  26.             params = lp;  
  27.         }  
  28.         CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();  
  29.         if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {  
  30.             params = lp;  
  31.             mFullRedrawNeeded = true;  
  32.             mLayoutRequested = true;  
  33.             if (mLastInCompatMode) {  
  34.                 params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;  
  35.                 mLastInCompatMode = false;  
  36.             } else {  
  37.                 params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;  
  38.                 mLastInCompatMode = true;  
  39.             }  
  40.         }  
  41.   
  42.         mWindowAttributesChangesFlag = 0;  
  43.   
  44.         Rect frame = mWinFrame;  
  45.         if (mFirst) {  
  46.             mFullRedrawNeeded = true;  
  47.             mLayoutRequested = true;  
  48.   
  49.             if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL  
  50.                     || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {  
  51.                 // NOTE – system code, won’t try to do compat mode.  
  52.                 Point size = new Point();  
  53.                 mDisplay.getRealSize(size);  
  54.                 desiredWindowWidth = size.x;  
  55.                 desiredWindowHeight = size.y;  
  56.             } else {  
  57.                 DisplayMetrics packageMetrics =  
  58.                     mView.getContext().getResources().getDisplayMetrics();  
  59.                 desiredWindowWidth = packageMetrics.widthPixels;  
  60.                 desiredWindowHeight = packageMetrics.heightPixels;  
  61.             }  
  62.   
  63.             // We used to use the following condition to choose 32 bits drawing caches:  
  64.             // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888  
  65.             // However, windows are now always 32 bits by default, so choose 32 bits  
  66.             mAttachInfo.mUse32BitDrawingCache = true;  
  67.             mAttachInfo.mHasWindowFocus = false;  
  68.             mAttachInfo.mWindowVisibility = viewVisibility;  
  69.             mAttachInfo.mRecomputeGlobalAttributes = false;  
  70.             viewVisibilityChanged = false;  
  71.             mLastConfiguration.setTo(host.getResources().getConfiguration());  
  72.             mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;  
  73.             // Set the layout direction if it has not been set before (inherit is the default)  
  74.             if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {  
  75.                 host.setLayoutDirection(mLastConfiguration.getLayoutDirection());  
  76.             }  
  77.             host.dispatchAttachedToWindow(mAttachInfo, 0);//这里调用了View的dispatchAttachedToWindow,也就是这里回调了onAttachedToWindow方法。  
  78.             mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);  
  79.             dispatchApplyInsets(host);  
  80.             //Log.i(TAG, “Screen on initialized: ” + attachInfo.mKeepScreenOn);  
  81.   
  82.         } else {  
  83.             desiredWindowWidth = frame.width();  
  84.             desiredWindowHeight = frame.height();  
  85.             if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {  
  86.                 if (DEBUG_ORIENTATION) Log.v(TAG,  
  87.                         ”View ” + host + “ resized to: ” + frame);  
  88.                 mFullRedrawNeeded = true;  
  89.                 mLayoutRequested = true;  
  90.                 windowSizeMayChange = true;  
  91.             }  
  92.         }  
  93.   
  94.         …  
  95.     }  
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

  1. @Override  
  2. void dispatchAttachedToWindow(AttachInfo info, int visibility) {  
  3.     mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;  
  4.     super.dispatchAttachedToWindow(info, visibility);//这里先调用父类,也就是View的dispathcAttachedToWindow。  
  5.     mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;  
  6.   
  7.     final int count = mChildrenCount;  
  8.     final View[] children = mChildren;  
  9.     for (int i = 0; i < count; i++) {  
  10.         final View child = children[i];  
  11.         child.dispatchAttachedToWindow(info,  
  12.                 combineVisibility(visibility, child.getVisibility()));//这里调用子View的<span style=”font-family:Arial, Helvetica, sans-serif;”>dispatchAttachedToWindow</span>  
  13.     }  
  14.     final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();  
  15.     for (int i = 0; i < transientCount; ++i) {  
  16.         View view = mTransientViews.get(i);  
  17.         view.dispatchAttachedToWindow(info,  
  18.                 combineVisibility(visibility, view.getVisibility()));  
  19.     }  
  20. }  
    @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

  1. void dispatchAttachedToWindow(AttachInfo info, int visibility) {  
  2.         //System.out.println(“Attached! ” + this);  
  3.         mAttachInfo = info;  
  4.         if (mOverlay != null) {  
  5.             mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);  
  6.         }  
  7.         mWindowAttachCount++;  
  8.         // We will need to evaluate the drawable state at least once.  
  9.         mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;  
  10.         if (mFloatingTreeObserver != null) {  
  11.             info.mTreeObserver.merge(mFloatingTreeObserver);  
  12.             mFloatingTreeObserver = null;  
  13.         }  
  14.         if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {  
  15.             mAttachInfo.mScrollContainers.add(this);  
  16.             mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;  
  17.         }  
  18.         performCollectViewAttributes(mAttachInfo, visibility);  
  19.         onAttachedToWindow();//快看,快看,在这里!终于找到这个方法调用的位置了  
  20.         ListenerInfo li = mListenerInfo;  
  21.         final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =  
  22.                 li != null ? li.mOnAttachStateChangeListeners : null;  
  23.         if (listeners != null && listeners.size() > 0) {  
  24.             // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to  
  25.             // perform the dispatching. The iterator is a safe guard against listeners that  
  26.             // could mutate the list by calling the various add/remove methods. This prevents  
  27.             // the array from being modified while we iterate it.  
  28.             for (OnAttachStateChangeListener listener : listeners) {  
  29.                 listener.onViewAttachedToWindow(this);//  
  30.             }  
  31.         }  
  32.   
  33.         int vis = info.mWindowVisibility;  
  34.         if (vis != GONE) {  
  35.             onWindowVisibilityChanged(vis);  
  36.         }  
  37.   
  38.         // Send onVisibilityChanged directly instead of dispatchVisibilityChanged.  
  39.         // As all views in the subtree will already receive dispatchAttachedToWindow  
  40.         // traversing the subtree again here is not desired.  
  41.         onVisibilityChanged(this, visibility);  
  42.   
  43.         if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {  
  44.             // If nobody has evaluated the drawable state yet, then do it now.  
  45.             refreshDrawableState();  
  46.         }  
  47.         needGlobalAttributesUpdate(false);  
  48.     }  
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

  1. private void handleDestroyActivity(IBinder token, boolean finishing,  
  2.         int configChanges, boolean getNonConfigInstance) {  
  3.     ActivityClientRecord r = performDestroyActivity(token, finishing,  
  4.             configChanges, getNonConfigInstance);  
  5.     if (r != null) {  
  6.         cleanUpPendingRemoveWindows(r);  
  7.         WindowManager wm = r.activity.getWindowManager();  
  8.         View v = r.activity.mDecor;  
  9.         if (v != null) {  
  10.             if (r.activity.mVisibleFromServer) {  
  11.                 mNumVisibleActivities–;  
  12.             }  
  13.             IBinder wtoken = v.getWindowToken();  
  14.             if (r.activity.mWindowAdded) {  
  15.                 if (r.onlyLocalRequest) {  
  16.                     // Hold off on removing this until the new activity’s  
  17.                     // window is being added.  
  18.                     r.mPendingRemoveWindow = v;  
  19.                     r.mPendingRemoveWindowManager = wm;  
  20.                 } else {  
  21.                     wm.removeViewImmediate(v);//看这里,看这里  
  22.                 }  
  23.             }  
  24.             if (wtoken != null && r.mPendingRemoveWindow == null) {  
  25.                 WindowManagerGlobal.getInstance().closeAll(wtoken,  
  26.                         r.activity.getClass().getName(), ”Activity”);  
  27.             }  
  28.             r.activity.mDecor = null;  
  29.         }  
  30.         if (r.mPendingRemoveWindow == null) {  
  31.             // If we are delaying the removal of the activity window, then  
  32.             // we can’t clean up all windows here.  Note that we can’t do  
  33.             // so later either, which means any windows that aren’t closed  
  34.             // by the app will leak.  Well we try to warning them a lot  
  35.             // about leaking windows, because that is a bug, so if they are  
  36.             // using this recreate facility then they get to live with leaks.  
  37.             WindowManagerGlobal.getInstance().closeAll(token,  
  38.                     r.activity.getClass().getName(), ”Activity”);  
  39.         }  
  40.   
  41.         // Mocked out contexts won’t be participating in the normal  
  42.         // process lifecycle, but if we’re running with a proper  
  43.         // ApplicationContext we need to have it tear down things  
  44.         // cleanly.  
  45.         Context c = r.activity.getBaseContext();  
  46.         if (c instanceof ContextImpl) {  
  47.             ((ContextImpl) c).scheduleFinalCleanup(  
  48.                     r.activity.getClass().getName(), ”Activity”);  
  49.         }  
  50.     }  
  51.     if (finishing) {  
  52.         try {  
  53.             ActivityManagerNative.getDefault().activityDestroyed(token);  
  54.         } catch (RemoteException ex) {  
  55.             // If the system process has died, it’s game over for everyone.  
  56.         }  
  57.     }  
  58.     mSomeActivitiesChanged = true;  
  59. }  
    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

  1. @Override  
  2. public void removeViewImmediate(View view) {  
  3.     mGlobal.removeView(view, true);  
  4. }  
    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }
好熟悉啊,还是走到了WindowManagerGlobal类中的removeView,代码如下:
WindowManagerGlobal.java
  1. public void removeView(View view, boolean immediate) {  
  2.     if (view == null) {  
  3.         throw new IllegalArgumentException(“view must not be null”);  
  4.     }  
  5.   
  6.     synchronized (mLock) {  
  7.         int index = findViewLocked(view, true);  
  8.         View curView = mRoots.get(index).getView();  
  9.         removeViewLocked(index, immediate);  
  10.         if (curView == view) {  
  11.             return;  
  12.         }  
  13.   
  14.         throw new IllegalStateException(“Calling with view ” + view  
  15.                 + ” but the ViewAncestor is attached to ” + curView);  
  16.     }  
  17. }  
  18. private void removeViewLocked(int index, boolean immediate) {  
  19.     ViewRootImpl root = mRoots.get(index);  
  20.     View view = root.getView();  
  21.   
  22.     if (view != null) {  
  23.         InputMethodManager imm = InputMethodManager.getInstance();  
  24.         if (imm != null) {  
  25.             imm.windowDismissed(mViews.get(index).getWindowToken());  
  26.         }  
  27.     }  
  28.     boolean deferred = root.die(immediate);  
  29.     if (view != null) {  
  30.         view.assignParent(null);  
  31.         if (deferred) {  
  32.             mDyingViews.add(view);  
  33.         }  
  34.     }  
  35. }  
    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

  1. /** 
  2.      * @param immediate True, do now if not in traversal. False, put on queue and do later. 
  3.      * @return True, request has been queued. False, request has been completed. 
  4.      */  
  5.     boolean die(boolean immediate) {  
  6.         // Make sure we do execute immediately if we are in the middle of a traversal or the damage  
  7.         // done by dispatchDetachedFromWindow will cause havoc on return.  
  8.         if (immediate && !mIsInTraversal) {  
  9.             doDie();  
  10.             return false;  
  11.         }  
  12.   
  13.         if (!mIsDrawing) {  
  14.             destroyHardwareRenderer();  
  15.         } else {  
  16.             Log.e(TAG, ”Attempting to destroy the window while drawing!\n” +  
  17.                     ”  window=” + this + “, title=” + mWindowAttributes.getTitle());  
  18.         }  
  19.         mHandler.sendEmptyMessage(MSG_DIE);  
  20.         return true;  
  21.     }  
  22.   
  23.     void doDie() {  
  24.         checkThread();  
  25.         if (LOCAL_LOGV) Log.v(TAG, “DIE in ” + this + “ of ” + mSurface);  
  26.         synchronized (this) {  
  27.             if (mRemoved) {  
  28.                 return;  
  29.             }  
  30.             mRemoved = true;  
  31.             if (mAdded) {  
  32.                 dispatchDetachedFromWindow();//看这里,看这里  
  33.             }  
  34.   
  35.             if (mAdded && !mFirst) {  
  36.                 destroyHardwareRenderer();  
  37.   
  38.                 if (mView != null) {  
  39.                     int viewVisibility = mView.getVisibility();  
  40.                     boolean viewVisibilityChanged = mViewVisibility != viewVisibility;  
  41.                     if (mWindowAttributesChanged || viewVisibilityChanged) {  
  42.                         // If layout params have been changed, first give them  
  43.                         // to the window manager to make sure it has the correct  
  44.                         // animation info.  
  45.                         try {  
  46.                             if ((relayoutWindow(mWindowAttributes, viewVisibility, false)  
  47.                                     & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {  
  48.                                 mWindowSession.finishDrawing(mWindow);  
  49.                             }  
  50.                         } catch (RemoteException e) {  
  51.                         }  
  52.                     }  
  53.   
  54.                     mSurface.release();  
  55.                 }  
  56.             }  
  57.   
  58.             mAdded = false;  
  59.         }  
  60.         WindowManagerGlobal.getInstance().doRemoveView(this);  
  61.     }  
/**
     * @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

  1. void dispatchDetachedFromWindow() {  
  2.         if (mView != null && mView.mAttachInfo != null) {  
  3.             mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);  
  4.             mView.dispatchDetachedFromWindow();//看这里,看这里  
  5.         }  
  6.   
  7.         mAccessibilityInteractionConnectionManager.ensureNoConnection();  
  8.         mAccessibilityManager.removeAccessibilityStateChangeListener(  
  9.                 mAccessibilityInteractionConnectionManager);  
  10.         mAccessibilityManager.removeHighTextContrastStateChangeListener(  
  11.                 mHighContrastTextManager);  
  12.         removeSendWindowContentChangedCallback();  
  13.   
  14.         destroyHardwareRenderer();  
  15.   
  16.         setAccessibilityFocus(nullnull);  
  17.   
  18.         mView.assignParent(null);  
  19.         mView = null;  
  20.         mAttachInfo.mRootView = null;  
  21.   
  22.         mSurface.release();  
  23.   
  24.         if (mInputQueueCallback != null && mInputQueue != null) {  
  25.             mInputQueueCallback.onInputQueueDestroyed(mInputQueue);  
  26.             mInputQueue.dispose();  
  27.             mInputQueueCallback = null;  
  28.             mInputQueue = null;  
  29.         }  
  30.         if (mInputEventReceiver != null) {  
  31.             mInputEventReceiver.dispose();  
  32.             mInputEventReceiver = null;  
  33.         }  
  34.         try {  
  35.             mWindowSession.remove(mWindow);  
  36.         } catch (RemoteException e) {  
  37.         }  
  38.   
  39.         // Dispose the input channel after removing the window so the Window Manager  
  40.         // doesn’t interpret the input channel being closed as an abnormal termination.  
  41.         if (mInputChannel != null) {  
  42.             mInputChannel.dispose();  
  43.             mInputChannel = null;  
  44.         }  
  45.   
  46.         mDisplayManager.unregisterDisplayListener(mDisplayListener);  
  47.   
  48.         unscheduleTraversals();  
  49.     }  
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

  1. @Override  
  2. void dispatchDetachedFromWindow() {  
  3.     // If we still have a touch target, we are still in the process of  
  4.     // dispatching motion events to a child; we need to get rid of that  
  5.     // child to avoid dispatching events to it after the window is torn  
  6.     // down. To make sure we keep the child in a consistent state, we  
  7.     // first send it an ACTION_CANCEL motion event.  
  8.     cancelAndClearTouchTargets(null);  
  9.   
  10.     // Similarly, set ACTION_EXIT to all hover targets and clear them.  
  11.     exitHoverTargets();  
  12.   
  13.     // In case view is detached while transition is running  
  14.     mLayoutCalledWhileSuppressed = false;  
  15.   
  16.     // Tear down our drag tracking  
  17.     mDragNotifiedChildren = null;  
  18.     if (mCurrentDrag != null) {  
  19.         mCurrentDrag.recycle();  
  20.         mCurrentDrag = null;  
  21.     }  
  22.   
  23.     final int count = mChildrenCount;  
  24.     final View[] children = mChildren;  
  25.     for (int i = 0; i < count; i++) {  
  26.         children[i].dispatchDetachedFromWindow();//这里会先调子类的dispatchDetachedFromWindow  
  27.     }  
  28.     clearDisappearingChildren();  
  29.     final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();  
  30.     for (int i = 0; i < transientCount; ++i) {  
  31.         View view = mTransientViews.get(i);  
  32.         view.dispatchDetachedFromWindow();  
  33.     }  
  34.     super.dispatchDetachedFromWindow();//然后这里才调用自己的。  
  35. }  
    @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

  1. void dispatchDetachedFromWindow() {  
  2.         AttachInfo info = mAttachInfo;  
  3.         if (info != null) {  
  4.             int vis = info.mWindowVisibility;  
  5.             if (vis != GONE) {  
  6.                 onWindowVisibilityChanged(GONE);  
  7.             }  
  8.         }  
  9.   
  10.         onDetachedFromWindow();//绕了一大圈,还是找到你了。快看快看,揪出来了。  
  11.         onDetachedFromWindowInternal();  
  12.   
  13.         InputMethodManager imm = InputMethodManager.peekInstance();  
  14.         if (imm != null) {  
  15.             imm.onViewDetachedFromWindow(this);  
  16.         }  
  17.   
  18.         ListenerInfo li = mListenerInfo;  
  19.         final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =  
  20.                 li != null ? li.mOnAttachStateChangeListeners : null;  
  21.         if (listeners != null && listeners.size() > 0) {  
  22.             // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to  
  23.             // perform the dispatching. The iterator is a safe guard against listeners that  
  24.             // could mutate the list by calling the various add/remove methods. This prevents  
  25.             // the array from being modified while we iterate it.  
  26.             for (OnAttachStateChangeListener listener : listeners) {  
  27.                 listener.onViewDetachedFromWindow(this);  
  28.             }  
  29.         }  
  30.   
  31.         if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0) {  
  32.             mAttachInfo.mScrollContainers.remove(this);  
  33.             mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;  
  34.         }  
  35.   
  36.         mAttachInfo = null;  
  37.         if (mOverlay != null) {  
  38.             mOverlay.getOverlayView().dispatchDetachedFromWindow();  
  39.         }  
  40.     }  
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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值