乐趣区

关于android:Android中的ViewTreeObserver分析一

PS:本文系转载文章,浏览原文可读性会更好,文章开端有原文链接

ps:源码是基于 android api 27 来剖析的

在 Android 中,想要获取 View 的一些状态,比如说 View 的宽高产生了扭转,能够应用 ViewTreeObserver 外部接口 OnGlobalLayoutListener 进行注册监听,见名知意,一看名字就晓得它是 ViewTree 的观察者,一个界面蕴含多个 View 造成 ViewTree 的树状构造;ViewTreeObserver 不能间接实例化,只能通过 View 的 getViewTreeObserver 办法获取,对于做 Android 开发的咱们来说,常常接触到 View,所以说学习了解好 ViewTreeObserver 是很有必要的。

ViewTreeObserver 的外部有很多的事件监听接口,蕴含 View 的布局产生扭转、窗口焦点发生变化等监听事件,咱们来看看与 View 相干罕用的 ViewTreeObserver 的外部接口申明;

public interface OnWindowAttachListener {public void onWindowDetached();
}

public interface OnWindowFocusChangeListener {public void onWindowFocusChanged(boolean hasFocus);
}

public interface OnGlobalFocusChangeListener {public void onGlobalFocusChanged(View oldFocus, View newFocus);
}

public interface OnGlobalLayoutListener {public void onGlobalLayout();
}

public interface OnPreDrawListener {public boolean onPreDraw();
}

public interface OnDrawListener {public void onDraw();
}

public interface OnTouchModeChangeListener {public void onTouchModeChanged(boolean isInTouchMode);
}

public interface OnScrollChangedListener {public void onScrollChanged();
}

(1)OnWindowAttachListener 接口:当视图层次结构关联到窗口或与之拆散时回调。

(2)OnWindowFocusChangeListener 接口:当视图层次结构的窗口焦点状态发生变化时回调。

(3)OnGlobalFocusChangeListener 接口:当视图树中的焦点状态更改时回调。

(4)OnGlobalLayoutListener 接口:当全局布局状态或视图树中视图的可见性更改时回调。

(5)OnPreDrawListener 接口:当视图行将绘制时回调。

(6)OnDrawListener 接口:当视图树行将绘制时。

(7)OnTouchModeChangeListener 接口:当触摸模式扭转时回调。

(8)OnScrollChangedListener 接口:当视图树中的某些内容被滚动时回调。

咱们来看一下获取 ViewTreeObserver 对象的办法 getViewTreeObserver,它在 View 类中;

public ViewTreeObserver getViewTreeObserver() {

    if (mAttachInfo != null) {

        //1、return mAttachInfo.mTreeObserver;
    }
    if (mFloatingTreeObserver == null) {

        //2、mFloatingTreeObserver = new ViewTreeObserver(mContext);
    }
    return mFloatingTreeObserver;

}

正文 1 中的 ViewTreeObserver 对象是 ViewRootImpl 提供的;正文 2 中的 ViewTreeObserver 对象是以后 View 创立的;View 的测量、布局和绘制入口我就不讲了,能够看对 Android 中 View 的 post 办法进行摸索这篇文章,咱们看一下 View 的测量、布局和绘制入口会调用 View 的 dispatchAttachedToWindow 办法;

void dispatchAttachedToWindow(AttachInfo info, int visibility) {

    //3、mAttachInfo = info;
    ......
    if (mFloatingTreeObserver != null) {

        //4、info.mTreeObserver.merge(mFloatingTreeObserver);
        mFloatingTreeObserver = null;
    }
    ......

}

正文 3 中的 mAttachInfo 关联的是 ViewRootImpl 中的 View.AttachInfo 对象,同一个 View Hierarchy 内所有的 View 中的 mAttachInfo 关联的对象都是 ViewRootImpl 中的 View.AttachInfo 对象;正文 4 中的 info.mTreeObserver 是 ViewTreeObserver 对象,正文 4 中代码的作用是将以后 View 的 ViewTreeObserver 对象外面的一些接口合并到 ViewRootImpl 中的 View.AttachInfo 对象外面的 ViewTreeObserver,咱们看看 ViewTreeObserver 的 merge 办法具体实现;

void merge(ViewTreeObserver observer) {

    if (observer.mOnWindowAttachListeners != null) {if (mOnWindowAttachListeners != null) {mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
        } else {mOnWindowAttachListeners = observer.mOnWindowAttachListeners;}
    }

    if (observer.mOnWindowFocusListeners != null) {if (mOnWindowFocusListeners != null) {mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
        } else {mOnWindowFocusListeners = observer.mOnWindowFocusListeners;}
    }

    if (observer.mOnGlobalFocusListeners != null) {if (mOnGlobalFocusListeners != null) {mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
        } else {mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;}
    }

    if (observer.mOnGlobalLayoutListeners != null) {if (mOnGlobalLayoutListeners != null) {mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
        } else {mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;}
    }

    if (observer.mOnPreDrawListeners != null) {if (mOnPreDrawListeners != null) {mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
        } else {mOnPreDrawListeners = observer.mOnPreDrawListeners;}
    }

    if (observer.mOnDrawListeners != null) {if (mOnDrawListeners != null) {mOnDrawListeners.addAll(observer.mOnDrawListeners);
        } else {mOnDrawListeners = observer.mOnDrawListeners;}
    }

    if (observer.mOnTouchModeChangeListeners != null) {if (mOnTouchModeChangeListeners != null) {mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
        } else {mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;}
    }

    if (observer.mOnComputeInternalInsetsListeners != null) {if (mOnComputeInternalInsetsListeners != null) {mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
        } else {mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;}
    }

    if (observer.mOnScrollChangedListeners != null) {if (mOnScrollChangedListeners != null) {mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners);
        } else {mOnScrollChangedListeners = observer.mOnScrollChangedListeners;}
    }

    if (observer.mOnWindowShownListeners != null) {if (mOnWindowShownListeners != null) {mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
        } else {mOnWindowShownListeners = observer.mOnWindowShownListeners;}
    }

    observer.kill();

}

看到没,一开始先判断以后 View 的 ViewTreeObserver(observer)对象里的相应 Listeners 汇合如果不为空时,而后再判断以后的 ViewTreeObserver 对象(ViewRootImpl 中的 View.AttachInfo 对象外面的 ViewTreeObserver)的相应 Listeners 汇合是否为空,如果以后的 ViewTreeObserver 对象的相应 Listeners 汇合不为空时,就将以后 View 的 ViewTreeObserver 对象里的相应 Listeners 汇合全副增加到以后 ViewTreeObserver 对象的相应 Listeners 汇合,否则将以后 ViewTreeObserver 对象的相应 Listeners 汇合指向以后 View 的 ViewTreeObserver 对象里的相应 Listeners 汇合。

1、OnWindowAttachListener 的触发机会

View 的测量、布局和绘制开始入口是从 ViewRootImpl 的 setView 办法开始的,能够看一下对 Android 中 View 的 post 办法进行摸索这篇文章,ViewRootImpl 的 setView 办法会间接调用 ViewRootImpl 的 performTraversals 办法;

private void performTraversals() {

    ......
    if (mFirst) {
        ......
        //5、mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
        ......
    } else {......}
    ......
    ......

}

ViewRootImpl 的 performTraversals 办法调用了正文 5 中的代码,也就是 ViewTreeObserver 的 dispatchOnWindowAttachedChange 办法,咱们往下看;

final void dispatchOnWindowAttachedChange(boolean attached) {

    // 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.
    final CopyOnWriteArrayList<ViewTreeObserver.OnWindowAttachListener> listeners
            = mOnWindowAttachListeners;
    if (listeners != null && listeners.size() > 0) {for (ViewTreeObserver.OnWindowAttachListener listener : listeners) {
            
            //6、if (attached) listener.onWindowAttached();
            
            //7、else listener.onWindowDetached();}
    }

}

因为正文 5 中传入的 boolean 值为 true,所以会调用正文 6 中的 OnWindowAttachListener 的 onWindowAttached 办法,正文 6 的办法示意视图层次结构附加到窗口时的回调办法;正文 7 示意当视图层次结构从窗口拆散时调用的回调办法,它什么时候被调用呢?它是从 Activity 的 onDestroy 办法执行完后被调用的,咱们看一下 ActivityThread 的 handleDestroyActivity 办法;

private void handleDestroyActivity(IBinder token, boolean finishing,

                                   int configChanges, boolean getNonConfigInstance) {

    //8、ActivityClientRecord r = performDestroyActivity(token, finishing,
            configChanges, getNonConfigInstance);
    if (r != null) {
        ......
        if (v != null) {
            ......
            if (r.activity.mWindowAdded) {if (r.mPreserveWindow) {......} else {
                    
                    //9、wm.removeViewImmediate(v);
                }
            }
            ......
        }
       ......
    }
    ......

}

正文 8 中的 performDestroyActivity 办法最终调用 Activity 的 onDestroy 办法,有趣味的读者能够跟踪进去看一下;正文 9 中的 wm 对象的实现类是 WindowManagerImpl,咱们来看一下 WindowManagerImpl 的 removeViewImmediate 办法;

@Override
public void removeViewImmediate(View view) {
    //10、mGlobal.removeView(view, true);
}

正文 10 中的 mGlobal 是 WindowManagerGlobal 类型的对象,咱们得去 WindowManagerGlobal 的 removeView 办法看看;

public void removeView(View view, boolean immediate) {

    ......
    synchronized (mLock) {int index = findViewLocked(view, true);
        ......
        //11、removeViewLocked(index, immediate);
        ......
    }

}

咱们往下看一下正文 11 的代码,也就是 WindowManagerGlobal 的 removeViewLocked 办法;

private void removeViewLocked(int index, boolean immediate) {

    ViewRootImpl root = mRoots.get(index);
    ......
    //12、boolean deferred = root.die(immediate);
    ......

}

看正文 12,WindowManagerGlobal 的 removeViewLocked 办法调用了 ViewRootImpl 的 die 办法;

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) {
        //13、doDie();
        return false;
    }
    ......
    return true;

}

看正文 13,ViewRootImpl 的 die 办法调用了 ViewRootImpl 的 doDie 办法;

void doDie() {

    ......
    synchronized (this) {
        ......
        if (mAdded) {
            //14、dispatchDetachedFromWindow();}
        ......
    }
    ......

}

看正文 14,ViewRootImpl 的 doDie 办法调用了 ViewRootImpl 的 dispatchDetachedFromWindow 办法;

void dispatchDetachedFromWindow() {

    if (mView != null && mView.mAttachInfo != null) {
        //15、mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
        ......
    }
    ......

}

看正文 15,ViewRootImpl 的 dispatchDetachedFromWindow 办法调用了 ViewTreeObserver 的 dispatchOnWindowAttachedChange 办法,传入的参数是 false,所以 ViewTreeObserver 的 dispatchOnWindowAttachedChange 办法最终调用的是 OnWindowAttachListener 接口的 onWindowDetached 办法,所以 OnWindowAttachListener 接口的 onWindowDetached 办法的调用机会是在 Activity 的 onDestroy 办法之后。

2、OnWindowFocusChangeListener 的触发机会

OnWindowFocusChangeListener 的触发机会是在 View 的绘制完之后,View 的绘制入口的跟踪过程能够看对 Android 中 View 的 post 办法进行摸索这篇文章,View 的绘制入口会调用 ViewRootImpl 的 setView 办法;

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

    synchronized (this) {if (mView == null) {
            ......
            // 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.
            //16、requestLayout();
            ......
            try {
                ......
                //17、res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } catch (RemoteException e) {......} finally {......}
            ......
        }
    }

}

正文 16 的 requestLayout 办法会进行 View 的测量、布局和绘制操作;正文 17 中的 mWindowSession 是 IWindowSession 类型,然而它的实现类是什么呢?从 ViewRootImpl 的构造方法能够看出,mWindowSession 的值是通过 WindowManagerGlobal 的 getWindowSession 办法失去的,咱们看一下 WindowManagerGlobal 的 getWindowSession 办法;

public static IWindowSession getWindowSession() {

    synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {
            try {
                ......
                IWindowManager windowManager = getWindowManagerService();
                
                //18、sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);
                            }
                        },
                        imm.getClient(), imm.getInputContext());
            } catch (RemoteException e) {throw e.rethrowFromSystemServer();
            }
        }
        return sWindowSession;
    }

}

咱们看到正文 18 中的 windowManager 尽管是 IWindowManager 类型的,但它的实现类是 WindowManagerService,所以咱们看一下 WindowManagerService 的 openSession 办法;

@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
                                  IInputContext inputContext) {if (client == null) throw new IllegalArgumentException("null client");
    if (inputContext == null) throw new IllegalArgumentException("null inputContext");
    Session session = new Session(this, callback, client, inputContext);
    
    //19、return session;
}

看到正文 19 没,返回的是 Session 类型的对象,所以 ViewRootImpl 的 mWindowSession 对象的实现类是 Session,咱们看回正文 17 的办法,也就是 Session 的 addToDisplay 办法;

@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
                        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
                        Rect outOutsets, InputChannel outInputChannel) {

    //20、return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

正文 20 中 mService 是 WindowManagerService,咱们看看 WindowManagerService 的 addWindow 办法;

public int addWindow(Session session, IWindow client, int seq,

                     WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
                     Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
                     InputChannel outInputChannel) {
    ......
    synchronized(mWindowMap) {
        ......
        if (win.canReceiveKeys()) {
            //21、focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                    false /*updateInputWindows*/);
            ......
        }
        ......
    }
    ......
    return res;

}

从正文 21 能够看出,WindowManagerService 的 addWindow 办法调用了 WindowManagerService 的 updateFocusedWindowLocked 办法;

boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {

    WindowState newFocus = mRoot.computeFocusedWindow();
    if (mCurrentFocus != newFocus) {
        ......
        // This check makes sure that we don't already have the focus
        // change message pending.
        mH.removeMessages(H.REPORT_FOCUS_CHANGE);
        
        //22、mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
        ......
        final WindowState oldFocus = mCurrentFocus;
        mCurrentFocus = newFocus;
        ......
        return true;
    }
    return false;

}

从正文 22 看出,WindowManagerService 的 updateFocusedWindowLocked 办法发送一条音讯给外部类 H,咱们来看看 H 是怎么做相应的解决的;

final class H extends android.os.Handler {

    ......
    @Override
    public void handleMessage(Message msg) {
        ......
        switch (msg.what) {
            case REPORT_FOCUS_CHANGE: {
                WindowState lastFocus;
                WindowState newFocus;
                ......
                //System.out.println("Changing focus from" + lastFocus
                //                   + "to" + newFocus);
                if (newFocus != null) {if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus:" + newFocus);
                    
                    //23、newFocus.reportFocusChangedSerialized(true, mInTouchMode);
                    notifyFocusChanged();}
                ......
            }
            break;
        }
    }

}

从正文 23 看出,WindowManagerService 的外部类 H 调用了 WindowState 的 reportFocusChangedSerialized 办法;

void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {

    try {
        //24、mClient.windowFocusChanged(focused, inTouchMode);
    } catch (RemoteException e) { }
    ......

}

正文 24 的 mClient 的类型是 IWindow,但它的实现类是 ViewRootImpl 的外部类 W,WindowState 的 reportFocusChangedSerialized 办法调用了 W 的 windowFocusChanged 办法;

static class W extends IWindow.Stub {

    ......
    @Override
    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {final ViewRootImpl viewAncestor = mViewAncestor.get();
        if (viewAncestor != null) {
            //25、viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
        }
    }
    ......

}

从正文 25 中看出,W 的 windowFocusChanged 办法调用了 ViewRootImpl 的 windowFocusChanged 办法;

public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {

    Message msg = Message.obtain();
    msg.what = MSG_WINDOW_FOCUS_CHANGED;
    msg.arg1 = hasFocus ? 1 : 0;
    msg.arg2 = inTouchMode ? 1 : 0;
    //26、mHandler.sendMessage(msg);

}

从正文 26 看出,ViewRootImpl 的 windowFocusChanged 办法只是插入了一条音讯,咱们看看 ViewRootImpl 的外部类 ViewRootHandler 对此音讯相应的解决;

final class ViewRootHandler extends Handler {

    ......
    @Override
    public void handleMessage(Message msg) {switch (msg.what) {
           ......
            case MSG_WINDOW_FOCUS_CHANGED: {if (mAdded) {
                    ......
                    if (mView != null) {
                        ......
                        //27、mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
                        ......
                    }
                    ......
                }
            } break;
            ......
        }
    }

}

看正文 27,ViewRootHandler 对 ViewRootImpl 的 windowFocusChanged 办法发送过去的音讯是这样解决的,mView(实际上是 DecorView) 不为空,那么就调用 ViewTreeObserver 的 dispatchOnWindowFocusChange 办法,咱们往下看;

final void dispatchOnWindowFocusChange(boolean hasFocus) {

    // 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.
    final CopyOnWriteArrayList<ViewTreeObserver.OnWindowFocusChangeListener> listeners
            = mOnWindowFocusListeners;
    if (listeners != null && listeners.size() > 0) {for (ViewTreeObserver.OnWindowFocusChangeListener listener : listeners) {

            //28、listener.onWindowFocusChanged(hasFocus);
        }
    }

}

从正文 28 能够看出,ViewTreeObserver 的 dispatchOnWindowFocusChange 办法调用了 OnWindowFocusChangeListener 接口的 onWindowFocusChanged 办法,所以从以上跟踪的代码过程得出 OnWindowFocusChangeListener 的触发机会是在 View 的测量、布局和绘制之后。

这篇文章写到这里先告一段落了,但还没完,下一篇会持续剖析这篇文章列举进去的但又没剖析触发机会的接口。

退出移动版