viewrootimpl.java_Activity的显示之ViewRootImpl初探

本文详细探讨了Android应用中Activity显示的关键步骤,从WindowManager到ViewRootImpl的流程。通过分析WindowManager接口及其实现类WindowManagerImpl,揭示了如何通过WindowManagerGlobal将View添加到窗口中,并重点介绍了ViewRootImpl在控件树中的核心作用,它是控件测量、布局、绘制及事件处理的起点。下篇将继续深入剖析ViewRootImpl的工作流程。
摘要由CSDN通过智能技术生成

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,如果能给各位看官带来一丝启发或者帮助,那真是极好的。

前言

在上一篇文章中我们主要分析了android.app.ActivityThread的main函数以及setContentView。另外我们还稍微分析了一下我们自己的源码,通过WindowManager添加View。我们知道调用setContentView把我们自己的xml布局添加到了DecorView ID为ID_ANDROID_CONTENT的布局后,最终还是会调用WindowManager.addView把DecorView加入PhoneWindow。到这里呢,我们把流程梳理一下。还是上图:12ef4790d284813ba0b8e39f2dc88c61.png

相信读者根据上图再结合前面所讲的内容应该对Activity的创建和显示有了初步的认识。那么本章我们来继续讲Activity的显示。该注意的是本系列并不意在带领读者去看清每一步具体的源码。在前面的文章中我也很少贴出源码。本系列文章意在让读者对Android系统有个更整体的把握。我所写的每一章知识都有可能在实际工作中用到。就如前面所讲解的Android下的进程问题以及Activity的生命周期以及本章要讲解的View的五大过程的基础ViewRootImpl。而理解View的五大过程(一般文章里都是三大过程)以及View的事件体系是更好的去自定义View的基础。

WindowManager 与 ViewRootImpl

WindowManager.addView()源码解析

public interface WindowManager extends ViewManager {

//这里我们只列出了一部分函数,但是并没有addView、updateViewLayout、removeView这三个函数

public Display getDefaultDisplay();

public void removeViewImmediate(View view);

...

}

好吧,果然没有这么简单,WindowManager是个接口,而且在其方法中没有找到addView方法,那么我们只能看看ViewManager了

public interface ViewManager

{

public void addView(View view, ViewGroup.LayoutParams params);

public void updateViewLayout(View view, ViewGroup.LayoutParams params);

public void removeView(View view);

}

还好是找到了,ViewManager没有再继承其他接口了。(要不然真不知道要找到什么时候去。)

既然WindowManager是个接口,那肯定要找它的实现类了。(在这里安利一个比较简单的方法,在Android Studio中)

2129f2f3919db3d4a624f8fda66d91aa.png

public final class WindowManagerImpl implements WindowManager {

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

private final Context mContext;

private final Window mParentWindow;

private IBinder mDefaultToken;

public WindowManagerImpl(Context context) {

this(context, null);

}

private WindowManagerImpl(Context context, Window parentWindow) {

mContext = context;

mParentWindow = parentWindow;

}

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {

return new WindowManagerImpl(mContext, parentWindow);

}

public WindowManagerImpl createPresentationWindowManager(Context displayContext) {

return new WindowManagerImpl(displayContext, 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, mContext.getDisplay(), 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 void requestAppKeyboardShortcuts(

final KeyboardShortcutsReceiver receiver, int deviceId) {

IResultReceiver resultReceiver = new IResultReceiver.Stub() {

@Override

public void send(int resultCode, Bundle resultData) throws RemoteException {

List result =

resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);

receiver.onKeyboardShortcutsReceived(result);

}

};

try {

WindowManagerGlobal.getWindowManagerService()

.requestAppKeyboardShortcuts(resultReceiver, deviceId);

} catch (RemoteException e) {

}

}

@Override

public Display getDefaultDisplay() {

return mContext.getDisplay();

}

}

WindowManagerImpl的源码如上所示,我们可以看到WindowManagerImpl的addView方法,WindowManagerImpl把工作交给了WindowManagerGlobal

WindowManagerGlobal

/**

WindowManagerGlobal 源码比较长,这里我们只列出了一部分

*/

public final class WindowManagerGlobal {

private WindowManagerGlobal() {

}

public static void initialize() {

getWindowManagerService();

}

public static WindowManagerGlobal getInstance() {

synchronized (WindowManagerGlobal.class) {

if (sDefaultWindowManager == null) {

sDefaultWindowManager = new WindowManagerGlobal();

}

return sDefaultWindowManager;

}

}

public static IWindowManager getWindowManagerService() {

synchronized (WindowManagerGlobal.class) {

if (sWindowManagerService == null) {

sWindowManagerService = IWindowManager.Stub.asInterface(

ServiceManager.getService("window"));

try {

if (sWindowManagerService != null) {

ValueAnimator.setDurationScale(

sWindowManagerService.getCurrentAnimatorScale());

}

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

return sWindowManagerService;

}

}

public static IWindowSession getWindowSession() {

synchronized (WindowManagerGlobal.class) {

if (sWindowSession == null) {

try {

InputMethodManager imm = InputMethodManager.getInstance();

IWindowManager windowManager = getWindowManagerService();

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;

}

}

public static IWindowSession peekWindowSession() {

synchronized (WindowManagerGlobal.class) {

return sWindowSession;

}

}

//addView方法

public void addView(View view, ViewGroup.LayoutParams params,

Display display, Window parentWindow) {

... //参数检查

final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

if (parentWindow != null) {

//① 如果当前窗口需要被添加为另一个窗口的附属窗口(子窗口),则需要父窗口视自己的情况对当前窗口的布局参数进行调整

parentWindow.adjustLayoutParamsForSubWindow(wparams);

}

ViewRootImpl root;

View panelParentView = null;

int index = findViewLocked(view, false);

if (index >= 0) {

if (mDyingViews.contains(view)) {

mRoots.get(index).doDie();

} else {

//同一个View不允许被添加2次

throw new IllegalStateException("View " + view

+ " has already been added to the window manager.");

}

}

//② 创建一个ViewRootImpl对象并保存在root变量中

root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);

//③ 保存作为窗口的控件、布局参数以及新建的ViewRootImpl

mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

// do this last because it fires off messages to start doing things

try {

// ④ 将作为窗口的控件设置给ViewRootImpl.这个动作将导致ViewRootImpl向WMS添加新的窗口、申请Surface以及托管控件在Surface上的重绘工作。这才是真正意义上完成了窗口的添加工作。

root.setView(view, wparams, panelParentView);

} catch (RuntimeException e) {

// BadTokenException or InvalidDisplayException, clean up.

if (index >= 0) {

removeViewLocked(index, true);

}

throw e;

}

}

}

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {

if (view == null) {

throw new IllegalArgumentException("view must not be null");

}

if (!(params instanceof WindowManager.LayoutParams)) {

throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");

}

final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

view.setLayoutParams(wparams);

synchronized (mLock) {

int index = findViewLocked(view, true);

ViewRootImpl root = mRoots.get(index);

mParams.remove(index);

mParams.add(index, wparams);

root.setLayoutParams(wparams, false);

}

}

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);

}

}

}

我们可以看到WindowManagerGlobal的私有构造函数以及getInstance()这个熟悉的静态方法名字。可以看出WindowManagerGlobal是个典型的单例。

WindowManagerGlobal 的addView方法并不复杂,其主要的关键点我们已经标注并写了注释。也就是说WindowManagerGlobal的职责如下:

同意管理整个进程中所有窗口的信息。包括控件、布局参数以及ViewRootImpl这三个元素。(这一点从第③个注释可以看出)

WindowManagerGlobal将窗口的创建、销毁、布局更新等任务交给了ViewRootImpl完成。

本篇总结

本篇文章分析了WindowManager的addView的过程,WindowManager是个接口,它的实现类是WindowManagerImpl类,而WindowManagerImpl又把相关逻辑交给了WindowManagerGlobal处理。WindowManagerGlobal是个单例类,它在进程中只存在一个实例,是它内部的addView方法最终创建了我们的核心类ViewRootImpl。ViewRootImpl实现了ViewParent接口,作为整个控件树的根部,它是控件树正常运作的动力所在,控件的测量、布局、绘制以及输入事件的派发处理窦世友ViewRootImpl出发。它是WindowManagerGlobal的实际工作者。

下篇预告

在下一篇文章中我们将深入介绍ViewRootImpl的工作流程。测量、布局、以及绘制。

参考博文

此致,敬礼

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值