本文旨在将Framework的框架描绘出来,主要是记录我一段时间关于android framework的学习,希望抛砖引玉,对于读者有一定的帮助。
前言
android framework与我们的开发息息相关,本文将从开机,即framework的初始化讲起,然后再涉及android运行过程中的几个使用场景。比如用户启动app(点击桌面图标后发生了什么),用户使用app(一次触摸,Android到底干了啥)。其中会涉及主线程、anr、handler、binder、zygote、app的入口是什么、自定义view为什么要三步走等一些我们经常接触的概念,并一一解答。涉及源码为api 27。
一、初始化篇
当按开机键的时候,设备首先执行BootLoader,BootLoader负责把Linux内核从加载到内存,并执行内核的初始化,最后内核将读取init.rc文件,并启动该文件中定义的各种服务程序。Android framework对于内核而言就是一个Linux程序而已,而该程序就在init.rc文件中被定义。Android framework的初始化过程由此开始。
首先被创建的是zygote进程,这是系统中运行的第一个Dalvik虚拟机程序,顾名思义,后面所有Dalvik虚拟机进程都是通过它“孵化”而来(学过生物的我们都知道,人体所有的细胞都是由受精卵分裂而来,所以本人觉得这个名称取得非常准确巧妙)。
zygote孵化出的第一个 Dalvik1 进程叫做 SystemServer,是Framework相当重要的进程。 SystemServer 仅仅是该进程的别名,而该进程具休对应的程序依然是 app\_process, 因为 SystemServer 是从 app\_process中孵化出来的。Ams、Wms、Pms等等都在此进程中创建,可以说SystemServer管理Framework所有的活动。
SystemServer 中创建了一个 Socket2 客户端,并有AmS负责管理该客户端,之后所有的 Dalvik 进程都将通过该 Socket 客户端间接被启动。当要启动新的 APK 进程时 ,AmS 中会通过该 Socket 客户端向 zygote 进程的 Socket服务端发送一个启动命令,然后zygote会孵化出新的进程。
1、zygote的启动
前面我们提到内核初始化时,会启动在init.rc文件中配置的程序,zygote相关的配置信息如下:
简单说明一下这个语句的意思,内核会执行/system/bin/app\_process3目录下的程序,启动一个叫zygote的服务。其中参数--start-system-server, 仅在指定 -- zygote 参数时才有效,意思是告知Zygotelnit启动完毕后孵化出第一个进程SystemServer。由此可知,zygote启动后做的事情之一就是启动SystemServer。
当 zygote 服务从 app\_process 开始启动后,会启动一个 Dalvik 虚拟机,而虚拟机执行的第一个 Java类就是 ZygoteInit.java。(app进程fork自zygote进程,所以ZygoteInit.main同样也是app的入口,只不过会根据进程的不同执行不同的逻辑。这就是有时候我们程序错误日志的调用栈里面可以看到"…ZygoteInit.main……"的原因。)ZygoteInit会做另外两件事:一是前面提到的,启动一个Socket服务端口,该Socket端口用于接收启动新进程的命令;二是预加载的Framework大部分类及资源供后续app使用。zygote fork app进程时,并不需要复制这一部分,而是使用共享内存的方式。
总结: zygote的进程启动后主要做了三件事:分别是启动一个Socket服务,用于接收启动新进程的命令、预加载的Framework大部分类及资源以及启动SystemServer。
2、SystemServer的启动
SystemServer是在zygote进程中最终调用到Zygote.forkSystemServer方法启动的。启动后会做一些初始的配置,比如关闭Socket服务端(非zygote进程不需要),配置SystemServer运行环境。然后调用SystemServer.main。
SystemServer启动后,主要做两件事:一是通过SystemServerManager启动各种服务线程,比如AMS、WMS、PMS等等,并将其注册到ServiceManager(AMS、WMS与app的运行息息相关,其具体内容后面再展开);二是启动HomeActivity,也就是启动launcher,launcher与普通app的启动大同小异,后面再详细介绍。
3、ServiceManager的启动
此处的ServiceManager不是java世界的,而是native世界的。它也是通过init.rc配置启动的,其功能相当于service4的DNS服务器。SystemServer启动的各个服务都会注册于其中,我们在使用binder进行跨进程调用时,首先回去查询ServiceManager获取到对应service的binder引用,然后再进行后续操作。这个过程与我们通过域名查询dns服务器获得ip最后访问网站类似。
注意:我们查看Framework代码时候会发现SystemServiceRegistry类,这个类和系统服务的注册没有半毛钱关系,它只不过是将查询各种service的工具类缓存起来。
二、运行时篇
我们在使用android设备时,就是Framework的运行时。下面我会从两个关键场景说起:第一个场景,点击桌面图标,这个场景会涉及到android的消息机制、app的启动、activity的创建、window的创建和view的绘制。第二个场景,我们在滑动屏幕或者点击按钮等等,这个场景会涉及到Framework怎么获得硬件的事件以及app的事件分发机制。
1、点击桌面图标后发生了什么
Activity的启动
我们都知道桌面程序也就是launcher和普通的app基本没什么差别,我们点击桌面图标其实调用了Activity的startActivity方法。Activity是Context的子类5,所以本来应该是调用了Context的startActivity方法,不过Activity重载了该方法,和Context区别是少了是否有Intent.FLAG\_ACTIVITY\_NEW\_TASK的判断。这也是为什么我们在非Activity的Context(比如Service)启动时要加Intent.FLAG\_ACTIVITY\_NEW\_TASK。不论是Activity还是Context,最终都会调用到Instrumentation的execStartActivity方法,然后通过Binder跨进程调用Ams的startActivity方法。
Ams会调用到startActivityAsUser方法,然后调用ActivityStarter的startActivityMayWait方法。
ActivityStarter.java
final int startActivityMayWait(...){
...
//根据intent通过PMS查找activity相关信息
//如何没有在AndroidManifest.xml注册过就无法找到activity
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
...
//见下方
int res = startActivityLocked(...);
...
}
int startActivityLocked(...){
...
//见下方
mLastStartActivityResult = startActivity(...);
...
}
private int startActivity(...){
...
//ActivityRecord是Activity在Ams的记录,与我们app的activity是一一对应的,
//它有一个成员变量appToken是一个binder类,后面app的activity就是通过这个
//类与Ams的activity通信的
ActivityRecord r = new ActivityRecord(...);
...
//调用startActivity的另一个重载,见下方
return startActivity(...);
}
private int startActivity(...){
...
//见下方
result = startActivityUnchecked(...);
...
}
private int startActivityUnchecked(...){
//初始化一些参数,比如mLaunchSingleTop(launchMode是否为singletop),调整mLaunchFlags等
setInitialState(...);
//进一步调整mLaunchFlags,比如原activity为singleinstance或者要启动的activity为
//singleinstance或singletask时,确保mLaunchFlags拥有 FLAG_ACTIVITY_NEW_TASK属性
computeLaunchingTaskFlags();
...
//查找是否有已启动的activity
ActivityRecord reusedActivity = getReusableIntentActivity();
if (reusedActivity != null) {
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| mLaunchSingleInstance || mLaunchSingleTask) {
//清理task使得目标activity在顶部,这里我们就可以明白
//FLAG_ACTIVITY_CLEAR_TOP或者singletask的原理。
...
if (top != null) {
...
//回调onNewIntent
deliverNewIntent(top);
}
...
}
}
...
//调整好stack以及task
mTargetStack.startActivityLocked(...);
...
//见下方
mSupervisor.resumeFocusedStackTopActivityLocked(...);
}
至此我们先总结一下,Ams根据intent通过PMS查找activity相关信息,这解释了为什么没有在AndroidManifest.xml注册就无法被启动。然后根据activity的launchMode、taskAffinity以及intent的launchFlags为activity找到合适的stack和task。stack、task以及ActivityRecord的关系如下图。Ams通过ActivityRecord保存activity的各种状态信息,以及通过其成员变量appToken(binder类)来和app的activity通信。
图片来自互联网
我们接着讲ActivityStackSupervisor的resumeFocusedStackTopActivityLocked方法,该方法会接着调用ActivityStack的resumeTopActivityUncheckedLocked方法,接着调用resumeTopActivityInnerLocked方法,然后再返回到ActivityStackSupervisor的startSpecificActivityLocked方法。
ActivityStackSupervisor.java
void startSpecificActivityLocked(...) {
if (app != null && app.thread != null) {
...
//如果该activity对应的app已启动,则直接启动activity
//具体见后面
realStartActivityLocked(...);
...
}
//通过Ams启动进程,具体见下方
mService.startProcessLocked(...);
}
final boolean realStartActivityLocked(...){
//这里的app.thread是一个binder类,用于与app的ActivityThread通信
//通过binder跨进程调用ActivityThread的scheduleLaunchActivity方法。
app.thread.scheduleLaunchActivity();
}
这里我们先接着讲通过Ams启动进程,Ams调用startProcessLocked后会紧接着调用另一个startProcessLocked重载
ActivityManagerService.java
final ProcessRecord startProcessLocked(...){
...
if (app != null && app.pid > 0) {
//如果app已启动且满足一些条件则直接返回
}
...
//见下方
startProcessLocked(...);
...
}
private final void startProcessLocked(...){
...
//entryPoint将作为参数通过socket传递,后面成为进程java代码执行的入口
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
...
//见下方
startResult = Process.start(entryPoint,...);
...
}
Process的start方法会紧接着调用ZygoteProcess的start方法,然后调用startViaZygote方法。
ZygoteProcess.java
private Process.ProcessStartResult startViaZygote(...){
...
//openZygoteSocketIfNeeded方法作用是和zygote进程建立socket连接
//之前我们提到zygote进程会扮演socket服务端的角色接受命令然后fork出进出
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
...
}
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(...){
//发送fork的命令以及上面提到entryPoint等其他参数
...
}
我们回到zygote进程,在zygote进程启动时,我们是调用到ZygoteInit的main方法进行初始化,其中会开启ZygoteServer的runSelectLoop线程一直循环接收命令。而其中的主要方法时ZygoteConnection的processOneCommand方法。
ZygoteConnection.java
Runnable processOneCommand(...){
//读取命令和参数
...
//fork进程
pid = Zygote.forkAndSpecialize(...);
...
//对linux下c编程有一定了解的朋友会知道,fork后子进程的pid为0
if (pid == 0) {
...
//处理子进程,见下方
return handleChildProc(parsedArgs, descriptors, childPipeFd);
}
}
private Runnable handleChildProc(...){
...
return ZygoteInit.zygoteInit(...);
}
ZygoteInit.java
public static final Runnable zygoteInit(...){
...
return RuntimeInit.applicationInit(...);
}
RuntimeInit.java
protected static Runnable applicationInit(...) {
//args.startClass就是我们之前提到的entryPoint,也就是"android.app.ActivityThread"
//由此可知app第一个被调用的方法是ActivityThread的main方法,这就是应用程序的入口。
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
我们终于回到了自己的进程,也很明确ActivityThread的main方法(提到main方法,总是有一种无以言表的亲切感)就是应用程序的入口。接着继续探索。
ActivityThread.java
public static void main(String[] args) {
//创建looper,looper其实很好理解,就是一直在循环,一直在取指执行。
//(我们的计算机的原理不也是一直取指执行吗)
Looper.prepareMainLooper();
//创建ActivityThread,一开始我们看到这个名字会以为它是一个Thread的类
//事实上它也完全可以代表app的主线程,因为它拥sMainLooper,
//拥有sMainThreadHandler,它会和Ams以及其他系统服务打交道
//而我个人的理解,activity即活动,thread即线,它就是一条线串起了所有app的活动。
ActivityThread thread = new ActivityThread();
//建立与Ams的Binder通道,见下方
thread.attach(false);
//创建handler,handler其实就是一个工具,让我们往MessageQueue放消息,移除消息以及处理消息
//looper才是主角,looper会从MessageQueue一直取消息,然后执行
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
}
private void attach(boolean system) {
...
//mgr是一个binder类用于与Ams通信
mgr.attachApplication(mAppThread);
...
}
看到这我们已经很清楚什么是主线程了,进程最初运行的那个线程就是主线程。而后面我们会发现一个“恐怖”的事实,我们的app会一直运行在sMainLooper之中(也就是主线程之中,当然排除我们创建的其他线程),包括启动activity,发送触摸消息、按键消息,我们都会通过sMainThreadHandler交由sMainLooper处理(我们开发时通过handler进行同步也是这个原理)。既然清楚了主线程的概念,那么anr的原理也就很好理解了,sMainLooper在处理这个消息的时候如果超过5s(activity)或者20s(service)就会anr,这种确保用户体验的一个做法。
接下来我们回到Ams,Ams会紧接着调用attachApplicationLocked方法。
ActivityManagerService.java
private final boolean attachApplicationLocked(...){
...
//通过binder IPC通知ActivityThread创建Application
thread.bindApplication(...);
...
mStackSupervisor.attachApplicationLocked(app);
...
}
ActivityStackSupervisor.java
boolean attachApplicationLocked(...) throws RemoteException {
...
//看我们发现了什么,似曾相识。没错就是上面我们留下来的问题。
//道理很简单,我们在启动一个activity的时候发现进程未启动,
//当我们启动进程后当然得重新启动activity
realStartActivityLocked(...);
...
}
final boolean realStartActivityLocked(...){
...
//这里的thread是一个binder类,和ActivityThread是对应的
app.thread.scheduleLaunchActivity(...);
...
}
重新回到app进程,先看创建Application的流程
ActivityThread$ApplicationThread.java
public final void bindApplication(...){
//通过handler调用到handleBindApplication方法,接着调用到performLaunchActivity方法
sendMessage(H.BIND_APPLICATION, data);
}
ActivityThread.java
private void handleBindApplication(AppBindData data) {
...
//创建Instrumentation
mInstrumentation = new Instrumentation();
...
//创建Application,回调attachBaseContext方法
app = data.info.makeApplication(data.restrictedBackupMode, null);
...
//回调onCreate方法
mInstrumentation.callApplicationOnCreate(app);
...
}
接着看创建Activity的流程
ActivityThread$ApplicationThread.java
public final void scheduleLaunchActivity(...){
//通过handler调用到handleLaunchActivity方法,接着调用到performLaunchActivity方法
sendMessage(H.LAUNCH_ACTIVITY, r);
}
ActivityThread.java
private Activity performLaunchActivity(...){
//真正的Context类,也就是我们上面提到的mBase
ContextImpl appContext = createBaseContextForActivity(r);
...
//利用发射创建activity
activity = mInstrumentation.newActivity(...);
...
//将appContext赋给mBase,并且回调attachBaseContext(context);
//getInstrumentation()之前提到过调用Instrumentation的execStartActivity方法
//r.token为binder类与Ams的ActivityRecord对应,我的另一篇文章(见注6)提到它有重要作用
activity.attach(appContext, this, getInstrumentation(),r.token,...,app,...,window,...);
...
//回调onCreate
mInstrumentation.callActivityOnCreate(...);
}
View的绘制
这一部分并不是要讲自定义view,而是将窗口的创建(包括添加与绘制)。 从WmS的角度来观察,一个窗口并不是一个Window类,而是一个View类。当WmS收到用户的消息后,需要把消息派发到窗口,View类本身并不能直接接收WmS传递过来的消息,真正接收用户消息的必须是IWindow类(binder类),而实现IWindow类的是ViewRoot.W类,每一个W内部都包含了一个View变量。这两句话引用自《Android内核剖析》,从后面讲解可知,Window类更多是窗口的抽象,而其中的view才是窗口的内容。
Framework中定义了三种窗口,分别为应用窗口、子窗口和系统窗口。其中应用窗口对应一个Activity,接下来就是讲解应用窗口(下面简称为窗口)的创建。既然窗口对应一个Activity,那么窗口就是在startActivity的过程中创建的。上面提到Activity的创建会回调onCreate的,而我们在开发的时候会在其中调用setContentView方法。而setContentView会调用Window类的setContentView方法,如果你去查看Activity的attach方法时,会发现Window类实际上是一个PhoneWindow类。
PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
...
installDecor();
...
//加载app自定义的布局,由下可知我们的布局至少包裹着两个view,
//先是由mContentParent,然后再由mDecor包裹
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}
private void installDecor() {
...
//创建DecorView(继承自FrameLayout),Decor顾名思义,窗口的所有内容都在这个view中展示,
//这个View是Activity所有View的root。
//这也是我们查看Activity view hierarchy,最外层都是FrameLayout的原因
mDecor = generateDecor(-1);
...
//根据不同的style配置inflate不同的xml布局,
//这些xml有个共同特点:都有一个id为ID_ANDROID_CONTEN的view
//所以可以通ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT)
//为contentParent赋值
mContentParent = generateLayout(mDecor);
...
}
这一步只是将View创建出来,接下来还会涉及到两步:1、将窗口添加到Wms,Wms也会和Ams一样将窗口以一定形式管理起来。2、将View要展示的内容转成数据保存于屏幕缓冲区内存,交由系统绘制出来。
在startActivity的过程中,创建Activity后会接着调用handleResumeActivity。
ActivityThread.java
private void handleLaunchActivity(...){
...
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
...
//见下方
handleResumeActivity(...);
...
}
}
final void handleResumeActivity(...){
...
//回调onResume
r = performResumeActivity(token, clearHide, reason);
...
//将decor设为不可见
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
...
//调用Activity的makeVisible
r.activity.makeVisible();
...
}
Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
//最后会调用到WindowManagerGlobal的addView
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
WindowManagerGlobal.java
public void addView(...) {
...
//创建ViewRootImpl(View root的抽象)
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//将view(这里其实是mDecor)、root(View的抽象)以及layoutParam缓存起来
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
...
//见下方
root.setView(view, wparams, panelParentView);
...
}
ViewRootImpl.java
public void setView(...) {
...
//下面小节接着讲
requestLayout();
...
//ipc到Wms,Session类为服务端
//mInputChannel是一个InputChannel类,是管道在java层的实现,后面讲到Android事件的时候会细说
res = mWindowSession.addToDisplay(...,mInputChannel);
...
}
Session.java
@Override
public int addToDisplay(...) {
//见下方
return mService.addWindow(...);
}
WindowManagerService.java
public int addWindow(...) {
//创建WindowState
//其中session是Session的Binder服务端
//client是IWindow的Binder客户端
final WindowState win = new WindowState(..., session, client,...);
...
//会调用到Session的windowAddedLocked方法,见下方
win.attach();
//将win缓存起来
mWindowMap.put(client.asBinder(), win);
...
if (win.canReceiveKeys()) {
//如果该窗口是可交互窗口(比如Toast为不可交互窗口),则更新聚焦窗口
focusChanged = updateFocusedWindowLocked(...);
...
}
}
Session.java
void windowAddedLocked(String packageName) {
...
if (mSurfaceSession == null) {
//创建SurfaceSession,该类是直接和Surfaceflinger交互的类,
//用于向SurfaceFlinger中添加、删除、变换窗口。由千每个应用程序仅对应一个Session对象,
//因此,mSurfaceSession实际上只会被创建一次,
//即应用程序中的第一个窗口创建时会构造一个SurfaceSession对象,
//同一个应用程序中后续的窗口添加不会再构造 SurfaceSession 对象.
mSurfaceSession = new SurfaceSession();
...
//保存Session
mService.mSessions.add(this);
...
}
...
}
到这里可以看到Wms将窗口的信息保存下来,也就是管理起来,是事件分发的基础。
回到上面的requestLayout方法,requestLayout调用了scheduleTraversals方法,该方法发起一个View树遍历的消息,该消息是异步处理的,对应的处理函数为performTraversals方法。
ViewRootImpl.java
private void performTraversals() {
final View host = mView;
if (mFirst) {
...
//如果是窗口第一次显示,为mAttachInfo初始化,并赋给mView,
//调用ViewGroup的dispatch方法,将mAttachInfo递归地传递给子View
host.dispatchAttachedToWindow(mAttachInfo, 0);
}
...
//如果窗口尺寸发生了改变,则调用 host.measure()重新计算窗口中视图的大小
//Android的View和ViewGroup是很经典的组合模式
//measure过程会遍历整个View tree,会调用每个View的measure以及回调onMeasure
//layout和draw的过程也是类似
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
//根据以七步骤的执行结果,判断是否需要进行重新布局。比如当任意视图的大小发生变化时,
//它会影响其他视图的布局
performLayout(lp, mWidth, mHeight);
...
//判断没有取消绘制,并且不是 newSurface, 则开始绘制
//newSurface变拉的含义是指,ViewRoot中创建了Surface对象,但是该对象还没有被WmS分配真正的显存,
//ViewRoot中是调用sWindowSession.relayoutWindow()为该Surface对象中分配真正的显存,
//在一般情况下,此处的newSurface都是false。
performDraw();
...
}
第一次由于窗口设置不可见,所以前面的代码可以看到,在Activity的makeVisible方法会调用mDecor.setVisibility(View.VISIBLE),经过一系列调用会再次调用到ViewRootImpl的performTraversals方法,然后调用performDraw方法。
Surface是原始图像缓冲区(raw buffer)的一个句柄。也就是说Surface对应一段内存,这段内存的数据就是要绘制的内容,Surface 类本质上就是表示一个平面
我们知道绘制不同图案显然是一种橾作,而不是一段数据。Android用Canvas类来表示这些操作,也就是说Canvas就是绘制的功能类。看Canvas的描述:A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap)——Canvas的功能就是响应draw的调用,并将其写入bitmap,而这个bitmap用于保存所有像素,你就会更清楚Canvas的概念。
ViewRootImpl.java
private void performDraw() {
...
draw(fullRedrawNeeded);
...
}
private void draw(boolean fullRedrawNeeded) {
//之前向Wms申请的
Surface surface = mSurface;
...
if (!drawSoftware(...)) {
return;
}
...
}
private boolean drawSoftware(...) {
...
//获取canvas并锁定
canvas = mSurface.lockCanvas(dirty);
...
//遍历子类的draw,很显然这一过程就是将所有子类的内容转成像素写入Canvas的bitmap中
mView.draw(canvas);
...
//将保存所有view内容的Canvas提交给surface,交由系统底层绘制。
surface.unlockCanvasAndPost(canvas);
...
}
至此view绘制的框架已描述完毕。
2、一次触摸,Android到底干了啥
这个标题来自《一次触摸,Android到底干了啥》,所以下面很多内容会引用自这篇文章。
一次触摸意味着什么?我们在使用Android设备的过程中,点击、长按、滑动(TouchEvent)以及按实体按键(KeyEvent)都可以成为“一次触摸”。因此,一次触摸可以代表所有我们使用过程中的操作。
我们的触摸当然是从硬件开始,硬件会将事件传递到内核,内核传递到Framework。前面提到SystemServer启动时会启动各种服务:
SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
startOtherServices();
...
}
private void startOtherServices() {
...
//创建InputManagerService
inputManager = new InputManagerService(context);
...
//创建WindowManagerService,且持有InputManagerService
wm = WindowManagerService.main(..., inputManager,...);
...
//将InputMonitor设为回调,且启动InputManagerService
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
}
InputManagerService.java
public InputManagerService(Context context) {
...
//初始化native对象(mPtr是long,保存native对象的指针,这是jni常用的保持native对象的方法)
//InputManagerService对应native的InputManager.cpp
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
...
}
public void start() {
...
//用于启动下面提到的两个线程
nativeStart(mPtr);
...
}
InputManager.cpp
InputManager::InputManager(...) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
/**
由上面的代码,我们可以看到InputManager初始化时创建了InputDispatcher、InputReader
以及InputReaderThread、InputDispatcherThread。InputReader用来读取输入信息(也就是各种事件),
InputDispatcher用于分发事件。而两个线程很显然就是来运行这两个功能。
*/
InputReader线程会持续调用输入设备的驱动,读取所有用户输入的消息该线程和InputDispatcher线程都在系统进程(system\_process)空间中运行。InputDispatcher线程从自己的消息队列中取出原始消息,取出的消息有可能经过两种方式进行派发。第一种是经过管道(Pipe)直接派发到客户窗口中,另一种则是先派发到WmS中,由WmS经过一定的处理,如果WmS没有处理该消息,则再派发到客户窗口中,否则 ,不派发到客户窗口(引用自《Android内核剖析》)。如图(图片同样来自《Android内核剖析》):
如果是按键消息,InputDispatcher会先回调InputManager中定义的回调函数,这既会回调InputMonitor中的回调函数,这又会调用WmS中定义的相关函数。对于系统按键消息,比如"Home"键、电话按键等,WmS内部会按照默认的方式处理,如果事件被消耗,InputDispatcher则不会继续把这些按键消息传递给客户窗口对于触摸屏消息,InputDispatcher则直接传递给客户窗口。
InputManagerService.java
//按键事件的回调,前面我们提到回调对象是InputMonitor
// Native callback.
private long interceptKeyBeforeDispatching(InputWindowHandle focus,
KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
InputMonitor.java
@Override
public long interceptKeyBeforeDispatching(
InputWindowHandle focus, KeyEvent event, int policyFlags) {
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
//回调Wms,mPolicy在SystemServer初始化时创建,为PhoneWindowManager类,可以看到其中对各种按键的处理
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
当InputDispatcher直接传递给窗口时,通过findTouchedWindowAtLocked方法找到当前获得焦点的窗口,然后通过Pipe(管道)进行ipc。也就是说在native层也会维护一组窗口相关的信息。我们回到Wms添加窗口的过程:
WindowManagerService.java
public int addWindow(...) {
//创建WindowState,构造方法中会创建InputWindowHandle
final WindowState win = new WindowState(...);
...
//建立管道通信,其中outInputChannel是从app进程传递过来的
win.openInputChannel(outInputChannel是从app进程);
...
//更新窗口焦点改变的信息到native层,同理当窗口切换或者销毁时也会更新
mInputMonitor.updateInputWindowsLw(false /*force*/);
...
}
WindowState.java
void openInputChannel(InputChannel outInputChannel) {
...
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
/*Channel[0]保存在server端*/
mInputWindowHandle.inputChannel = inputChannels[0];
...
/* Channel[1]返回给app的ViewRootImpl端*/
mClientChannel.transferTo(outInputChannel);
...
/*注册到InputManagerService native层*/
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
可以看到,在Wms中维护一组WindowState,用于窗口的创建、销毁切换,而在InputManagerService则维护一组InputWindowHandle,用于事件的分发。
我们回到ViewRootImpl中。在添加窗口时,我们调用了setView方法。
ViewRootImpl.java
public void setView(...) {
...
//InputChannel类,是管道在java层的实现
mInputChannel = new InputChannel();
...
//查看IWindowSession.aidl会发现这里的mInputChannel标注着out,
//也就是在另一个进程的改变都会同步到本进程
res = mWindowSession.addToDisplay(...,mInputChannel);
...
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//建立管道的回调,见下方
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
}
}
ViewRootImpl$WindowInputEventReceiver.java
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
//见下方
super(inputChannel, looper);
}
InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
...
//建立管道的回调,native层收到事件就会回调给InputEventReceiver
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
...
}
当InputManagerService的InputDispatcher通过管道将消息传递给app进程,app进程的管道会回调InputEventReceiver(也就是WindowInputEventReceiver),进行事件分发。后面的代码可以说是责任链模式的标准答案,非常精彩读者可以自行学习。
总结
Android Framework可以说是一个庞大的工程,如果我们在一开始的过程中就陷入细节,就无法走通一条路。君子善假于物也,借助大佬的研究学习成果,我们可以先学习整体的框架,有必要时再各个击破。非常感谢各个大佬,下面的参考文献可能有所遗漏,在此致歉!希望本篇文章对于读者有所帮助。
本文转自 https://juejin.cn/post/6844903717301387272,如有侵权,请联系删除。本文旨在将Framework的框架描绘出来,主要是记录我一段时间关于android framework的学习,希望抛砖引玉,对于读者有一定的帮助。
前言
android framework与我们的开发息息相关,本文将从开机,即framework的初始化讲起,然后再涉及android运行过程中的几个使用场景。比如用户启动app(点击桌面图标后发生了什么),用户使用app(一次触摸,Android到底干了啥)。其中会涉及主线程、anr、handler、binder、zygote、app的入口是什么、自定义view为什么要三步走等一些我们经常接触的概念,并一一解答。涉及源码为api 27。
一、初始化篇
当按开机键的时候,设备首先执行BootLoader,BootLoader负责把Linux内核从加载到内存,并执行内核的初始化,最后内核将读取init.rc文件,并启动该文件中定义的各种服务程序。Android framework对于内核而言就是一个Linux程序而已,而该程序就在init.rc文件中被定义。Android framework的初始化过程由此开始。
首先被创建的是zygote进程,这是系统中运行的第一个Dalvik虚拟机程序,顾名思义,后面所有Dalvik虚拟机进程都是通过它“孵化”而来(学过生物的我们都知道,人体所有的细胞都是由受精卵分裂而来,所以本人觉得这个名称取得非常准确巧妙)。
zygote孵化出的第一个 Dalvik1 进程叫做 SystemServer,是Framework相当重要的进程。 SystemServer 仅仅是该进程的别名,而该进程具休对应的程序依然是 app\_process, 因为 SystemServer 是从 app\_process中孵化出来的。Ams、Wms、Pms等等都在此进程中创建,可以说SystemServer管理Framework所有的活动。
SystemServer 中创建了一个 Socket2 客户端,并有AmS负责管理该客户端,之后所有的 Dalvik 进程都将通过该 Socket 客户端间接被启动。当要启动新的 APK 进程时 ,AmS 中会通过该 Socket 客户端向 zygote 进程的 Socket服务端发送一个启动命令,然后zygote会孵化出新的进程。
1、zygote的启动
前面我们提到内核初始化时,会启动在init.rc文件中配置的程序,zygote相关的配置信息如下:
简单说明一下这个语句的意思,内核会执行/system/bin/app\_process3目录下的程序,启动一个叫zygote的服务。其中参数--start-system-server, 仅在指定 -- zygote 参数时才有效,意思是告知Zygotelnit启动完毕后孵化出第一个进程SystemServer。由此可知,zygote启动后做的事情之一就是启动SystemServer。
当 zygote 服务从 app\_process 开始启动后,会启动一个 Dalvik 虚拟机,而虚拟机执行的第一个 Java类就是 ZygoteInit.java。(app进程fork自zygote进程,所以ZygoteInit.main同样也是app的入口,只不过会根据进程的不同执行不同的逻辑。这就是有时候我们程序错误日志的调用栈里面可以看到"…ZygoteInit.main……"的原因。)ZygoteInit会做另外两件事:一是前面提到的,启动一个Socket服务端口,该Socket端口用于接收启动新进程的命令;二是预加载的Framework大部分类及资源供后续app使用。zygote fork app进程时,并不需要复制这一部分,而是使用共享内存的方式。
总结: zygote的进程启动后主要做了三件事:分别是启动一个Socket服务,用于接收启动新进程的命令、预加载的Framework大部分类及资源以及启动SystemServer。
2、SystemServer的启动
SystemServer是在zygote进程中最终调用到Zygote.forkSystemServer方法启动的。启动后会做一些初始的配置,比如关闭Socket服务端(非zygote进程不需要),配置SystemServer运行环境。然后调用SystemServer.main。
SystemServer启动后,主要做两件事:一是通过SystemServerManager启动各种服务线程,比如AMS、WMS、PMS等等,并将其注册到ServiceManager(AMS、WMS与app的运行息息相关,其具体内容后面再展开);二是启动HomeActivity,也就是启动launcher,launcher与普通app的启动大同小异,后面再详细介绍。
3、ServiceManager的启动
此处的ServiceManager不是java世界的,而是native世界的。它也是通过init.rc配置启动的,其功能相当于service4的DNS服务器。SystemServer启动的各个服务都会注册于其中,我们在使用binder进行跨进程调用时,首先回去查询ServiceManager获取到对应service的binder引用,然后再进行后续操作。这个过程与我们通过域名查询dns服务器获得ip最后访问网站类似。
注意:我们查看Framework代码时候会发现SystemServiceRegistry类,这个类和系统服务的注册没有半毛钱关系,它只不过是将查询各种service的工具类缓存起来。
二、运行时篇
我们在使用android设备时,就是Framework的运行时。下面我会从两个关键场景说起:第一个场景,点击桌面图标,这个场景会涉及到android的消息机制、app的启动、activity的创建、window的创建和view的绘制。第二个场景,我们在滑动屏幕或者点击按钮等等,这个场景会涉及到Framework怎么获得硬件的事件以及app的事件分发机制。
1、点击桌面图标后发生了什么
Activity的启动
我们都知道桌面程序也就是launcher和普通的app基本没什么差别,我们点击桌面图标其实调用了Activity的startActivity方法。Activity是Context的子类5,所以本来应该是调用了Context的startActivity方法,不过Activity重载了该方法,和Context区别是少了是否有Intent.FLAG\_ACTIVITY\_NEW\_TASK的判断。这也是为什么我们在非Activity的Context(比如Service)启动时要加Intent.FLAG\_ACTIVITY\_NEW\_TASK。不论是Activity还是Context,最终都会调用到Instrumentation的execStartActivity方法,然后通过Binder跨进程调用Ams的startActivity方法。
Ams会调用到startActivityAsUser方法,然后调用ActivityStarter的startActivityMayWait方法。
ActivityStarter.java
final int startActivityMayWait(...){
...
//根据intent通过PMS查找activity相关信息
//如何没有在AndroidManifest.xml注册过就无法找到activity
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
...
//见下方
int res = startActivityLocked(...);
...
}
int startActivityLocked(...){
...
//见下方
mLastStartActivityResult = startActivity(...);
...
}
private int startActivity(...){
...
//ActivityRecord是Activity在Ams的记录,与我们app的activity是一一对应的,
//它有一个成员变量appToken是一个binder类,后面app的activity就是通过这个
//类与Ams的activity通信的
ActivityRecord r = new ActivityRecord(...);
...
//调用startActivity的另一个重载,见下方
return startActivity(...);
}
private int startActivity(...){
...
//见下方
result = startActivityUnchecked(...);
...
}
private int startActivityUnchecked(...){
//初始化一些参数,比如mLaunchSingleTop(launchMode是否为singletop),调整mLaunchFlags等
setInitialState(...);
//进一步调整mLaunchFlags,比如原activity为singleinstance或者要启动的activity为
//singleinstance或singletask时,确保mLaunchFlags拥有 FLAG_ACTIVITY_NEW_TASK属性
computeLaunchingTaskFlags();
...
//查找是否有已启动的activity
ActivityRecord reusedActivity = getReusableIntentActivity();
if (reusedActivity != null) {
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| mLaunchSingleInstance || mLaunchSingleTask) {
//清理task使得目标activity在顶部,这里我们就可以明白
//FLAG_ACTIVITY_CLEAR_TOP或者singletask的原理。
...
if (top != null) {
...
//回调onNewIntent
deliverNewIntent(top);
}
...
}
}
...
//调整好stack以及task
mTargetStack.startActivityLocked(...);
...
//见下方
mSupervisor.resumeFocusedStackTopActivityLocked(...);
}
至此我们先总结一下,Ams根据intent通过PMS查找activity相关信息,这解释了为什么没有在AndroidManifest.xml注册就无法被启动。然后根据activity的launchMode、taskAffinity以及intent的launchFlags为activity找到合适的stack和task。stack、task以及ActivityRecord的关系如下图。Ams通过ActivityRecord保存activity的各种状态信息,以及通过其成员变量appToken(binder类)来和app的activity通信。
图片来自互联网
我们接着讲ActivityStackSupervisor的resumeFocusedStackTopActivityLocked方法,该方法会接着调用ActivityStack的resumeTopActivityUncheckedLocked方法,接着调用resumeTopActivityInnerLocked方法,然后再返回到ActivityStackSupervisor的startSpecificActivityLocked方法。
ActivityStackSupervisor.java
void startSpecificActivityLocked(...) {
if (app != null && app.thread != null) {
...
//如果该activity对应的app已启动,则直接启动activity
//具体见后面
realStartActivityLocked(...);
...
}
//通过Ams启动进程,具体见下方
mService.startProcessLocked(...);
}
final boolean realStartActivityLocked(...){
//这里的app.thread是一个binder类,用于与app的ActivityThread通信
//通过binder跨进程调用ActivityThread的scheduleLaunchActivity方法。
app.thread.scheduleLaunchActivity();
}
这里我们先接着讲通过Ams启动进程,Ams调用startProcessLocked后会紧接着调用另一个startProcessLocked重载
ActivityManagerService.java
final ProcessRecord startProcessLocked(...){
...
if (app != null && app.pid > 0) {
//如果app已启动且满足一些条件则直接返回
}
...
//见下方
startProcessLocked(...);
...
}
private final void startProcessLocked(...){
...
//entryPoint将作为参数通过socket传递,后面成为进程java代码执行的入口
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
...
//见下方
startResult = Process.start(entryPoint,...);
...
}
Process的start方法会紧接着调用ZygoteProcess的start方法,然后调用startViaZygote方法。
ZygoteProcess.java
private Process.ProcessStartResult startViaZygote(...){
...
//openZygoteSocketIfNeeded方法作用是和zygote进程建立socket连接
//之前我们提到zygote进程会扮演socket服务端的角色接受命令然后fork出进出
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
...
}
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(...){
//发送fork的命令以及上面提到entryPoint等其他参数
...
}
我们回到zygote进程,在zygote进程启动时,我们是调用到ZygoteInit的main方法进行初始化,其中会开启ZygoteServer的runSelectLoop线程一直循环接收命令。而其中的主要方法时ZygoteConnection的processOneCommand方法。
ZygoteConnection.java
Runnable processOneCommand(...){
//读取命令和参数
...
//fork进程
pid = Zygote.forkAndSpecialize(...);
...
//对linux下c编程有一定了解的朋友会知道,fork后子进程的pid为0
if (pid == 0) {
...
//处理子进程,见下方
return handleChildProc(parsedArgs, descriptors, childPipeFd);
}
}
private Runnable handleChildProc(...){
...
return ZygoteInit.zygoteInit(...);
}
ZygoteInit.java
public static final Runnable zygoteInit(...){
...
return RuntimeInit.applicationInit(...);
}
RuntimeInit.java
protected static Runnable applicationInit(...) {
//args.startClass就是我们之前提到的entryPoint,也就是"android.app.ActivityThread"
//由此可知app第一个被调用的方法是ActivityThread的main方法,这就是应用程序的入口。
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
我们终于回到了自己的进程,也很明确ActivityThread的main方法(提到main方法,总是有一种无以言表的亲切感)就是应用程序的入口。接着继续探索。
ActivityThread.java
public static void main(String[] args) {
//创建looper,looper其实很好理解,就是一直在循环,一直在取指执行。
//(我们的计算机的原理不也是一直取指执行吗)
Looper.prepareMainLooper();
//创建ActivityThread,一开始我们看到这个名字会以为它是一个Thread的类
//事实上它也完全可以代表app的主线程,因为它拥sMainLooper,
//拥有sMainThreadHandler,它会和Ams以及其他系统服务打交道
//而我个人的理解,activity即活动,thread即线,它就是一条线串起了所有app的活动。
ActivityThread thread = new ActivityThread();
//建立与Ams的Binder通道,见下方
thread.attach(false);
//创建handler,handler其实就是一个工具,让我们往MessageQueue放消息,移除消息以及处理消息
//looper才是主角,looper会从MessageQueue一直取消息,然后执行
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
}
private void attach(boolean system) {
...
//mgr是一个binder类用于与Ams通信
mgr.attachApplication(mAppThread);
...
}
看到这我们已经很清楚什么是主线程了,进程最初运行的那个线程就是主线程。而后面我们会发现一个“恐怖”的事实,我们的app会一直运行在sMainLooper之中(也就是主线程之中,当然排除我们创建的其他线程),包括启动activity,发送触摸消息、按键消息,我们都会通过sMainThreadHandler交由sMainLooper处理(我们开发时通过handler进行同步也是这个原理)。既然清楚了主线程的概念,那么anr的原理也就很好理解了,sMainLooper在处理这个消息的时候如果超过5s(activity)或者20s(service)就会anr,这种确保用户体验的一个做法。
接下来我们回到Ams,Ams会紧接着调用attachApplicationLocked方法。
ActivityManagerService.java
private final boolean attachApplicationLocked(...){
...
//通过binder IPC通知ActivityThread创建Application
thread.bindApplication(...);
...
mStackSupervisor.attachApplicationLocked(app);
...
}
ActivityStackSupervisor.java
boolean attachApplicationLocked(...) throws RemoteException {
...
//看我们发现了什么,似曾相识。没错就是上面我们留下来的问题。
//道理很简单,我们在启动一个activity的时候发现进程未启动,
//当我们启动进程后当然得重新启动activity
realStartActivityLocked(...);
...
}
final boolean realStartActivityLocked(...){
...
//这里的thread是一个binder类,和ActivityThread是对应的
app.thread.scheduleLaunchActivity(...);
...
}
重新回到app进程,先看创建Application的流程
ActivityThread$ApplicationThread.java
public final void bindApplication(...){
//通过handler调用到handleBindApplication方法,接着调用到performLaunchActivity方法
sendMessage(H.BIND_APPLICATION, data);
}
ActivityThread.java
private void handleBindApplication(AppBindData data) {
...
//创建Instrumentation
mInstrumentation = new Instrumentation();
...
//创建Application,回调attachBaseContext方法
app = data.info.makeApplication(data.restrictedBackupMode, null);
...
//回调onCreate方法
mInstrumentation.callApplicationOnCreate(app);
...
}
接着看创建Activity的流程
ActivityThread$ApplicationThread.java
public final void scheduleLaunchActivity(...){
//通过handler调用到handleLaunchActivity方法,接着调用到performLaunchActivity方法
sendMessage(H.LAUNCH_ACTIVITY, r);
}
ActivityThread.java
private Activity performLaunchActivity(...){
//真正的Context类,也就是我们上面提到的mBase
ContextImpl appContext = createBaseContextForActivity(r);
...
//利用发射创建activity
activity = mInstrumentation.newActivity(...);
...
//将appContext赋给mBase,并且回调attachBaseContext(context);
//getInstrumentation()之前提到过调用Instrumentation的execStartActivity方法
//r.token为binder类与Ams的ActivityRecord对应,我的另一篇文章(见注6)提到它有重要作用
activity.attach(appContext, this, getInstrumentation(),r.token,...,app,...,window,...);
...
//回调onCreate
mInstrumentation.callActivityOnCreate(...);
}
View的绘制
这一部分并不是要讲自定义view,而是将窗口的创建(包括添加与绘制)。 从WmS的角度来观察,一个窗口并不是一个Window类,而是一个View类。当WmS收到用户的消息后,需要把消息派发到窗口,View类本身并不能直接接收WmS传递过来的消息,真正接收用户消息的必须是IWindow类(binder类),而实现IWindow类的是ViewRoot.W类,每一个W内部都包含了一个View变量。这两句话引用自《Android内核剖析》,从后面讲解可知,Window类更多是窗口的抽象,而其中的view才是窗口的内容。
Framework中定义了三种窗口,分别为应用窗口、子窗口和系统窗口。其中应用窗口对应一个Activity,接下来就是讲解应用窗口(下面简称为窗口)的创建。既然窗口对应一个Activity,那么窗口就是在startActivity的过程中创建的。上面提到Activity的创建会回调onCreate的,而我们在开发的时候会在其中调用setContentView方法。而setContentView会调用Window类的setContentView方法,如果你去查看Activity的attach方法时,会发现Window类实际上是一个PhoneWindow类。
PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
...
installDecor();
...
//加载app自定义的布局,由下可知我们的布局至少包裹着两个view,
//先是由mContentParent,然后再由mDecor包裹
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}
private void installDecor() {
...
//创建DecorView(继承自FrameLayout),Decor顾名思义,窗口的所有内容都在这个view中展示,
//这个View是Activity所有View的root。
//这也是我们查看Activity view hierarchy,最外层都是FrameLayout的原因
mDecor = generateDecor(-1);
...
//根据不同的style配置inflate不同的xml布局,
//这些xml有个共同特点:都有一个id为ID_ANDROID_CONTEN的view
//所以可以通ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT)
//为contentParent赋值
mContentParent = generateLayout(mDecor);
...
}
这一步只是将View创建出来,接下来还会涉及到两步:1、将窗口添加到Wms,Wms也会和Ams一样将窗口以一定形式管理起来。2、将View要展示的内容转成数据保存于屏幕缓冲区内存,交由系统绘制出来。
在startActivity的过程中,创建Activity后会接着调用handleResumeActivity。
ActivityThread.java
private void handleLaunchActivity(...){
...
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
...
//见下方
handleResumeActivity(...);
...
}
}
final void handleResumeActivity(...){
...
//回调onResume
r = performResumeActivity(token, clearHide, reason);
...
//将decor设为不可见
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
...
//调用Activity的makeVisible
r.activity.makeVisible();
...
}
Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
//最后会调用到WindowManagerGlobal的addView
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
WindowManagerGlobal.java
public void addView(...) {
...
//创建ViewRootImpl(View root的抽象)
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//将view(这里其实是mDecor)、root(View的抽象)以及layoutParam缓存起来
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
...
//见下方
root.setView(view, wparams, panelParentView);
...
}
ViewRootImpl.java
public void setView(...) {
...
//下面小节接着讲
requestLayout();
...
//ipc到Wms,Session类为服务端
//mInputChannel是一个InputChannel类,是管道在java层的实现,后面讲到Android事件的时候会细说
res = mWindowSession.addToDisplay(...,mInputChannel);
...
}
Session.java
@Override
public int addToDisplay(...) {
//见下方
return mService.addWindow(...);
}
WindowManagerService.java
public int addWindow(...) {
//创建WindowState
//其中session是Session的Binder服务端
//client是IWindow的Binder客户端
final WindowState win = new WindowState(..., session, client,...);
...
//会调用到Session的windowAddedLocked方法,见下方
win.attach();
//将win缓存起来
mWindowMap.put(client.asBinder(), win);
...
if (win.canReceiveKeys()) {
//如果该窗口是可交互窗口(比如Toast为不可交互窗口),则更新聚焦窗口
focusChanged = updateFocusedWindowLocked(...);
...
}
}
Session.java
void windowAddedLocked(String packageName) {
...
if (mSurfaceSession == null) {
//创建SurfaceSession,该类是直接和Surfaceflinger交互的类,
//用于向SurfaceFlinger中添加、删除、变换窗口。由千每个应用程序仅对应一个Session对象,
//因此,mSurfaceSession实际上只会被创建一次,
//即应用程序中的第一个窗口创建时会构造一个SurfaceSession对象,
//同一个应用程序中后续的窗口添加不会再构造 SurfaceSession 对象.
mSurfaceSession = new SurfaceSession();
...
//保存Session
mService.mSessions.add(this);
...
}
...
}
到这里可以看到Wms将窗口的信息保存下来,也就是管理起来,是事件分发的基础。
回到上面的requestLayout方法,requestLayout调用了scheduleTraversals方法,该方法发起一个View树遍历的消息,该消息是异步处理的,对应的处理函数为performTraversals方法。
ViewRootImpl.java
private void performTraversals() {
final View host = mView;
if (mFirst) {
...
//如果是窗口第一次显示,为mAttachInfo初始化,并赋给mView,
//调用ViewGroup的dispatch方法,将mAttachInfo递归地传递给子View
host.dispatchAttachedToWindow(mAttachInfo, 0);
}
...
//如果窗口尺寸发生了改变,则调用 host.measure()重新计算窗口中视图的大小
//Android的View和ViewGroup是很经典的组合模式
//measure过程会遍历整个View tree,会调用每个View的measure以及回调onMeasure
//layout和draw的过程也是类似
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
//根据以七步骤的执行结果,判断是否需要进行重新布局。比如当任意视图的大小发生变化时,
//它会影响其他视图的布局
performLayout(lp, mWidth, mHeight);
...
//判断没有取消绘制,并且不是 newSurface, 则开始绘制
//newSurface变拉的含义是指,ViewRoot中创建了Surface对象,但是该对象还没有被WmS分配真正的显存,
//ViewRoot中是调用sWindowSession.relayoutWindow()为该Surface对象中分配真正的显存,
//在一般情况下,此处的newSurface都是false。
performDraw();
...
}
第一次由于窗口设置不可见,所以前面的代码可以看到,在Activity的makeVisible方法会调用mDecor.setVisibility(View.VISIBLE),经过一系列调用会再次调用到ViewRootImpl的performTraversals方法,然后调用performDraw方法。
Surface是原始图像缓冲区(raw buffer)的一个句柄。也就是说Surface对应一段内存,这段内存的数据就是要绘制的内容,Surface 类本质上就是表示一个平面
我们知道绘制不同图案显然是一种橾作,而不是一段数据。Android用Canvas类来表示这些操作,也就是说Canvas就是绘制的功能类。看Canvas的描述:A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap)——Canvas的功能就是响应draw的调用,并将其写入bitmap,而这个bitmap用于保存所有像素,你就会更清楚Canvas的概念。
ViewRootImpl.java
private void performDraw() {
...
draw(fullRedrawNeeded);
...
}
private void draw(boolean fullRedrawNeeded) {
//之前向Wms申请的
Surface surface = mSurface;
...
if (!drawSoftware(...)) {
return;
}
...
}
private boolean drawSoftware(...) {
...
//获取canvas并锁定
canvas = mSurface.lockCanvas(dirty);
...
//遍历子类的draw,很显然这一过程就是将所有子类的内容转成像素写入Canvas的bitmap中
mView.draw(canvas);
...
//将保存所有view内容的Canvas提交给surface,交由系统底层绘制。
surface.unlockCanvasAndPost(canvas);
...
}
至此view绘制的框架已描述完毕。
2、一次触摸,Android到底干了啥
这个标题来自《一次触摸,Android到底干了啥》,所以下面很多内容会引用自这篇文章。
一次触摸意味着什么?我们在使用Android设备的过程中,点击、长按、滑动(TouchEvent)以及按实体按键(KeyEvent)都可以成为“一次触摸”。因此,一次触摸可以代表所有我们使用过程中的操作。
我们的触摸当然是从硬件开始,硬件会将事件传递到内核,内核传递到Framework。前面提到SystemServer启动时会启动各种服务:
SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
startOtherServices();
...
}
private void startOtherServices() {
...
//创建InputManagerService
inputManager = new InputManagerService(context);
...
//创建WindowManagerService,且持有InputManagerService
wm = WindowManagerService.main(..., inputManager,...);
...
//将InputMonitor设为回调,且启动InputManagerService
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
}
InputManagerService.java
public InputManagerService(Context context) {
...
//初始化native对象(mPtr是long,保存native对象的指针,这是jni常用的保持native对象的方法)
//InputManagerService对应native的InputManager.cpp
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
...
}
public void start() {
...
//用于启动下面提到的两个线程
nativeStart(mPtr);
...
}
InputManager.cpp
InputManager::InputManager(...) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
/**
由上面的代码,我们可以看到InputManager初始化时创建了InputDispatcher、InputReader
以及InputReaderThread、InputDispatcherThread。InputReader用来读取输入信息(也就是各种事件),
InputDispatcher用于分发事件。而两个线程很显然就是来运行这两个功能。
*/
InputReader线程会持续调用输入设备的驱动,读取所有用户输入的消息该线程和InputDispatcher线程都在系统进程(system\_process)空间中运行。InputDispatcher线程从自己的消息队列中取出原始消息,取出的消息有可能经过两种方式进行派发。第一种是经过管道(Pipe)直接派发到客户窗口中,另一种则是先派发到WmS中,由WmS经过一定的处理,如果WmS没有处理该消息,则再派发到客户窗口中,否则 ,不派发到客户窗口(引用自《Android内核剖析》)。如图(图片同样来自《Android内核剖析》):
如果是按键消息,InputDispatcher会先回调InputManager中定义的回调函数,这既会回调InputMonitor中的回调函数,这又会调用WmS中定义的相关函数。对于系统按键消息,比如"Home"键、电话按键等,WmS内部会按照默认的方式处理,如果事件被消耗,InputDispatcher则不会继续把这些按键消息传递给客户窗口对于触摸屏消息,InputDispatcher则直接传递给客户窗口。
InputManagerService.java
//按键事件的回调,前面我们提到回调对象是InputMonitor
// Native callback.
private long interceptKeyBeforeDispatching(InputWindowHandle focus,
KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
InputMonitor.java
@Override
public long interceptKeyBeforeDispatching(
InputWindowHandle focus, KeyEvent event, int policyFlags) {
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
//回调Wms,mPolicy在SystemServer初始化时创建,为PhoneWindowManager类,可以看到其中对各种按键的处理
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
当InputDispatcher直接传递给窗口时,通过findTouchedWindowAtLocked方法找到当前获得焦点的窗口,然后通过Pipe(管道)进行ipc。也就是说在native层也会维护一组窗口相关的信息。我们回到Wms添加窗口的过程:
WindowManagerService.java
public int addWindow(...) {
//创建WindowState,构造方法中会创建InputWindowHandle
final WindowState win = new WindowState(...);
...
//建立管道通信,其中outInputChannel是从app进程传递过来的
win.openInputChannel(outInputChannel是从app进程);
...
//更新窗口焦点改变的信息到native层,同理当窗口切换或者销毁时也会更新
mInputMonitor.updateInputWindowsLw(false /*force*/);
...
}
WindowState.java
void openInputChannel(InputChannel outInputChannel) {
...
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
/*Channel[0]保存在server端*/
mInputWindowHandle.inputChannel = inputChannels[0];
...
/* Channel[1]返回给app的ViewRootImpl端*/
mClientChannel.transferTo(outInputChannel);
...
/*注册到InputManagerService native层*/
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
可以看到,在Wms中维护一组WindowState,用于窗口的创建、销毁切换,而在InputManagerService则维护一组InputWindowHandle,用于事件的分发。
我们回到ViewRootImpl中。在添加窗口时,我们调用了setView方法。
ViewRootImpl.java
public void setView(...) {
...
//InputChannel类,是管道在java层的实现
mInputChannel = new InputChannel();
...
//查看IWindowSession.aidl会发现这里的mInputChannel标注着out,
//也就是在另一个进程的改变都会同步到本进程
res = mWindowSession.addToDisplay(...,mInputChannel);
...
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//建立管道的回调,见下方
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
}
}
ViewRootImpl$WindowInputEventReceiver.java
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
//见下方
super(inputChannel, looper);
}
InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
...
//建立管道的回调,native层收到事件就会回调给InputEventReceiver
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
...
}
当InputManagerService的InputDispatcher通过管道将消息传递给app进程,app进程的管道会回调InputEventReceiver(也就是WindowInputEventReceiver),进行事件分发。后面的代码可以说是责任链模式的标准答案,非常精彩读者可以自行学习。
总结
Android Framework可以说是一个庞大的工程,如果我们在一开始的过程中就陷入细节,就无法走通一条路。君子善假于物也,借助大佬的研究学习成果,我们可以先学习整体的框架,有必要时再各个击破。非常感谢各个大佬,下面的参考文献可能有所遗漏,在此致歉!希望本篇文章对于读者有所帮助。
本文转自 https://juejin.cn/post/6844903717301387272,如有侵权,请联系删除。