189 8069 5689

android源码分析,Android源码分析

Android源码解析Window系列第(一)篇---Window的基本认识和Activity的加载流程

您可能听说过View ,ViewManager,Window,PhoneWindow,WindowManager,WindowManagerService,可是你知道这几个类是什么关系,干嘛用的。概括的来说,View是放在Window中的,Window是一个抽象类,它的具体实现是PhoneWindow,PhoneWindow还有个内部类DecorView,WindowManager是一个interface,继承自ViewManager,它是外界访问Window的入口,,提供了add/remove/updata的方法操作View,WindowManager与WindowManagerSerice是个跨进程的过程,WindowManagerService的职责是对系统中的所有窗口进行管理。如果您不太清楚,建议往下看,否则就不要看了。

博野网站制作公司哪家好,找成都创新互联!从网页设计、网站建设、微信开发、APP开发、响应式网站设计等网站项目制作,到程序开发,运营维护。成都创新互联从2013年创立到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选成都创新互联

Android系统的Window有很多种,大体上来说,Framework定义了三种窗口类型;

这就是Framework定义了三种窗口类型,这三种类型定义在WindowManager的内部类LayoutParams中,WindowManager讲这三种类型 进行了细化,把每一种类型都用一个int常量来表示,这些常量代表窗口所在的层,WindowManagerService在进行窗口叠加的时候,会按照常量的大小分配不同的层,常量值越大,代表位置越靠上面, 所以我们可以猜想一下,应用程序Window的层值常量要小于子Window的层值常量,子Window的层值常量要小于系统Window的层值常量。 Window的层级关系如下所示。

上面说了Window分为三种,用Window的type区分,在搞清楚Window的创建之前,我们需要知道怎么去描述一个Window,我们就把Window当做一个实体类,给我的感觉,它必须要下面几个字段。

实际上WindowManager.LayoutParams对Window有很详细的定义。

提取几个重要的参数

Window是一个是一个抽象的概念,千万不要认为我们所看到的就是Window,我们平时所看到的是视图,每一个Window都对应着一个View,View和Window通过ViewRootImpl来建立联系。有了View,Window的存在意义在哪里呢,因为View不能单独存在,它必须依附着Window,所以有视图的地方就有Window,比如Activity,一个Dialog,一个PopWindow,一个菜单,一个Toast等等。

通过上面我们知道视图和Window的关系,那么有一个问题,是先有视图,还是先有Window。这个答案只有在源码中找了。应用程序的入口类是ActivityThread,在ActivityThread中有performLaunchActivity来启动Activity,这个performLaunchActivity方法内部会创建一个Activity。

如果activity不为null,就会调用attach,在attach方法中通过PolicyManager创建了Window对象,并且给Window设置了回调接口。

PolicyManager的实现类是Policy

这样Window就创建出来了, 所以先有Window,后有视图,视图依赖Window存在 ,再说一说视图(Activity)为Window设置的回调接口。

Activity实现了这个回调接口,当Window的状态发生变化的时候,就会回调Activity中实现的这些接口,有些回调接口我们还是熟悉的,dispatchTouchEvent,onAttachedToWindow,onDetachedFromWindow等。

下面分析view是如何附属到window上的,通过上面可以看到,在attach之后就要执行callActivityOnCreate,在onCreate中我们会调用setContentView方法。

getWindow获取了Window对象,Window的具体实现类是PhoneWindow,所以要看PhoneWindow的setContentView方法。

这里涉及到一个mContentParent变量,他是一个DecorView的一部分,DecorView是PhoneWindow的一个内部类,我先介绍一下关于DecorView的知识。

DecorView是Activity的顶级VIew,DecorView继承自FrameLayout,在DecorView中有上下两个部分,上面是标题栏,下面是内容栏,我们通过PhoneWindow的setContentView所设置的布局文件是加到内容栏(mContentParent)里面的,View层的事件都是先经过DecorView在传递给我们的View的。

OK在回到setContentView的源码分析,我们可以得到Activity的Window创建需要三步。

- 1、 如果没有DecorView,在installDecor中创建DecorView。

- 2、将View添加到decorview中的mContentParent中。

- 3、回调Activity的onContentChanged接口。

先看看第一步,installDecor的源码

installDecor中调用了generateDecor,继续看

直接给new一个DecorView,有了DecorView之后,就可以加载具体的布局文件到DecorView中了,具体的布局文件和系统和主题有关系。

在看第二步,将View添加到decorview中的mContentParent中。

直接将Activity视图加到DecorView的mContentParent中,最后一步,回调Activity的onContentChanged接口。在Activity中寻找onContentChanged方法,它是个空实现,我们可以在子Activity中处理。

到此DecorView被创建完毕,我们一开始从Thread中的handleLaunchActivity方法开始分析,首先加载Activity的字节码文件,利用反射的方式创建一个Activity对象,调用Activity对象的attach方法,在attach方法中,创建系统需要的Window并为设置回调,这个回调定义在Window之中,由Activity实现,当Window的状态发生变化的时候,就会回调Activity实现的这些回调方法。调用attach方法之后,Window被创建完成,这时候需要关联我们的视图,在handleLaunchActivity中的attach执行之后就要执行handleLaunchActivity中的callActivityOnCreate,在onCreate中我们会调用setContentView方法。通过setContentView,创建了Activity的顶级View---DecorView,DecorView的内容栏(mContentParent)用来显示我们的布局。 这个是我们上面分析得到了一个大致流程,走到这里,这只是添加的过程,还要有一个显示的过程,显示的过程就要调用handleLaunchActivity中的handleResumeActivity方法了。最后会调用makeVisible方法。

这里面首先拿到WindowManager对象,用tWindowManager 的父接口ViewManager接收,ViewManager可以

最后调用 mDecor.setVisibility(View.VISIBLE)设置mDecor可见。到此,我们终于明白一个Activity是怎么显示在我们的面前了。

参考链接:

从零开始仿写一个抖音App——Android绘制机制以及Surface家族源码全解析

本篇文章分为以下章节,读者可以按需阅读

阅读须知

图1就是 Android 屏幕显示的抽象示意图,这里我来解释一下:

Android 的两种常用绘图机制:

其实源码的主要流程都在图3中,我下面讲的东西算是对图3的补充和说明。另外强烈建议结合 Android 源码阅读本章节。

**这里我们以 View 的创建流程为例,讲述一下 Surface 在这个过程中的创建流程,Surface 的创建流程如图5所示。 **

我们都知道 Surface 可以通过 lockCanvas 和 unlockCanvasAndPost 这两个 api 来再通过 Canvas 来绘制图像,这一节我就通过这两个 api 来讲讲 Surface 的绘制流程,整个流程如图6所示。

图7是 ST 与 Surface、SV、TV 等等组件结合的概览图,我这里简单解释一下:

我将根据图8的流程来讲解 ST 的创建与使用

和前面一样,本小节接下来的分析也都是顺着图9来的

Android源码解析RPC系列(一)---Binder原理

看了几天的Binder,决定有必要写一篇博客,记录一下学习成果,Binder是Android中比较综合的一块知识了,目前的理解只限于JAVA层。首先Binder是干嘛用的?不用说,跨进程通信全靠它,操作系统的不同进程之间,数据不共享,对于每个进程来说,它都天真地以为自己独享了整个系统,完全不知道其他进程的存在,进程之间需要通信需要某种系统机制才能完成,在Android整个系统架构中,采用了大量的C/S架构的思想,所以Binder的作用就显得非常重要了,但是这种机制为什么是Binder呢?在Linux中的RPC方式有管道,消息队列,共享内存等,消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,这样就有两次拷贝过程。共享内存不需要拷贝,但控制复杂,难以使用。Binder是个折中的方案,只需要拷贝一次就行了。其次Binder的安全性比较好,好在哪里,在下还不是很清楚,基于安全性和传输的效率考虑,选择了Binder。Binder的英文意思是粘结剂,Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,这个进程一般是Server端,该对象提供了一套方法用以实现对服务的请求,而它的引用却遍布于系统的各个进程(Client端)之中,这样Client通过Binder的引用访问Server,所以说,Binder就像胶水一样,把系统各个进程粘结在一起了,废话确实有点多。

为了从而保障了系统的安全和稳定,整个系统被划分成内核空间和用户空间

内核空间:独立于普通的应用程序,可以访问受保护的内存空间,有访问底层硬件设备的所有权限。

用户空间:相对与内核空间,上层运用程序所运行的空间就是用户空间,用户空间访问内核空间的唯一方式就是系统调用。一个4G的虚拟地址空间,其中3G是用户空间,剩余的1G是内核空间。如果一个用户空间想与另外一个用户空间进行通信,就需要内核模块支持,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动,虽然叫做Binder驱动,但是和硬件并没有什么关系,只是实现方式和设备驱动程序是一样的,提供了一些标准文件操作。

在写AIDL的时候,一般情况下,我们有两个进程,一个作为Server端提供某种服务,然后另外一个进程作为Client端,连接Server端之后,就 可以使用Server里面定义的服务。这种思想是一种典型的C/S的思想。值得注意的是Android系统中的Binder自身也是C/S的架构,也有Server端与Client端。一个大的C/S架构中,也有一个小的C/S架构。

先笼统的说一下,在整个Binder框架中,由系列组件组成,分别是Client、Server、ServiceManager和Binder驱动程序,其中Client、Server和ServiceManager运行在用户空间,Binder驱动程序运行内核空间。运行在用户空间中的Client、Server和ServiceManager,是在三个不同进程中的,Server进程中中定义了服务提供给Client进程使用,并且Server中有一个Binder实体,但是Server中定义的服务并不能直接被Client使用,它需要向ServiceManager注册,然后Client要用服务的时候,直接向ServiceManager要,ServiceManager返回一个Binder的替身(引用)给Client,这样Client就可以调用Server中的服务了。

场景 :进程A要调用进程B里面的一个draw方法处理图片。

分析 :在这种场景下,进程A作为Client端,进程B做为Server端,但是A/B不在同一个进程中,怎么来调用B进程的draw方法呢,首先进程B作为Server端创建了Binder实体,为其取一个字符形式,可读易记的名字,并将这个Binder连同名字以数据包的形式通过Binder驱动发送给ServiceManager,也就是向ServiceManager注册的过程,告诉ServiceManager,我是进程B,拥有图像处理的功能,ServiceManager从数据包中取出名字和引用以一个注册表的形式保留了Server进程的注册信息。为什么是以数据包的形式呢,因为这是两个进程,直接传递对象是不行滴,只能是一些描述信息。现在Client端进程A联系ServiceManager,说现在我需要进程B中图像处理的功能,ServiceManager从注册表中查到了这个Binder实体,但是呢,它并不是直接把这个Binder实体直接给Client,而是给了一个Binder实体的代理,或者说是引用,Client通过Binder的引用访问Server。分析到现在,有个关键的问题需要说一下,ServiceManager是一个进程,Server是另一个进程,Server向ServiceManager注册Binder必然会涉及进程间通信。当前实现的是进程间通信却又要用到进程间通信,这就好象蛋可以孵出鸡前提却是要找只鸡来孵蛋,确实是这样的,ServiceManager中预先有了一个自己的Binder对象(实体),就是那只鸡,然后Server有个Binder对象的引用,就是那个蛋,Server需要通过这个Binder的引用来实现Binder的注册。鸡就一只,蛋有很多,ServiceManager进程的Binder对象(实体)仅有一个,其他进程所拥有的全部都是它的代理。同样一个Server端Binder实体也应该只有一个,对应所有Client端全部都是它的代理。

我们再次理解一下Binder是什么?在Binder通信模型的四个角色里面;他们的代表都是“Binder”,一个Binder对象就代表了所有,包括了Server,Client,ServiceManager,这样,对于Binder通信的使用者而言,不用关心实现的细节。对Server来说,Binder指的是Binder实体,或者说是本地对象,对于Client来说,Binder指的是Binder代理对象,也就是Binder的引用。对于Binder驱动而言,在Binder对象进行跨进程传递的时候,Binder驱动会自动完成这两种类型的转换。

简单的总结一下,通过上面一大段的分析,一个Server在使用的时候需要经历三个阶段

1、定义一个AIDL文件

Game.aidl

GameManager .aidl

2、定义远端服务Service

在远程服务中的onBind方法,实现AIDL接口的具体方法,并且返回Binder对象

3、本地创建连接对象

以上就是一个远端服务的一般套路,如果是在两个进程中,就可以进程通信了,现在我们分析一下,这个通信的流程。重点是GameManager这个编译生成的类。

从类的关系来看,首先接口GameManager 继承 IInterface ,IInterface是一个接口,在GameManager内部有一个内部类Stub,Stub继承了Binder,(Binder实现了IBinder),并且实现了GameManager接口,在Stub中还有一个内部类Proxy,Proxy也实现了GameManager接口,一个整体的结构是这样的

现在的问题是,Stub是什么?Proxy又是什么?在上面说了在Binder通信模型的四个角色里面;他们的代表都是“Binder”,一个Binder对象就代表了所有,包括了Server,Clinet,ServiceManager,为了两个进程的通信,系统给予的内核支持是Binder,在抽象一点的说,Binder是系统开辟的一块内存空间,两个进程往这块空间里面读写数据就行了,Stub从Binder中读数据,Proxy向Binder中写数据,达到进程间通信的目的。首先我们分析Stub。

Stub 类继承了Binder ,说明了Stub有了跨进程传输的能力,实现了GameManager接口,说明它有了根据游戏ID查询一个游戏的能力。我们在bind一个Service之后,在onServiceConnecttion的回调里面,就是通过asInterface方法拿到一个远程的service的。

asInterface调用queryLocalInterface。

mDescriptor,mOwner其实是Binder的成员变量,Stub继承了Binder,在构造函数的时候,对着两个变量赋的值。

如果客户端和服务端是在一个进程中,那么其实queryLocalInterface获取的就是Stub对象,如果不在一个进程queryLocalInterface查询的对象肯定为null,因为不同进程有不同虚拟机,肯定查不到mOwner对象的,所以这时候其实是返回的Proxy对象了。拿到Stub对象后,通常在onServiceConnected中,就把这个对象转换成我们多定义AIDL接口。

比如我们这里会转换成GameManager,有了GameManager对象,就可以调用后querryGameById方法了。如果是一个进程,那直接调用的是自己的querryGameById方法,如果不是一个进程,那调用了就是代理的querryGameById方法了。

看到其中关键的一行是

mRemote就是一个IBinder对象,相对于Stub,Proxy 是组合关系(HAS-A),内部有一个IBinder对象mRemote,Stub是继承关系(IS-A),直接实现了IBinder接口。

transact是个native方法,最终还会回掉JAVA层的onTransact方法。

onTransact根据调用号(每个AIDL函数都有一个编号,在跨进程的时候,不会传递函数,而是传递编号指明调用哪个函数)调用相关函数;在这个例子里面,调用了Binder本地对象的querryGameById方法;这个方法将结果返回给驱动,驱动唤醒挂起的Client进程里面的线程并将结果返回。于是一次跨进程调用就完成了。

***Please accept mybest wishes for your happiness and success ! ***

[Android源码分析] - 异步通信Handler机制

一、问题:在Android启动后会在新进程里创建一个主线程,也叫UI线程( 非线程安全 )这个线程主要负责监听屏幕点击事件与界面绘制。当Application需要进行耗时操作如网络请求等,如直接在主线程进行容易发生ANR错误。所以会创建子线程来执行耗时任务,当子线程执行完毕需要通知UI线程并修改界面时,不可以直接在子线程修改UI,怎么办?

解决方法:Message Queue机制可以实现子线程与UI线程的通信。

该机制包括Handler、Message Queue、Looper。Handler可以把消息/ Runnable对象 发给Looper,由它把消息放入所属线程的消息队列中,然后Looper又会自动把消息队列里的消息/Runnable对象 广播 到所属线程里的Handler,由Handler处理接收到的消息或Runnable对象。

1、Handler

每次创建Handler对象时,它会自动绑定到创建它的线程上。如果是主线程则默认包含一个Message Queue,否则需要自己创建一个消息队列来存储。

Handler是多个线程通信的信使。比如在线程A中创建AHandler,给它绑定一个ALooper,同时创建属于A的消息队列AMessageQueue。然后在线程B中使用AHandler发送消息给ALooper,ALooper会把消息存入到AMessageQueue,然后再把AMessageQueue广播给A线程里的AHandler,它接收到消息会进行处理。从而实现通信。

2、Message Queue

在主线程里默认包含了一个消息队列不需要手动创建。在子线程里,使用Looper.prepare()方法后,会先检查子线程是否已有一个looper对象,如果有则无法创建,因为每个线程只能拥有一个消息队列。没有的话就为子线程创建一个消息队列。

Handler类包含Looper指针和MessageQueue指针,而Looper里包含实际MessageQueue与当前线程指针。

下面分别就UI线程和worker线程讲解handler创建过程:

首先,创建handler时,会自动检查当前线程是否包含looper对象,如果包含,则将handler内的消息队列指向looper内部的消息队列,否则,抛出异常请求执行looper.prepare()方法。

- 在 UI线程 中,系统自动创建了Looper 对象,所以,直接new一个handler即可使用该机制;

- 在 worker线程 中,如果直接创建handler会抛出运行时异常-即通过查‘线程-value’映射表发现当前线程无looper对象。所以需要先调用Looper.prepare()方法。在prepare方法里,利用ThreadLocalLooper对象为当前线程创建一个Looper(利用了一个Values类,即一个Map映射表,专为thread存储value,此处为当前thread存储一个looper对象)。然后继续创建handler, 让handler内部的消息队列指向该looper的消息队列(这个很重要,让handler指向looper里的消息队列,即二者共享同一个消息队列,然后handler向这个消息队列发送消息,looper从这个消息队列获取消息) 。然后looper循环消息队列即可。当获取到message消息,会找出message对象里的target,即原始发送handler,从而回调handler的handleMessage() 方法进行处理。

- handler与looper共享消息队列 ,所以handler发送消息只要入列,looper直接取消息即可。

- 线程与looper映射表 :一个线程最多可以映射一个looper对象。通过查表可知当前线程是否包含looper,如果已经包含则不再创建新looper。

5、基于这样的机制是怎样实现线程隔离的,即在线程中通信呢。 

核心在于 每一个线程拥有自己的handler、message queue、looper体系 。而 每个线程的Handler是公开 的。B线程可以调用A线程的handler发送消息到A的共享消息队列去,然后A的looper会自动从共享消息队列取出消息进行处理。反之一样。

二、上面是基于子线程中利用主线程提供的Handler发送消息出去,然后主线程的Looper从消息队列中获取并处理。那么还有另外两种情况:

1、主线程发送消息到子线程中;

采用的方法和前面类似。要在子线程中实例化AHandler并设定处理消息的方法,同时由于子线程没有消息队列和Looper的轮询,所以要加上Looper.prepare(),Looper.loop()分别创建消息队列和开启轮询。然后在主线程中使用该AHandler去发送消息即可。

2、子线程A与子线程B之间的通信。

1、 Handler为什么能够实现不同线程的通信?核心点在哪?

不同线程之间,每个线程拥有自己的Handler、消息队列和Looper。Handler是公共的,线程可以通过使用目标线程的Handler对象来发送消息,这个消息会自动发送到所属线程的消息队列中去,线程自带的Looper对象会不断循环从里面取出消息并把消息发送给Handler,回调自身Handler的handlerMessage方法,从而实现了消息的线程间传递。

2、 Handler的核心是一种事件激活式(类似传递一个中断)的还是主要是用于传递大量数据的?重点在Message的内容,偏向于数据传输还是事件传输。

目前的理解,它所依赖的是消息队列,发送的自然是消息,即类似事件中断。

0、 Android消息处理机制(Handler、Looper、MessageQueue与Message)

1、 Handler、Looper源码阅读

2、 Android异步消息处理机制完全解析,带你从源码的角度彻底理解

谢谢!

wingjay

![](;s=460)


本文标题:android源码分析,Android源码分析
本文网址:http://cdxtjz.com/article/dsdcodj.html

其他资讯