新乡彩票网站建设,项目外包和人力外包哪个好,专门做电脑壁纸网站,wordpress 地图前言#xff1a;
View绘制流程中#xff0c;主要流程是这样的#xff1a;
1.用户进入页面#xff0c;首先创建和绑定Window#xff1b;
2.首次创建以及后续vsync信号来临时#xff0c;会请求执行刷新流程#xff1b;
3.刷新流程完成后#xff0c;会通知SurfaceFlin…前言
View绘制流程中主要流程是这样的
1.用户进入页面首先创建和绑定Window
2.首次创建以及后续vsync信号来临时会请求执行刷新流程
3.刷新流程完成后会通知SurfaceFlinger读取数据以及刷新页面。
本篇就是大流程中的第一个环节重点讲解进入页面后Window是如何创建以及绑定到系统侧的。 本文的流程主要分为以下三大块 1.APP侧window和布局的创建流程
2.APP侧window是如何绑定ViewRootImpl以及注册到系统侧的
3.系统侧接收到window后是如何处理的。 一.APP侧Window和View创建
1.1 创建Window
Activity启动时会经历performLaunchActivity和handleResumeActivity的流程而window的创建以及decorView的创建就是在launch的过程中。
我们首先看一下ActivityThread.performLaunchActivity中的代码
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...//Activity的创建Activity activity null;activity mInstrumentation.newActivity(cl, component.getClassName(), r.intent);//Activity的关联activity.attach();//执行Activity的onCreate流程mInstrumentation.callActivityOnCreate()...
}
我们看一下activity.attach中实现的相关内容
//android.app.Activity
final void attach(){//1mWindow new PhoneWindow();//3mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),...);mWindow.setColorMode(info.colorMode);
}//com.android.internal.policy.PhoneWindow.java
public PhoneWindow(UiContext Context context) {public PhoneWindow(UiContext Context context) {super(context);mLayoutInflater LayoutInflater.from(context);}
}主要执行了以下的逻辑
1.创建了Activity所绑定的Window成员名为mWindow类型为PhoneWindow。
2.在PhoneWindow中mLayoutInflater赋值。我们的布局就是通过mLayoutInflater对象去解析的。
3.给Window对象绑定WindowManager这个WindowManager实际上是WindowManagerImpl。
所以此时Activity中的mWindow以及PhoneWindow中的mWindowManager和mLayoutInflater都已经有值了。 1.2 DecorView和ContentParent创建
接下来我们看下callActivityOnCreate的流程。Activity.onCreate流程没有什么有关window的逻辑但是一般我们都会在onCreate中调用setContentView这个方法中却大有玄机我们一起看一下
//android.app.Window.java
public void setContentView(LayoutRes int layoutResID) {//1getWindow().setContentView(layoutResID);
}//com.android.internal.policy.PhoneWindow
public void setContentView(int layoutResID) {if (mContentParent null) {//2installDecor();}...//3mLayoutInflater.inflate(layoutResID, mContentParent);
}private void installDecor() {if(mDecor null){mDecor generateDecor(-1);}if (mContentParent null) {mContentParent generateLayout(mDecor);}
}
主要执行了以下的逻辑
1.调用Activity中所持有window去加载layout。
2.首次的时候通过installDecor方法去创建根布局DecorView以及容器布局mContentParent。mContentParent是Activity上所有的View的父容器。
3.通过mLayoutInflater对象去解析生成布局对象并且关联到mContentParent上。
具体的解析逻辑不是本文的核心这里就不去细讲了。 1.3 小结
至此Activity的创建和其onCreate的流程已经结束此时Activity中的成员变量mWindowManager和mWindow对象已经完成了赋值总结一下如下图所示 二.APP侧Window注册
2.1 Activity和ActivityClientRecord中成员变量赋值
在第一章中Activity所对应的window及其中的布局创建完成了所以下一步就是需要把这个window向系统做一个绑定这个流程主要是在Activity的onResume周期中执行的。
首先我们仍然看一下resume周期所对应的代码如下
//ActivityThread.java
public void handleResumeActivity(ActivityClientRecord r, ...) {final Activity a r.activity;...if (r.window null !a.mFinished willBeVisible) {r.window r.activity.getWindow();...//1a.mDecor decor;//2l.type WindowManager.LayoutParams.TYPE_BASE_APPLICATION;//3wm.addView(decor, l);}
}主要执行了以下的逻辑
1.把window中的decor赋值给Activity中的mDecor
2.设置Window.LayoutParams的type类型为WindowManager.LayoutParams.TYPE_BASE_APPLICATION;在安卓中type决定window图层优先级值越大优先级越高部分图层优先级如下
public static final int TYPE_BASE_APPLICATION 1;//默认Activity对应的图层
public static final int FIRST_SYSTEM_WINDOW 2000;//系统弹窗的图层
public static final int TYPE_TOAST FIRST_SYSTEM_WINDOW5;//Toast的图层
public static final int TYPE_SYSTEM_OVERLAY FIRST_SYSTEM_WINDOW6;//悬浮窗的图层等级
public static final int TYPE_APPLICATION_OVERLAY FIRST_SYSTEM_WINDOW 38;//同上用于替代上面那个
3.通过windowManager添加decor。这里wm的对象实际上是WindowManagerImpl而其中的addView方法中又交给了WindowManagerGlobal来处理相关代码如下
//WindowManagerImpl.java
public void addView(NonNull View view, NonNull ViewGroup.LayoutParams params) {mGlobal.addView(view, params,...);
} 2.2 WindowManagerGlobal装载Window
接下来我们看一下WindowManagerGlobal.addView()中的逻辑。
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {ViewRootImpl root;View panelParentView null;...//1if (windowlessSession null) {root new ViewRootImpl(view.getContext(), display);} else {root new ViewRootImpl(view.getContext(), display, windowlessSession);}view.setLayoutParams(wparams);//2mViews.add(view);mRoots.add(root);mParams.add(wparams);//3root.setView(view, wparams, panelParentView, userId);
}
主要执行了以下的逻辑
1.创建ViewRootImplViewRootImpl的角色是页面刷新显示流程的执行者。
2.WindowManagerGlobal的角色是维护客户端所有的页面的所以自然而然的其中就维护了很多集合。比如存储所有根布局的mView对象等等这里就是往集合中注册的。
UnsupportedAppUsage
private final ArrayListView mViews new ArrayListView();
UnsupportedAppUsage
private final ArrayListViewRootImpl mRoots new ArrayListViewRootImpl();
UnsupportedAppUsage
private final ArrayListWindowManager.LayoutParams mParams new ArrayListWindowManager.LayoutParams();
3.上面说到ViewRootImpl是流程的具体执行者那么window的绑定自然也是交给其来处理。所以这里通过ViewRootImpl.setView方法来负责。 2.3 ViewRootImpl负责视图的绑定
接下来我们就看下ViewRootImpl的setView()方法。
ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {...if (mView null) {mView view;...//1requestLayout();//2InputChannel inputChannel null;if ((mWindowAttributes.inputFeatures WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) 0) {inputChannel new InputChannel();}//3res mWindowSession.addToDisplayAsUser(...);}//mInputEventReceiver new WindowInputEventReceiver(inputChannel,Looper.myLooper());
}这一块的逻辑也是较为清晰的
首先通过requestLayout方法尝试进行首次View绘制的完整流程虽然这时window还没有绑定上但是并不影响View流程的开始毕竟View流程中只有最后的绘制流程才需要和SurfaceFlinger进行交互。
然后生成InputChannel对象这个对象类似于一个回调通过后面的binder接口传递给系统侧。后面window上的点击事件就会通过InputChannel回调通知到应用侧。后面把inputChannel绑定到WindowInputEventReceiver中所以APP侧点击事件的来源就是其中的onInputEvent方法。
最后把相关的对象传递给系统侧完成注册。传递的内容如下
int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,in int viewVisibility, in int layerStackId, in int userId,in InsetsVisibilities requestedVisibilities, out InputChannel outInputChannel,out InsetsState insetsState, out InsetsSourceControl[] activeControls);
int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, in int userId, in InsetsVisibilities requestedVisibilities, out InputChannel outInputChannel, out InsetsState insetsState, out InsetsSourceControl[] activeControls);
整个流程如下图所示 给系统侧传递的数据列表如下 对象类型 成员变量名 解释 IWindow window 对应ViewRootImpl中的IWindow.Stub传递的一个binder对象 WindowManager.LayoutParams attrs window对应的layoutParams属性 int viewVisibility 根布局的显示状态 int layerStackId displayId显示区域的唯一ID int userId 应用的userId InsetsVisibilities requestedVisibilities InputChannel outInputChannel 事件分发流程中传递的通道 InsetsState insetsState InsetsSourceControl activeControls
三.系统侧Window绑定
介绍系统侧的流程前我们先对系统侧的几个核心类简单介绍下因为大多数的读者对于系统侧的了解较少。
3.1 核心类介绍 类名 功能介绍 com.android.server.wm.Session 一个session对应一个应用进程负责应用和系统之间的窗口注册/移除SurfaceSession注册等等。 SurfaceSession 这个类注册是native的实现。负责维护应用和surfaceFlinger之间的连接。所以APP刷新时是直接通知SF并不需要经过system_server。 WindowManagerService 顾名思义用于所有应用的窗口管理。这里只是维护窗口的关系并负责具体的渲染流程。 3.2 应用进程绑定唯一的IWindowSession
上一章有讲到一个应用会有一个维护所有的视图的容器WindowManagerGlobal那么它其中一定有一个负责和系统侧通信的对象这个对象就是IWindowSession。相关代码如下
//WindowManagerGlobal.java
public static IWindowSession getWindowSession() {if (sWindowSession null) {IWindowManager windowManager getWindowManagerService();sWindowSession windowManager.openSession(...);}
}//WindowManagerService.java
Override
public IWindowSession openSession(IWindowSessionCallback callback) {return new Session(this, callback);
}也就是说WindowManagerGlobal中只会持有一个sWindowSession对象而WindowManagerGlobal对应一个应用的进程所以IWindowSession是绑定唯一一个应用进程的。IWindowSession是一个binder的引用其在系统侧的具体实现是Session。上面的addToDisplayAsUser方法就是通过IWindowSession中提供的binder方法。 3.3 把window注册到系统侧
接下来我们就看一下第二中讲到的addToDisplayAsUser()方法它负责把应用侧的Window向系统侧注册。我们看一下其在系统侧的实现
//Session.java
class Session extends IWindowSession.Stub{public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {return mService.addWindow(this, ...);}
}
逻辑很简单直接交给WindowServiceManger的addWindow方法去处理接下来我们就看下这个方法
//WindowManagerService.java
public int addWindow(Session session, ...) {WindowState parentWindow null;...//1final DisplayContent displayContent getDisplayContentOrCreate(displayId, attrs.token);...//2WindowToken token displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);...if (token null) {if( hasParent ){token parentWindow.mToken;} else if (){token new WindowToken.Builder(this, binder, type)} else {token new WindowToken.Builder(this, binder, type)}...}...//3final WindowState win new WindowState(this, session, );...if (openInputChannels) {win.openInputChannel(outInputChannel);}...//4win.openInputChannel(outInputChannel);...//5win.attach();win.initAppOpsState();...win.mToken.addWindow(win);
}
首先根据displayId找到归属的DisplayContentDisplayContent的作用是用于跟踪一系列的WindowState
然后如果当前的window存在parent则去查询其parent的WindowToken。WindowToken顾名思义用于识别WindowState
接下来生成WindowState这里的WindowState和APP侧的Window是对应的WindowState就是在系统侧window的描述并负责和window进行通讯
然后绑定事件输入这里的outInputChannel就是APP侧传递过来的。
最后通过attch()方法完成绑定我们重点看一下这个方法
//WindowState.java
void attach() {if (DEBUG) Slog.v(TAG, Attaching this token mToken);mSession.windowAddedLocked();
}//Session.java
void windowAddedLocked() {if (mPackageName null) {mPackageName wpc.mInfo.packageName;}if (mSurfaceSession null) {mSurfaceSession new SurfaceSession();...mService.mSessions.add(this);}mNumWindow;
}
简单来说一个应用首次完成window.attch()的时候初始化mPackageName和mSurfaceSession()。
而mSurfaceSession对应的就是显示在前台的区域它初始化后对应的就是native创建surface以及后续和surfaceFlinger交互的流程了这个我们后面的文章来讲解。
最后使用mNumWindow记录Window的数量。 3.4 小结
我们仍然做一个小的总结window注册在系统侧的实现。其实就是接受一个客户端传递过来的binder引用对象IWindow然后生成一个唯一的对应对象WindowState。并且在应用进程级别生成一个SurfaceSession去维护应用的ViewRootImpl和surfaceFlinger的关系。流程图如下 四.总结
最后我们做一下总结整个window的注册流程主要分为三块大块
1.create流程主要是各种对象的初始化。流程中完成客户端window的创建以及mDecormContentParent等相关成员变量的初始化
2.resume流程主要是window关系的维护。所以创建视图处理类ViewRootImpl并且使用其把window向系统侧申请绑定
3.系统收到后主要是生成window在系统侧的对象并记录。所以分别创建Window组的对象WindowToken和Window的系统侧对象WindowState并保存。
整体流程图如下 五.扩展性问题
1.如果onCreate中不调用setContentView那么会执行后面的流程吗
答会的即使不调用setContentView只是不会有ContentView但是DecorView仍然会创建和绑定的只不过这时候展示的会是黑屏。