本文旨在将Framework的框架描绘出来,次要是记录我一段时间对于android framework的学习,心愿抛砖引玉,对于读者有肯定的帮忙。
前言
写在后面:
1、有没有必要学习linux内核?
我认为是很有必要的。学习linux内核有助于咱们加深对一些概念的了解,比方“过程”、“线程”。举荐入门的教程:中国大学MOOC李治军老师的操作系统课程
2、有没有必要本人编译android源码?
非必须,能够间接用android studio查看sdk源码,除非要调试一些性能。然而要亲自操练起来能力更相熟。
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所有的流动。
注1:Andoird 4.4引入ART
SystemServer 中创立了一个 Socket2 客户端,并有AmS负责管理该客户端,之后所有的 Dalvik 过程都将通过该 Socket 客户端间接被启动。当要启动新的 APK 过程时 ,AmS 中会通过该 Socket 客户端向 zygote 过程的 Socket服务端发送一个启动命令,而后zygote会孵化出新的过程。
注2:此处波及Android过程中通信的一种办法Socket,学过计算机网络的读者应该对此有肯定的概念。当前还会提及pipe、binder两种过程通信办法,无论如何,它们最终的目标都是为了让开发者跨过程调用时都像是在进行本地调用。至于它们的优缺点以及实现形式,读者能够自行探索。
1、zygote的启动
后面咱们提到内核初始化时,会启动在init.rc文件中配置的程序,zygote相干的配置信息如下:
service zygote /system/bin/app\_process -Xzygote /system/bin --zygote --start-system-server
简略阐明一下这个语句的意思,内核会执行/system/bin/app\_process3目录下的程序,启动一个叫zygote的服务。其中参数--start-system-server, 仅在指定 -- zygote 参数时才无效,意思是告知Zygotelnit启动结束后孵化出第一个过程SystemServer。由此可知,zygote启动后做的事件之一就是启动SystemServer。
注3:Android反对64位当前,会依据不同的ABI别离执行/system/bin/app\_process32和/system/bin/app\_process64目录。
当 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的工具类缓存起来。
注4:这里不是指Android的四大组件之一的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办法。
注5:更精确的说Activity是ContextWrapper的子类,而ContextWrapper不过是个代理类。实际上Activity具体的操作都是由其成员变量mBase实现,而mBase是一个ContextImpl类(继承Context)。所以如果Context是一个Interface的话,那么这就是一个规范的动态代理模式。
Ams会调用到startActivityAsUser办法,而后调用ActivityStarter的startActivityMayWait办法。
ActivityStarter.javafinal 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.javavoid 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.javafinal 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.javaprivate 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.javaRunnable 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.javapublic static final Runnable zygoteInit(...){ ... return RuntimeInit.applicationInit(...);}RuntimeInit.javaprotected static Runnable applicationInit(...) { //args.startClass就是咱们之前提到的entryPoint,也就是"android.app.ActivityThread" //由此可知app第一个被调用的办法是ActivityThread的main办法,这就是应用程序的入口。 return findStaticMain(args.startClass, args.startArgs, classLoader);}
咱们终于回到了本人的过程,也很明确ActivityThread的main办法(提到main办法,总是有一种无以言表的亲切感)就是应用程序的入口。接着持续摸索。
ActivityThread.javapublic 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.javaprivate final boolean attachApplicationLocked(...){ ... //通过binder IPC告诉ActivityThread创立Application thread.bindApplication(...); ... mStackSupervisor.attachApplicationLocked(app); ...}ActivityStackSupervisor.javaboolean attachApplicationLocked(...) throws RemoteException { ... //看咱们发现了什么,似曾相识。没错就是下面咱们留下来的问题。 //情理很简略,咱们在启动一个activity的时候发现过程未启动, //当咱们启动过程后当然得重新启动activity realStartActivityLocked(...); ...}final boolean realStartActivityLocked(...){ ... //这里的thread是一个binder类,和ActivityThread是对应的 app.thread.scheduleLaunchActivity(...); ...}
从新回到app过程,先看创立Application的流程
ActivityThread$ApplicationThread.javapublic final void bindApplication(...){ //通过handler调用到handleBindApplication办法,接着调用到performLaunchActivity办法 sendMessage(H.BIND_APPLICATION, data);}ActivityThread.javaprivate void handleBindApplication(AppBindData data) { ... //创立Instrumentation mInstrumentation = new Instrumentation(); ... //创立Application,回调attachBaseContext办法 app = data.info.makeApplication(data.restrictedBackupMode, null); ... //回调onCreate办法 mInstrumentation.callApplicationOnCreate(app); ...}
接着看创立Activity的流程
ActivityThread$ApplicationThread.javapublic final void scheduleLaunchActivity(...){ //通过handler调用到handleLaunchActivity办法,接着调用到performLaunchActivity办法 sendMessage(H.LAUNCH_ACTIVITY, r);}ActivityThread.javaprivate 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(...);}
至此startActivity的框架已形容结束。
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@Overridepublic 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.javaprivate 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.javavoid makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); //最初会调用到WindowManagerGlobal的addView wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }WindowManagerGlobal.javapublic 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.javapublic void setView(...) { ... //上面大节接着讲 requestLayout(); ... //ipc到Wms,Session类为服务端 //mInputChannel是一个InputChannel类,是管道在java层的实现,前面讲到Android事件的时候会细说 res = mWindowSession.addToDisplay(...,mInputChannel); ...}Session.java@Overridepublic int addToDisplay(...) { //见下方 return mService.addWindow(...);}WindowManagerService.javapublic 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.javavoid 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.javaprivate 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:
Surface是原始图像缓冲区(raw buffer)的一个句柄。也就是说Surface对应一段内存,这段内存的数据就是要绘制的内容,Surface 类实质上就是示意一个立体
- Canvas:
咱们晓得绘制不同图案显然是一种橾作,而不是一段数据。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.javaprivate 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.javapublic 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.javapublic 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.cppInputManager::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@Overridepublic 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.javapublic int addWindow(...) { //创立WindowState,构造方法中会创立InputWindowHandle final WindowState win = new WindowState(...); ... //建设管道通信,其中outInputChannel是从app过程传递过去的 win.openInputChannel(outInputChannel是从app过程); ... //更新窗口焦点扭转的信息到native层,同理当窗口切换或者销毁时也会更新 mInputMonitor.updateInputWindowsLw(false /*force*/); ...}WindowState.javavoid 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.javapublic 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.javapublic WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { //见下方 super(inputChannel, looper);}InputEventReceiver.javapublic 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的学习,心愿抛砖引玉,对于读者有肯定的帮忙。
前言
写在后面:
1、有没有必要学习linux内核?
我认为是很有必要的。学习linux内核有助于咱们加深对一些概念的了解,比方“过程”、“线程”。举荐入门的教程:中国大学MOOC李治军老师的操作系统课程
2、有没有必要本人编译android源码?
非必须,能够间接用android studio查看sdk源码,除非要调试一些性能。然而要亲自操练起来能力更相熟。
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所有的流动。
注1:Andoird 4.4引入ART
SystemServer 中创立了一个 Socket2 客户端,并有AmS负责管理该客户端,之后所有的 Dalvik 过程都将通过该 Socket 客户端间接被启动。当要启动新的 APK 过程时 ,AmS 中会通过该 Socket 客户端向 zygote 过程的 Socket服务端发送一个启动命令,而后zygote会孵化出新的过程。
注2:此处波及Android过程中通信的一种办法Socket,学过计算机网络的读者应该对此有肯定的概念。当前还会提及pipe、binder两种过程通信办法,无论如何,它们最终的目标都是为了让开发者跨过程调用时都像是在进行本地调用。至于它们的优缺点以及实现形式,读者能够自行探索。
1、zygote的启动
后面咱们提到内核初始化时,会启动在init.rc文件中配置的程序,zygote相干的配置信息如下:
service zygote /system/bin/app\_process -Xzygote /system/bin --zygote --start-system-server
简略阐明一下这个语句的意思,内核会执行/system/bin/app\_process3目录下的程序,启动一个叫zygote的服务。其中参数--start-system-server, 仅在指定 -- zygote 参数时才无效,意思是告知Zygotelnit启动结束后孵化出第一个过程SystemServer。由此可知,zygote启动后做的事件之一就是启动SystemServer。
注3:Android反对64位当前,会依据不同的ABI别离执行/system/bin/app\_process32和/system/bin/app\_process64目录。
当 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的工具类缓存起来。
注4:这里不是指Android的四大组件之一的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办法。
注5:更精确的说Activity是ContextWrapper的子类,而ContextWrapper不过是个代理类。实际上Activity具体的操作都是由其成员变量mBase实现,而mBase是一个ContextImpl类(继承Context)。所以如果Context是一个Interface的话,那么这就是一个规范的动态代理模式。
Ams会调用到startActivityAsUser办法,而后调用ActivityStarter的startActivityMayWait办法。
ActivityStarter.javafinal 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.javavoid 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.javafinal 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.javaprivate 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.javaRunnable 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.javapublic static final Runnable zygoteInit(...){ ... return RuntimeInit.applicationInit(...);}RuntimeInit.javaprotected static Runnable applicationInit(...) { //args.startClass就是咱们之前提到的entryPoint,也就是"android.app.ActivityThread" //由此可知app第一个被调用的办法是ActivityThread的main办法,这就是应用程序的入口。 return findStaticMain(args.startClass, args.startArgs, classLoader);}
咱们终于回到了本人的过程,也很明确ActivityThread的main办法(提到main办法,总是有一种无以言表的亲切感)就是应用程序的入口。接着持续摸索。
ActivityThread.javapublic 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.javaprivate final boolean attachApplicationLocked(...){ ... //通过binder IPC告诉ActivityThread创立Application thread.bindApplication(...); ... mStackSupervisor.attachApplicationLocked(app); ...}ActivityStackSupervisor.javaboolean attachApplicationLocked(...) throws RemoteException { ... //看咱们发现了什么,似曾相识。没错就是下面咱们留下来的问题。 //情理很简略,咱们在启动一个activity的时候发现过程未启动, //当咱们启动过程后当然得重新启动activity realStartActivityLocked(...); ...}final boolean realStartActivityLocked(...){ ... //这里的thread是一个binder类,和ActivityThread是对应的 app.thread.scheduleLaunchActivity(...); ...}
从新回到app过程,先看创立Application的流程
ActivityThread$ApplicationThread.javapublic final void bindApplication(...){ //通过handler调用到handleBindApplication办法,接着调用到performLaunchActivity办法 sendMessage(H.BIND_APPLICATION, data);}ActivityThread.javaprivate void handleBindApplication(AppBindData data) { ... //创立Instrumentation mInstrumentation = new Instrumentation(); ... //创立Application,回调attachBaseContext办法 app = data.info.makeApplication(data.restrictedBackupMode, null); ... //回调onCreate办法 mInstrumentation.callApplicationOnCreate(app); ...}
接着看创立Activity的流程
ActivityThread$ApplicationThread.javapublic final void scheduleLaunchActivity(...){ //通过handler调用到handleLaunchActivity办法,接着调用到performLaunchActivity办法 sendMessage(H.LAUNCH_ACTIVITY, r);}ActivityThread.javaprivate 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(...);}
至此startActivity的框架已形容结束。
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@Overridepublic 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.javaprivate 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.javavoid makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); //最初会调用到WindowManagerGlobal的addView wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }WindowManagerGlobal.javapublic 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.javapublic void setView(...) { ... //上面大节接着讲 requestLayout(); ... //ipc到Wms,Session类为服务端 //mInputChannel是一个InputChannel类,是管道在java层的实现,前面讲到Android事件的时候会细说 res = mWindowSession.addToDisplay(...,mInputChannel); ...}Session.java@Overridepublic int addToDisplay(...) { //见下方 return mService.addWindow(...);}WindowManagerService.javapublic 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.javavoid 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.javaprivate 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:
Surface是原始图像缓冲区(raw buffer)的一个句柄。也就是说Surface对应一段内存,这段内存的数据就是要绘制的内容,Surface 类实质上就是示意一个立体
- Canvas:
咱们晓得绘制不同图案显然是一种橾作,而不是一段数据。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.javaprivate 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.javapublic 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.javapublic 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.cppInputManager::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@Overridepublic 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.javapublic int addWindow(...) { //创立WindowState,构造方法中会创立InputWindowHandle final WindowState win = new WindowState(...); ... //建设管道通信,其中outInputChannel是从app过程传递过去的 win.openInputChannel(outInputChannel是从app过程); ... //更新窗口焦点扭转的信息到native层,同理当窗口切换或者销毁时也会更新 mInputMonitor.updateInputWindowsLw(false /*force*/); ...}WindowState.javavoid 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.javapublic 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.javapublic WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { //见下方 super(inputChannel, looper);}InputEventReceiver.javapublic 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,如有侵权,请分割删除。