Android中的ViewTreeObserver分析(一)

PS:本文系转载文章,阅读原文可读性会更好些,原文链接:https://mp.weixin.qq.com/s/zXHJsr4XSfPTynMpzN_mLw

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 的测量、布局和绘制之后。

这篇文章写到这里先告一段落了,但还没完,下一篇会继续分析这篇文章列举出来的但又没分析触发时机的接口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值