Service 相关资料:
1.service是什么?
Service是android 四大组件之一,它可以在后台执行长时间运行操作而没有用户界面的组件,service的启动方式有两种,startservice和bindservice,要注意的是service服务与其他应用程序对象一样,在其托管进程的主线程中运行。这意味着,如果你的服务要执行任何CPU密集型(例如 MP3 播放)或阻塞(例如网络)操作,它应该在Service中再创建一个子线程,然后在这里去处理耗时操作就没问题了。
2.service的启动
显式启动通过类名称来启动,需要在Intent中指明Service所在的类,并调用startService (lntent)启动service,通过该方式启动Service,访问者与Service之间没有关联,即使访问者退出了,Service也仍然运行。显式启动停止Service,需要将启动Service的Intent传递给stopService (Intent)函数。
通过bindservice方法启动service,需要传递ServiceConnnection对象,当绑定成功后,系统将调用serviceConnnection的onServiceConnected ()方法,当绑定意外断开后,系统将调用ServiceConnnection中的onServiceDisconnected方法。通过该方式启动Service,访问者与Service绑定在一起,访问者一旦退出了,Service也就终止了。取消绑定仅需要使用unbindService()方法,并将ServiceConnnection传递给unbindService()方法。
但需要注意的是,unbindService()方法成功后,系统并不会调用onServiceConnected(),因为onServiceConnected()仅在意外断开绑定时才被调用。当调用者通过unbindService()函数取消绑定Service时,onUnbind()函数将被调用。如果onUnbind()函数返回true,则表示重新绑定服务时,onRebind ()函数将被调用。
3.service的生命周期
startService启动的生命周期,onCreate() 当Service第一次被创建时,由系统调用。onStartCommand() 当startService方法启动Service时,该方法被调用。onDestroy() 当Service不再使用时,由系统调用。
bindService启动的生命周期
onCreate() 当Service被创建时,由系统调用。
onBind() 当bindService方法启动Service时,该方法被调用。
onUnbind() 当unbindService方法解除绑定时,该方法被调用。
onDestroy() 当Service不再使用时,由系统调用。
3.service和thread的区别
Service是安卓中系统的组件,它运行在独立进程的主线程中,不可以执行耗时操作。
Thread是程序执行的最小单元,分配CPU的基本单位,可以开启子线程执行耗时操作。
Service在不同Activity中可以获取自身实例,可以方便的对Service进行操作。
Thread在不同的Activity中难以获取自身实例,如果Activity被销毁,Thread实例就很难再获取得到。
4.Intentservice
IntentService是 Scrvice 的子类,因此它不是普通的Service,它比普通的Service增加了额外的功能。service本身存在两个问题,service不会专门启动一个单独的进程,service与它所在的应用位于同一个进程中,service不是一条新的线程,因此不应该在service中直接处理耗时任务。而intentservice会创建单独的worker线程来处理所有的intent请求,intentservice会创建单独的worker线程来处理onhandleintent方法实现的代码,
5.service的数据传递
程序组件可以通过传递一个intent给startservice来实现组件与service之前的数据传递,service是通过系统调用的onStartCommand方法接受传递的intent,完成整个数据传递过程。
6.service 文档小信息
通过在 标签里将 android:exported 设置为 false。可以防止其他的程序来启动你的 Service。
通过 started 方式来启动 Service
组件(例如 activity, fragment) 通过调用 startService() 方法,系统随之调用 onStartCommand() 方法来实现 started 方式启动 Service。
当 Service 以该形式启动后,Service 的整个生命周期是完全独立的,即便启动 Service 的组件已经被销毁了,Service 还是可以在后台无限的运行的。但开发者应该在 Service 中的操作执行完成后,调用 stopSelf() 或其它组件调用 stopService() 的方式来结束该 Service。
程序组件(例如 activity) 可以通过传递一个 Intent 给 startService(),来实现组件与 Service 之前的数据传递。Service 是通过系统调用的 onStartCommand() 方法接受传递的Intent ,完成整个数据传递过程。
注意: Service 本身默认是运行在主线程里的,所以如果在 Service 要进行一些会堵塞线程的操作,一定要将这些操作放在一个新的线程里。
当系统内存不足的时候,系统会强制回收一些 Activity 和 Service 来获取更多的资源给那些用户正在交互的程序或页面。这就要求 Service 能够自动重启当资源充足的时候。这个功能是通过 onStartCommand() 的返回值来实现的
START_NOT_STICKY
当系统因回收资源而销毁了 Service,当资源再次充足时不自动启动 Service。除非还有为处理的 Intent 准备发送。当你的程序可以很容易的重新开启未完成的操作时,这是最安全的避免 Service 在不必要的情况下启动的选项。
START_STICKY
当系统因回收资源而销毁了 Service,当资源再次充足时自动启动 Service,并且再次调用 onStartCommand() 方法,但是不会传递最后一次的 Intent,相反系统在回调onStartCommand() 的时候会传一个空 Intent 。除非还有为处理的 Intent 准备发送。
START_REDELIVER_INTENT
当系统因回收资源而销毁了 Service,当资源再次充足时自动启动 Service,并且再次调用 onStartCommand() 方法,并会把最后一次 Intent 再次传递给,onStartCommand(),相应的在队列里的 Intent 也会按次序一次传递。此模式适用于下载等服务。
Start Service
该方式允许多个组件同时对相同的 Service 进行 startService 操作,但是如果只要有其中有一个组件调用了 stopSelf() 或 stopService(), 该 Service 就会被销毁。
当有多个组件进行了 startService 操作时,不应该直接的去调用 stopSelf() 或 stopService() 来结束 Service, 因为这会对其他已经发起请求的操作产生影响,故在 onStartCommand() 方法中会接受一个 startId, 然后在结束 Service 时,调用 stopService(int) 方法来只是结束一个特定的请求,从而达到保护其他请求不受影响的目的。
通过 Bind 方式启动 Service
当应用程序中的 activity 或其它组件需要与服务进行交互,或者应用程序的某些功能需要暴露给其它应用程序时,你应该创建一个 Bind 服务,并通过进程间通信(IPC)来完成。
Service 只在为绑定的应用程序组件工作时才会存活,因此,只要没有组件绑定到服务,系统就会自动销毁服务
如果你需要接口跨越多个进程进行工作,可以通过 Messenger 来为服务创建接口。在这种方式下,服务定义一个响应各类消息对象 Message 的 Handler。此 Handler 是 Messenger 与客户端共享同一个 IBinder 的基础,它使得客户端可以用消息对象 Message向服务发送指令。此外,客户端还可以定义自己的 Message ,以便服务能够往回发送消息。
这是执行进程间通信(IPC)最为简便的方式,因为 Messenger 会把所有的请求放入一个独立进程中的队列,这样你就不一定非要把服务设计为线程安全的模式了。
7.service 启动源码分析:
当我们调用了startservice方法,其内部会通过contextimpl调用其startService方法,并将intent传入进去,继续流程可发现它调用了自己的startServiceCommon方法,在该方法内首先会对参数intent进行合法性检查,确保其中与service启动的相关属性值不为null,且在android5.0之后强制要求service必须通过显示intent启动,否则会直接抛出异常。然后对intent属性离开应用进程前做准备工作,以及对跨进程安全性做检查。当由于权限、安全性等问题导致 Service 无法正常启动时,返回值 cn 的成员变量 mPackage 会被设置为 “!”、“?” 等特殊值,此时应用进程会进行处理并抛出对应的异常。接着会调用AMS的startservice方法,在该方法内,会通过binder机制,获取调用方的UID和进程PID,然后连同其他参数传递给activeservices对象的startServiceLocked进行处理,继续调用 startServiceLocked,多加了最后一个参数 allowBackgroundActivityStarts,默认为 false,表示不允许后台 Activity 启动 Service。接着进行重载,会通过传入的applicationthread从 mLruProcesses 列表中找出指定进程,该列表由AMS进行维护,mLruProcesses集合保存了所有的进程信息。接着会调用retrieveServiceLocked(),其内部 根据 Intent 查询并返回一个 ServiceLookupResult 对象,其中封装了目标 Service 对应的 ServiceRecord。该方法首先尝试从 ServiceMap 中获取 ServiceRecord 缓存对象,而 ServiceMap 又是 ActiveServices 类的内部类,它为每个用户分别维护了 mServicesByName 和 mServicesByIntent 两个值类型为 ServiceRecord 的 ArrayMap。如果不存在缓存对象,该方法将调用 PackageManagerService 的 resolveService() 解析 Intent 并获得对应的 ResolveInfo 对象,然后利用它创建一个新的 ServiceRecord 对象。之后会根据应用的进程优先级及目标进程是否已创建等条件决定service是立即启动还是延迟启动,然后将其作为条件传入到startServiceInnerLocked进行启动服务。在该方法内部,首先会更新servicestate为started状态,然后根据传递进来的servicerecord等信息判断目标service是否已经创建,如果创建则发送启动服务消息,如果目标 Service 所属的进程尚未创建,则需要通过 AMS 的 startProcessLocked() 创建对应的进程;如果目标 Service 尚未创建但所属的进程已经存在,则需要通过 realStartServiceLocked() 执行 Service 的 create 流程,如果目标 Service 已经创建,则可以直接通过 sendServiceArgsLocked() 回调目标 Service 的 onStartCommand()。如果进程不存在,则将 ServiceRecord 添加到服务队列中等待执行并调用AMS的startProcessLocked方法创建一个新的进程,当进程创建完成后会调用 attachApplicationLocked(),该方法将遍历 mPendingServices 并调用 realStartServiceLocked() 执行目标 Service 的后续启动流程。realStartServiceLocked 主要做的事情,ActivityThread.scheduleCreateService:回调到 APP 端,通过类加载器构建反射创建service对象,接着会调用service的生命周期attach以及onCreate方法,当service 的创建流程走完之后会接着调用sendServiceArgsLocked方法,该方法对 Service 的启动参数进行检查和封装,然后通过 IPC 调用应用进程的 scheduleServiceArgs() 对启动参数进行处理。最后调用service的onStartCommand方法,整个启动流程走完。
8.service启动流程简述
APP与service不在同一进城
1.App调用context.startService方法,在Context.startService方法内部又会调用ActivityManagerNative.getDefault().startService方法,ActivityManagerNative.getDefault()返回的是一个IActivityManager接口,里面存储了四大组件的所有生命周期函数,而ActiivtyManagerProxy是AMS在App的代理对象,其也集成了IActivityManger接口。所以最终会是ActivityManagerProxy.startService把Service的信息发送给AMS
2.AMS通过跨进程通信的方式收到Service的信息后会保存在ServiceRecord中,然后检查Service是否在AndroidManifest.xml中注册,如果没有注册则报错。检查service进程是否已经启动了,如果没有启动则会调用Process.start(android.app.ActivityThread)方法启动一个新的进程。
3.App进程启动是会实例化ActivityThread对象并调用其中的main方法来开启进程,进程启动后会通过ActivityManagerProxy代理对象把ActivityThread对象发送给AMS
4.AMS接收到ActivityThread对象后会把ActivityThread对象包装成为ApplicationThreadProxy对象(app的代理对象),并从ServiceRecord中翻出在第二步中保存的service信息,并通过ApplicationThreadProxy.scheduleCreateService将信息发送给App进程
5.App进程通过ApplicationThread接收消息,并调用ActiivtyThread的sendMessage方法向H发送消息,在H的handleMessage方法会接收消息,之后会调用ActivityThead的handleCreateService方法通过packageinfo拿到加载类的classloader并通过反射创建Service对象,创建完service对象后会紧接着调用其onCreate函数来完成Service的启动。
App与service 同一进程
1.app调用Context.startService方法,startService方法又会调用ActivityManagerProxy.startSerivce方法把intent消息发送给AMS
2.AMS检查service是否在AndroidManifest.xml中注册,如果没有注册就抛异常,AMS检查Service所在的进程是否已经启动,如果启动了则将Service启动信息通过ApplicationThreadProxy.scheduleCreateService发送给App进程
3.App进程通过ApplicationThread接收消息并通过ActivityThread.sendMessage发送消息,之后的过程就和上面的第5步是一样的。
绑定过程原理描述:
1.App通过Context.bindService发起绑定,最终会通过ActivityManagerProxy.bindService把service的Intent信息放给AMS
2.AMS检查Service是否在AndroidManifest.xml中进行了注册,如果没有则抛异常。AMS会保存App进程传递过来的信息到ServiceRecord。AMS检查Service所在的进程是否存在,如果不存在就就调用Process.start(android.app.ActivityThread)创建一个新的进程。
3.实例化ActivityThread对象并执行其中的main方法,并对主线程Looper进行初始化。App进程启动后会将ActivityThread通过ActivityManagerProxy发送给AMS
4.AMS接收到ActivityThread后会将ActivityThread包装秤ApplicationThreadProxy,然后通过ApplicationThreadProxy.scheduleBindService向App进程发送消息
5.App进程通过ApplicationThread接收到消息后会调用ActivityThread.sendMessage方法向H发送消息,H通过handleMessage接收到消息后会调用handleBindService对Service进行实例化并调用其onCreate函数。之后会调用Service的onBind函数把Binder发送给AMS
9.intentService原理
在intentservice的onCreate方法内,会实例化一个handlethread,并启动线程,然后获取looper,然后将HandlerThread中的Looper与ServiceHandler进行绑定,ServiceHandler是一个内部类,继承于Handler,在ServiceHandler胡handleMessage中,会调用intentservice的onHandleIntent回调,且当onHandleIntent方法执行完毕之后会调用stopself,这也是为什么intentservice能自己停掉服务的原因,这里自己停掉服务用的是stopSelf(int startId)方法,而不是stopSelf() ,发现原来stopSelf()调用的是stopSelf(int startId),只不过startId为-1而已。
说到这个startId,它是什么呢?其实它就是service的一个生命周期:onStartCommand(@Nullable Intent intent, int flags, int startId)中最后的一个参数。
我们都知道,当我们多次调用startService来启动同一个service时,只有第一次会执行onCreate,然后会多次调用onStartCommand,如果你去打印log的话,你会发现尽管onCreate只执行一次,但是每次的startId却是不同的,且都大于0。
而stopSelf(int startId)中的startId与onStartCommand中的startId是一一对应的关系,所以,当我们调用stopSelf(int startId)时,系统会检测是否还有其它的startId存在,有的话就不销毁当前service,没有的话则销毁。
而如果我们调用的是stopSelf(),那么无论是否还存在其它的startId,都会立即销毁当前service。
这就是stopSelf()和stopSelf(int startId)两个方法的区别!
我们回到之前的的问题,为什么IntentService中自停服务用的是stopSelf(int startId)而不是stopSelf()呢?
从上面比较两个方法的区别我们不能得出:这是为了提高IntentService的利用率,也就是说,如果在onHandleIntent方法执行完毕前,又调用了startService启动了同一个IntentService,那么我们就没必要销毁当前service了,直接继续用当前service对象执行任务即可,这样有利于减少了对象的销毁及创建。
Handle机制:
在handle中主要牵扯到了4个角色,message,消息载体,handle消息的发起者和接收者,looper消息的遍历者,messagequeue消息队列,handle消息机制的使用主要为以下几个步骤,首先调用looper.prepare然后创建handler对象,再调用looper.loop,最后通过sendmessage或postmessage发送消息。当我们调用looper.prepare时,如果当前线程没有looper则会创建一个新的looper,并存放到threadlocal中去,threadlocal是一个静态类型的对象,在创建looper时会在其构造方法中创建messagequeue并持有它,然后获取当前的线程对象并持有它,然后我们在创建handle的时候,有2种方法一种是无参的构造方法,一种是有参的looper构造方法,无参的构造方法会获取当前线程的looper如果没有looper则抛出异常,有参的构造方法则使用传递进来的looper,然后从looper中获取messagequeue让handle持有,当调用looper.loop时,首先还是判断了当前线程是否有Looper,然后得到当前线程的MessageQueue,接下来写了一个死循环,不断调用messagequeue的next方法取出消息队列中的消息,如果消息队列中没有笑嘻嘻,则next方法会被阻塞,导致当前线程挂起。拿到message后会从message中取出对应的handle对象并调用它的dispatchMessage方法,首先,判断msg.callback是不是空,其实msg.callback是一个Runnable对象,是Handler.post方式传递进来的参数,然后,判断mCallback是否为空,这是一个Handler.Callback的接口类型,之前说了Handler有多个构造方法,可以提供设置Callback,如果这里不为空,则调用它的hanldeMessage方法,注意,这个方法有返回值,如果返回了true,表示已经处理 ,不再调用Handler的handleMessage方法;如果mCallback为空,或者不为空但是它的handleMessage返回了false,则会继续调用Handler的handleMessage方法,该方法就是我们经常重写的那个方法。当我们调用send或post方法时,会在其持有的消息队列中插入一条信息,此消息会将当前handle对象作为message的target属性,首先,判断了Message是否已经使用过了,如果使用过,则直接抛出异常,这是可以理解的,如果MessageQueue中已经存在一个Message,但是还没有得到处理,这时候如果再发送一次该Message,可能会导致处理前一个Message时,出现问题。然后,会判断when,它是表示延迟的时间,如果when不为0,则需要把消息加入到消息队列的合适位置。最后会去判断当前线程是否已经阻塞了,如果阻塞了,则需要调用本地方法去唤醒它。而post方法只是先调用了getPostMessage方法,用Runnable去封装一个Message,然后就调用了sendMessageDelayed,把封装的Message加入到MessageQueue中。
关于Handler的问题
1.Android中,有哪些是基于Handler来实现通信的?
答:App的运行、更新UI、AsyncTask、Glide、RxJava等
2.处理Handler消息,是在哪个线程?一定是创建Handler的线程么?
答:创建Handler所使用的Looper所在的线程
3.消息是如何插入到MessageQueue中的?
答: 是根据when在MessageQueue中升序排序的,when=开机到现在的毫秒数+延时毫秒数
4.当MessageQueue没有消息时,它的next方法是阻塞的,会导致App ANR么?
答:不会导致App的ANR,是Linux的pipe机制保证的,阻塞时,线程挂起;需要时,唤醒线程
5.子线程中可以使用Toast么?
答:可以使用,但是Toast的显示是基于Handler实现的,所以需要先创建Looper,然后调用Looper.loop。
6.Looper.loop()是死循环,可以停止么?
答:可以停止,Looper提供了quit和quitSafely方法
7.Handler内存泄露怎么解决?
答: 静态内部类+弱引用 、Handler的removeCallbacksAndMessages等方法移除MessageQueue中的消息
handle中的epoll机制
为什么Handler消息队列可以一直循环而不会阻塞主线程,主要因为linux的epoll机制。Handler里面维持了一个消息队列MessageQueue,通过Looper不断的插入消息,取出消息,当从MessageQueue中取出消息时先对检查消息执行的时间,如果时间还没到。则调用nativePollOnce方法使整个消息队列进入睡眠状态。ativePollOnce方法是native方法,仅供jni调用,当MessageQueue中没有Message需要处理或Message时间还没到时,调用java层的nativePollOnce方法,根据源码可以看出最后进入Looper.pollOnce的方法。在pollOnce中通过pollInner最终调用到epoll_wait,至此可以得出,Java层的nativePollOnce最终调用到JNI层的epoll_wait方法,并在在分析的过程中发现,JNI里面也有Looper。其他Java层的线程最终都是通过JNI调用pThread,对应的JNI也有和Java类名一样的线程相关类。epoll是Linux内核中的一种可扩展IO事件处理机制,能够提高应用程序同时大量IO操作请求时的性能。首先我们要知道在Linux中一切皆文件,发送消息是一种文件IO的处理。Android操作系统也是基于LInux内核。一般当系统同时发起多个IO事件请求时,Linux需要轮询所有事件,处理每个事件对应的事件流。由于系统中链接Linux内核的IO事件实在是太多了,每一次轮询都需要耗费大量的时间和资源。使用epoll_wait机制后。epoll会把每一个流对应的IO事件通知内核,这样内核就能根据具体的IO事件去处理对应的stream流。当Java层调用nativePollOnce时,对应JNI调用到epoll_wait方法,等待系统的返回。使整个消息队列进入阻塞状态。一旦时间到了,epoll_wait主动返回对应的事件流交给Linux内核处理。或者插入新的Message通过调用nativeWake方法,调用Looper的wake方法,发送唤醒信号给Linux内核。通过以上源码分析,我们可以看出Handler底层使用的epoll机制,能够高效的同时处理多个请求。
handlethread源码解析
HandlerThread本质上是一个线程类,它继承了Thread;HandlerThread有自己的内部Looper对象,可以进行looper循环,通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务,创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。从源码可以看出HandlerThread继续自Thread,构造函数的传递参数有两个,一个是name指的是线程的名称,一个是priority指的是线程优先级,我们根据需要调用即可。其中成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,是留给我们必要时可以去重写的,但是注意重写时机是在Looper循环启动前,再看看run方法,在创建HandlerThread对象后必须调用其start()方法才能进行其他操作,而调用start()方法后相当于启动了线程,也就是run方法将会被调用,而我们从run源码中可以看出其执行了Looper.prepare()代码,这时Looper对象将被创建,当Looper对象被创建后将绑定在当前线程,这里在Looper对象创建后将其赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程,最后执行Looper.loop();代码,开启looper循环语句。事实上可以看出外部在通过getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果线程已经启动,那么将会进入同步语句并判断Looper是否为null,为null则代表Looper对象还没有被赋值,也就是还没被创建,此时当前调用线程进入等待阶段,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,最后才返回Looper对象,之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建,到这里我们也就明白了在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,HandlerThread内部则通过等待唤醒机制解决了同步问题。当quit方法被调用的时候,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法。该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息还是非延迟消息。
Android view 绘制原理
1.渲染引擎
Android 系统中,采用2D渲染引擎库SKIA和3D渲染引擎库OpenGL来实现,它们是跨编程语言,跨平台的渲染引擎库,它主要为我们定义了用来操作图形和图片的一系列函数api,GPU的硬件开发商则需要提供满足其规范的实现,这些实现通常被称为驱动,负责将api命令翻译为GPU指令。因为其本身是一个与硬件无关的软件接口,OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。至于内部具体每个函数是如何实现的,将由OpenGL库的开发者自行决定。实际的OpenGL库的开发者通常是显卡的生产商。例如android系统的高通等,所以不同的平台可以对该接口完成实现,所以通用于市面上较为流行的平台,OpenGL库是用C语言写的,同时也支持多种语言的派生,但其内核仍是一个C库。其位于安卓系统架构的系统运行库层:
2.android 窗口管理
对于用户来说,窗口就是手机屏幕,对于应用来说,窗口是除系统状态栏和系统安检的屏幕区域,而android的窗口管理主要由window,windowmanager,WMS三者组成,window是视图的载体,window有三种类型,从低到高分为子window,应用window,系统window,每个window有着对应的层级,层级大的可以覆盖在层级小的window上面,window可以看做是一个容器,但它其实是个抽象的概念,视图必须放在window上才能显示,每个window对应着一个view和一个viewrootimpl,View负责定义window的布局以及样式等,而
ViewRootImpl则是负责View的渲染。当启动一个activity时会调用performLaunchActivity,接着会调用activity的attach方法,在activity的attach方法中会创建一个phonewindow,phonewindow是 window的具体实现,然后调用其setWindowManager方法在该方法内部会获取WMS然后创建一个windowmanager的实例WindowManagerImp,当调用setcontentview的时候会触发phoneWindow的setContentView,然后再其内部会创建decorview,然后将我们传入的VIEW放入到decorview内部的fragment中去。 window和View是通过WindowManager来建立起联系的,WindowManager是联系window和WindowManagerService的桥梁,是Android窗口管理机制的枢纽。它继承子ViewManager,定义了对于window的基本操作。它其实主要就是对外提供了对View的增加,更新,删除操作。所以我们也可以直接通过WindowManager来对window实现这些操作。在activity调用其onResume方法之后,会调用makeVisible将window加入到windowManager中,那么windowManager就可以对window进行管理了,然后调用了WindowManagerImpl的addview方法,把decorview传入,最终会调用到WindowManagerGlobal的addview方法,addView方法中,把decorview保存起来,创建了rootViewImpl对象,ViewRootImpl 是 View 的最高层级,是所有 View 的根。ViewRootImpl 实现了 View 和 WindowManager 之间所需要的协议。View 的三大流程都是通过 RootViewImpl 来完成的,然后调用了ViewRootImpl 的setView方法,在该方法内部首先会调用requestlayout内部的scheduleTraversals执行view的绘制流程,然后会通过aidl通知WMS通过addwindow添加到显示器.
3.android 渲染机制
CPU 用来计算图形数据,GPU用来处理graphics加快格栅化操作,opgenGL,跨平台的渲染库,跨平台的2D/3D图形应用程序接口API,有一套固定的渲染管线。DisplayList,在android 把XML布局文件转换成GPU能够识别并绘制的对象,这个操作是在Display帮助下完成的。Displaylist持有所有将要交给Gpu绘制到屏幕上的数据信息,格栅化是将图片等矢量资源转化为一格格像素点的像素图,显示到屏幕上,垂直同步vsync,让显卡的运算和显示器刷新率一致以稳定输出画面质量,该信号由屏幕产生,并以固定的频率发送给android系统,android整体绘制流程可以概括为,UI对象被CPU处理未多维图形,通过OpenGL接口调用GPU,GPU对图进行光栅化处理,经过垂直同步投射到屏幕上。
Android 采用了3重缓存机制,在CPU/GPU缓冲的基础上增加了graohicBuffer缓冲区,这样就可以最大限度的利用空闲时间
Android 系统每隔16Ms发出同步信号,触发对UI渲染
4.android 刷新机制
当应用程序启动后,走到ViewRootImpl的setview的时候,其内部会将decorview的parent设置为viewroot,并调用requestlayout发起布局请求,当此方法处理完后还会调用获取WMS并调用其addwindow方法。requestlayout内部调用了scheduleTraversals方法,首先会设置一个消息屏障,该消息屏障在此次绘制完成后会放开,主要作用是防止在一帧内过渡刷新重绘问题,其次会设置一个同步屏障消息 如果在主线程消息队列中发现存在异步消息则会立即取出该异步消息执行否则就让next方法陷入阻塞,指到该异步消息移除队列,这个过程主要是为了第一时间执行屏幕刷新操作,然后这个方法会将遍历绘制view树的操作performtraversals封装到runable,传给Chorerographer,Chorerographer里所有和message有关的消息都设置了异步标志,然后以当前的时间戳放进一个callback队列里,然后会对当前线程进行判断,如果是主线程则直接调用native层的方法向底层注册监听下一个屏幕刷新信号事件,如果不是则向主线程发送一个最高优先级的消息让主线程第一时间调用该native 方法进行注册,当下一个屏幕刷新信号发出的时候,如果我们的app有对这个事件进行监听,那么底层就会回调我们app层的Choreographer 的onVsync方法来通知,当onvsync被回调时,会发出一个message到主线程,将后续的工作切换到主线程执行。切到主线程的工作就是去callbackqueue队列里根据时间戳将之前放进去的runable取出来让其执行其doraversal方法,该方法内部会移除消息屏障并最终执行到performtraversals方法,在其内部根据条件分别执行测量,布局,绘制三大流程。
Choreographer
Choreographer机制,用于同Vsync机制配合,统一动画、输入和绘制时机,在ViewRootImpl的构造方法中会获取Choreographer实例,每一个Looper线程都有自己的Choreographer,其他线程发送的回调只能运行在对应Choreographer所属的Looper线程上,Choreographer类中有一个Looper和一个FrameHandler变量用来处理刷新回调相关的消息,Choreographer还创建了一个大小为3的CallbackQueue队列数组,用于保存不同类型的Callback。
5.view 的绘制流程
当performTraversals方法被调用的时候,在其内部会按照条件去做判断然后顺序调用performMeasure,performLayout,performDraw方法,分别进行测量,布局,绘制过程,在performMeasure方法中会调用其measure方法,在该方法内,首先会判断是否需要重新布局,然后判断是否需要从缓存中获取,如果缓存命中,则直接从缓存中获取布局,否则调用onmeasure方法,一个view的实际测量工作是在被本方法所调用的onMeasure方法中实现的,所以,只有onMeasure可以并且必须被子类重写,对于decorView来说,实际执行测量工作的是FrameLayout的onMeasure()方法,FrameLayout是ViewGroup的子类,后者有一个View[]类型的成员变量mChildren,代表了其子View集合。通过getChildAt(i)能获取指定索引处的子View,通过getChildCount()可以获得子View的总数,首先调用measureChildWithMargins()方法对所有子View进行了一遍测量,并计算出所有子View的最大宽度和最大高度。而后将得到的最大高度和宽度加上padding,这里的padding包括了父View的padding和前景区域的padding。然后会检查是否设置了最小宽高,并与其比较,将两者中较大的设为最终的最大宽高。最后,若设置了前景图像,我们还要检查前景图像的最小宽高。
经过了以上一系列步骤后,我们就得到了maxHeight和maxWidth的最终值,表示当前容器View用这个尺寸就能够正常显示其所有子View(同时考虑了padding和margin)。而后我们需要调用resolveSizeAndState()方法来结合传来的MeasureSpec来获取最终的测量宽高,并保存到mMeasuredWidth与mMeasuredHeight成员变量中。,容器View通过measureChildWithMargins()方法对所有子View进行测量后,才能得到自身的测量结果。 也就是说,对于ViewGroup及其子类来说,要先完成子View的测量,再进行自身的测量。接下来我们来看下ViewGroup的measureChildWithMargins()方法,对于ViewGroup来说,它会调用child.measure()来完成子View的测量。传入ViewGroup的MeasureSpec是它的父View用于约束其测量的,那么ViewGroup本身也需要生成一个childMeasureSpec来限制它的子View的测量工作。这个childMeasureSpec就由getChildMeasureSpec()方法生成,然后根据父View的MeasureSpec和子View的LayoutParams生成子View的MeasureSpec,在measureChildWithMargins()方法中,获取了知道子View测量的MeasureSpec后,接下来就要调用child.measure()方法,并把获取到的childMeasureSpec传入。这时便又会调用onMeasure()方法,若此时的子View为ViewGroup的子类,便会调用相应容器类的onMeasure()方法,其他容器View的onMeasure()方法与FrameLayout的onMeasure()方法执行过程相似,对于普通View,会调用View类的onMeasure()方法来进行实际的测量工作,其内部通过setMeasuredDimension()方法设置测量的结果,具体来说是以getDefaultSize()方法的返回值来作为测量结果,其内部是通过自身的size 和MeasureSpec共同决定,View的getDefaultSize()方法对于AT_MOST和EXACTLY这两种情况都返回了SpecSize作为result。所以若我们的自定义View直接继承了View类,我们就要自己对wrap_content (对应了AT_MOST)这种情况进行处理,否则对自定义View指定wrap_content就和match_parent效果一样了。
layout阶段的基本思想也是由根View开始,递归地完成整个控件树的布局工作。decorView的layout()方法的调用作为布局整个控件树的起点,实际上调用的是View类的layout()方法,这个方法会调用setFrame()方法来设置View的4个参数,这四个参数描述了View相对其父View的位置,在setFrame()方法中会判断View的位置是否发生了改变,若发生了改变,则需要对子View进行重新布局,对子View的布局是通过onLayout()方法实现,由于普通View不含子View,所以View类的onLayout()方法为空。继续看ViewGroup类的onLayout()方法,实际上ViewGroup类的onLayout()方法是abstract,这是因为不同的布局管理器有着不同的布局方式,这里我们以decorView,也就是FrameLayout的onLayout()方法为例内部先确定子View的显示区域,接下来,用一个for循环来完成子View的布局,在确保子View的可见性不为GONE的情况下才会对其进行布局。
对于draw的过程会经过以下几个步骤,当view的背景不透明时首先会对背景进行绘制,然后调用ondraw方法绘制自身,然后调用dispatchDraw绘制子View,最后绘制滚动条等完成这个view 的绘制过程。
6.view 的事件分发
手指触摸屏幕时,产生了触摸信息。这个触摸信息由屏幕这个硬件产生,被系统底层驱动获取,交给Android的输入系统服务:InputManagerService,也就是IMS,IMS会对这个触摸信息进行处理,通过WMS找到要分发的window,随后发送给对应的viewRootImpl。android的view管理是以window为单位的,每个window对应一个view树。Window机制不仅管理着view的显示,也负责view的事件分发。每一棵view树都有一个根,叫做ViewRootImpl ,负责管理这整一棵view树的绘制、事件分发等。App会有多个view树,activity布局就是一个view树、应用的悬浮窗也是一个view树、dialog界面也是一个view树,android中view的绘制和事件分发,都是以view树为单位。每一棵view树,则为一个window。系统服务WindowManagerService,管理界面的显示就是以window为单位,也可以说是以view树为单位。view树是由viewRootImpl来负责管理的,wms管理的是viewRootImpl,wms是运行在系统服务进程的,负责管理所有应用的window。app 与wms的通信必须通过Binder进行跨进程通信。每个viewRootImpl在wms中都有一个windowState对应,wms可以通过windowState找到对应的viewRootImpl进行管理。事件分发并不是由Activity驱动的,而是由系统服务驱动viewRootImpl来进行分发,IMS从系统底层接收到事件之后,会从WMS中获取window信息,并将事件信息发送给对应的viewRootImpl,viewRootImpl接收到事件信息,封装成motionEvent对象后,发送给管理的view,顶层viewGroup一般是DecorView,DecorView会根据自身callBack的情况,选择调用callBack或者调用父类ViewGroup的方法,管顶层viewGroup的类型,最终都会到达ViewGroup对事件进行分发,Window.CallBack接口中包含了 dispatchTouchEvent 和 onTouchEvent 方法,Activity和Dialog都实现了Window.CallBack接口,因此都实现了该方法,dispatchTouchEvent,事件分发的核心方法,事件分发的逻辑都是在这个方法中实现;View、以及 ViewGroup、其他的实现类都重写了该方法,如果成功处理则返回true,处理失败则返回false,表示事件没有被处理,在view的相关类中,该方法的主要作用是消费触摸事件,在viewGroup相关类中,该方法的主要作用是把事件分发到该viewGroup所拥有的子view,如果子view没有处理则自己处理。onInterceptTouchEvent,该方法只存在于viewGroup中,当一个事件需要被分发到子view时,viewGroup会调用此方法检查是否要进行拦截。如果拦截则自己处理,而如果不拦截才会调用子view的 dispatchTouchEvent 方法分发事件,方法返回true表示拦截事件,返回false表示不拦截,viewGroup分发事件时,如果没有一个子view消费事件,那么会调用viewGroup自身的onTouchEvent方法来处理事件,View的dispatchTouchEvent方法中,先调用onTouchListener判断是否消费;如果onTouchListener没有消费事件,才会调用onTouchEvent来处理事件,一个触控点的事件序列只能给一个view消费,除非发生异常情况;onTouchListener的onTouch方法优先级比onTouchEvent高,会先触发。
假如onTouchListener的onTouch方法返回false,会接着触发onTouchEvent,反之onTouchEvent方法不会被调用。
内置诸如click事件的实现等等都基于onTouchEvent,假如onTouchListener的onTouch返回true,这些事件将不会被触发。
7.事件分发面试题
事件是先到DecorView还是先到Window
ViewRootImpl -> DecorView -> Activity -> PhoneWindow -> DecorView -> ViewGroup
点击事件被拦截,但是想传到下面的view,如何操作
在子View中调用getParent().requestDisallowInterceptTouchEvent(true)
滑动冲突的解决方案
1、外部拦截法
外部拦截法是指点击事件都事先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要此事件就不拦截,这种方法比较符合点击事件的分发机制,外部拦截伐需要重写父容器的onInterceptTouchEvent方法在内部做相应的拦截即可;
2、内部拦截法
内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器处理,这种方法和Android的事件分发机制不一致需要配合requestDisallowInterceptTouchEvent方法才能正常工作。
同时对父 View 和子 View 设置点击方法,优先响应哪个
优先响应子 view;
如果先响应父 view,那么子 view 将永远无法响应;
requestDisallowInterceptTouchEvent的调用
一个手势的操作,会经历down,move,up等;
子view调用requestDisallowInterceptTouchEvent(true)是必须获取到点击事件,如果在down的时候调用了此方法,接下来的move,up都会传到子view上了,如果是在子view的move方法中调用的话,那么父view在move的过程中能将事件传递给子view就即可。
7.WMS原理
WMS 窗口管理服务,它是系统服务,由systemService启动,直到关机时才会退出,发生异常时必须重启。
WMS主要涉及的元素有,WindowManagerPolicy,窗口策略类,手机的实现类是PhoneWindowManager,应用到WMS中代表了android显示系统所遵循的统一的窗口显示规则,针对不同的产品,UI显示策略通常是不一样的。ArraySet mSession主要用于进程间通信,其他应用程序想要和WMS通信要经过Session,每个应用程序进程都会对应一个Session,WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端,WindowMap:WindowState用于保存窗口信息,用来描述一个窗口。mWindowMap其实就是用来保存WMS中各种窗口的集合.AppWindowToken,它为WindowToken的子类,WindowToken主要有2个作用,窗口令牌,当应用程序想要向WMS申请新创建一个窗口,则需要向WMS出示有效的WindowToken。AppWindowToken主要用来描述应用程序的WindowToken结构,应用程序中每个Activity都对应一个AppWindowToken,WindowToken会将同一个组件(比如同一个Activity)的窗口(WindowState)集合在一起,方便管理
ArrayList-mResizingWindows:用来存储正在调整大小的窗口列表,WindowAnimator用于管理窗口的动画以及特效动画,H-h:系统的Handler类,用于将任务加入到主线程消息队列中,InputManagerService-mInputManager:输入系统的管理者。会对触摸事件进行处理,他会寻找一个合适的窗口来处理触摸返回信息,WMS是窗口的管理者,所以需要持有IMS引用。窗口可以分为3类,Application Window:普通应用程序显示申请所产生的window,和系统窗口相比,它们的窗口层级值比较低,System Window:系统顶部的系统状态栏,壁纸等,Sub Window:Toast等弹窗。ActivityManagerService:AMS管理者所有的Activity,而Activity的变化通常会带来界面上的改变。那么界面上产生的变化(淡出等动画效果)也要涉及WMS。
WMS 包含的功能
窗口的添加与删除,窗口的启动,窗口动画,窗口大小控制,窗口层级,事件派发等
内部组织方式
当一个新的Activity被启动时,它首先需要在AMS中注册,此时AMS会在内部生成一个ActivityRecord来记录这个Activity;另外因为Activity是四大组件中专门用于UI显示的,所以WMS也会对它以WindowState的形式进行记录。
所以Activity在AMS中的表现形式为ActivityRecord,在WMS中的表现形式为WindowState,其中WMS还有一个变量AppWindowToken和AMS中的ActivityRecord相对应,这样它们就关联了起来。
WMS的启动
WMS由SystemServer进程启动,在SystemServer的main()中执行了 startOtherServices方法,在该方法内部首先会初始化一个watchdog,watchdog用来监控系统的一些关键服务运行的情况,如WMS,它每分钟都会对被监控的系统服务进行检查,如果被监控的服务出现了死锁,则会杀死watchdog所在进程,也就是systemserver进程,所以说当WMS发生异常时必须重启,就是在这里监控的。然后构建了一个InputManagerService,并通过WindowManagerService.main()构造了一个WMS对象,并传入了构造好的InputManagerService,这样WMS就持有了IMS的引用,最后将WMS和IMS注册到了servicemanager中,这样如果某个客户端想使用它们的功能,就可以去servicemanager去查询,进而可以进行进程间通信。进入WindowManagerService.main()方法,其内部在DisplayThread线程中创建了WMS这个线程是一个单例的前台线程,它用来处理需要低延迟显示的相关操作,由于WMS优先级更高,所以system_server线程需要等待DisplayThread线程执行完创建WMS的操作,才会继续执行,在这之前它会一直等待。在WMS的构造中会持有AMS的引用然后创建WindowAnimator对象用于管理窗口动画,以及持有IMS,displaymanager的引用,最后调用initPolicy,创建窗口策略管理类PhoneWindowManager,最后将WMS添加到watchdog中。
Window添加过程
window的添加从WMS的addWindow()开始。在该方法内会对所要添加的窗口进行检查,然后是对windowtoken和windowstate的创建和相关处理,并将它们进行关联,然后配置DisplayContent,DisplayContent会根据窗口的显示位置将其分组,隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中,每一个DisplayContent都对应这一个唯一的ID,在添加窗口时可以通过指定这个ID决定其将被显示在哪个屏幕中,DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。因此,就这几个方面来说,DisplayContent就像一个孤岛,所有这些操作都可以在其内部独立执行。因此,这些本来属于整个WMS全局性的操作,变成了DisplayContent内部的操作了,接着调用WindowManagerPolicy将窗口添加到系统中去。
Window的删除过程
窗口的删除从WindowManagerGlobal.removeVIew()发起,然后检查检查删除线程的正确性,如果不正确就抛出异常,从ViewRootImpl列表,布局列表和View列表中删除对应的元素,判断是否可以执行删除操作,如果不能就推迟删除操作,执行删除操作,清理和释放与view相关的一切资源。
Android surface 系统原理
WMS负责窗口的管理,同时充当应用程序和SurfaceFlinger的桥梁,而SurfaceFlinger顾名思义,就是负责完成图像合成输出的。真正绘制时,是由应用程序直接提供原始图像数据,SurfaceFlinger将各个应用程序的数据按照窗口Z序次序合成输出,而应用程序和SurfaceFlinger运行在不同的进程空间。
Android设计了Surface机制用来完成跨进程的图像数据传输,可以将其理解为屏幕数据的缓存区。Surface对应了一块屏幕缓冲区,每个window对应一个Surface,任何View都要画在Surface的Canvas上,Surface中有一个Canvas成员,专门用于画图。准确来说应该是一个数据容器交由生产者往surface申请对应的GraphicBuffer中填数据,交由surfaceflinger消费。
当viewroot调用setview方法并调用WMS的addwindow方法时,WMS的addWindow先创建一个维护应用程序窗口信息的WindowState对象,然后调用了WindowState的attach函数,attach调用了Session的windowAddedLocked,windowAddedLocked创建一个SurfaceSession对象,最终会通过调用ISurfaceComposer的createConnection完成对SurfaceFlinger Client对象的创建,然后在SurfaceFlinger创建一块匿名共享内存,这块内存的内容是一个SharedClient对象,该对象最终会在应用程序进程、WMS进程和SurfaceFlinger进程中共享,用来协调GraphicBuffer在应用程序进程和SurfaceFlinger进程的生产和消费。
当requestLayout方法的performTraversals方法时会触发relayoutWindow方法,它会创建一个空壳surface,然后调用到了WMS的relayout方法,在该方法内完成真正的surface初始化,然后再将这个WMS Surface复制给ViewRoot中的Surface,这么实现的目的就是保证ViewRoot和WMS共享同一个Surface,ViewRoot对Surface进行绘制,WMS对这个Surface进行初始化及管理,在这过程中,也将存储SharedClient对象的匿名共享内存映射到应用程序进程空间。然后在SurfaceFlinger这端创建了代表Surface的Layer以及SurfaceLayer对象,应用程序创建好Surface之后,就可以通过ISurface接口直接向SurfaceFlinger申请分配GraphicBuffer,对于软件实现的GraphicBuffer,就是创建一块匿名共享内存,然后分别映射到SurfaceFlinger进程和应用程序进程,应用程序进程申请到GraphicBuffer后,就可以使用OpenGL或者Skia接口随意的绘制了,在绘制流程中,会从surface 中获取到canvase,然后交给draw方法去做绘制,绘制完成后就可以通过GraphicBuffer通知SurfaceFlinger合成输出到FrameBuffer了。SurfaceFlinger是一个系统服务,实现了Surface的建立、控制、管理,创建display显示通道,控制GraphicBuffer申请轮转,基于Vsync事件同步管理需要参与显示的surface给硬件叠加器叠加显示到屏幕上。
SurfaceFlinger原理
SurfaceFlinger是一个系统服务,提供系统范围内的surface composer功能,它能够将各种应用程序的2D、3D surface进行组合,每个应用程序可能对应着一个或者多个图形界面,而每个界面我们就称之为一个surface ,或者说是window,每个surface 在屏幕上有它的位置、大小,然后每个surface 里面还有要显示的内容,各个surface 之间可能有重叠,surface 实际我们可以把它理解成一个容器,这个容器记录着应用程序界面的控制信息,比如说大小、位置,而它还有buffer 来专门存储需要显示的内容。当存在图形重合或者有些surface 还带有透明信息的时候,就需要 SurfaceFlinger 来解决问题,它要把各个surface 组合,成一个main Surface ,最后将Main Surface 的内容发送给framebuffer ,这样屏幕上就能看到我们想要的效果。在实际中对这些Surface 进行merge 可以采用两种方式,一种就是采用软件的形式来merge ,还一种就是采用硬件的方式,软件的方式就是我们的SurfaceFlinger ,而硬件的方式就是Overlay ,我们首先来看overlay,当IPU 向内核申请framebuffer 的时候它会申请3 个framebuffer ,,一个是主屏的,还一个是副屏的,还一个就是Overlay 的。 简单地来说,Overlay就是我们将硬件所能接受的格式数据和控制信息送到这个Overlay FrameBuffer,由硬件驱动来负责merge Overlay buffer和主屏buffer中的内容。一般来说现在的硬件都只支持一个Overlay,主要用在视频播放以及camera preview上,因为视频内容的不断变化用硬件Merge比用软件Merge要有效率得多,surfaceFlinger 只是负责 merge Surface 的控制,比如说计算出两个 Surface 重叠的区域,至于 Surface 需要显示的内容,则通过 skia,opengl 来计算。创建一个surface 分为两个过程,一个是在 SurfaceFlinger 这边为每个应用程序(Client) 创建一个管理结构,另一个就是创建存储内容的buffer ,以及在这个buffer 上的一系列画图之类的操作.
因为SurfaceFlinger 要管理多个应用程序的多个窗口界面,为了进行管理它提供了一个Client 类,每个来请求服务的应用程序就对应了一个 Client 。因为 surface 是在 SurfaceFlinger 创建的,必须返回一个结构让应用程序知道自己申请的 surface 信息,因此 SurfaceFlinger 将 Client 创建的控制结构per_client_cblk_t 经过 BClient 的封装以后返回给 SurfaceComposerClient ,并向应用程序提供了一组创建和销毁 surface 的接口,SurfaceFlinger ,为每个 Client 提供了 8M 的空间,包括控制信息和存储内容的 buffer 。
为应用程序创建一个 Client 以后,下面需要做的就是为这个 Client 分配 Surface , 可以理解为创建一个 Surface 就是创建一个 Layer 。创建 Layer 的过程,首先是由这个应用程序的 Client 根据应用程序的 pid 生成一个唯一的 layer ID ,然后根据大小、位置、格式等信息创建出 Layer 。在 Layer 里面有一个嵌套的 Surface 类,包含了这个 Surace 的统一标识符以及 buffer 信息等,提供给应用程序使用。最后应用程序会根据返回来的 ISurface 创建一个自己的 Surface 。Android 提供了 4 种类型的 layer 供选择,每个 layer 对应一种类型的窗口,并对应这种窗口相应的操作.Normal Layer
它是 Android 种使用最多的一种 Layer ,一般的应用程序在创建 surface 的时候都是采用的这样的 layer , Normal Layer 为每个 Surface 分配两个 buffer : front buffer 和 back buffer , Front buffer 用于 SurfaceFlinger 进行显示,而 Back buffer 用于应用程序进行画图,当 Back buffer 填满数据 (dirty) 以后,就会 flip , back buffer 就变成了 front buffer 用于显示,而 front buffer 就变成了 back buffer 用来画图。
LayerBuffer最复杂的一个 layer,它不具备 render buffer ,主要用在 camera preview / video playback 上。它提供了两种实现方式,一种就是 post buffer ,另外一种就是我们前面提到的 overlay , Overlay 的接口实际上就是在这个 layer 上实现的。不管是 overlay 还是 post buffer 都是指这个 layer 的数据来源自其他地方,只是 post buffer 是通过软件的方式最后还是将这个 layer merge 主的 FB ,而 overlay 则是通过硬件 merge 的方式来实现。与这个 layer 紧密联系在一起的是 ISurface 这个接口,通过它来注册数据来源。
SurfaceFlinger 是一个线程类,它继承了 Thread 类。当创建 SurfaceFlinger 这个服务的时候会启动一个 SurfaceFlinger 监听线程,这个线程会一直等待事件的发生,比如说需要进行 sruface flip ,或者说窗口位置大小发生了变化等,一旦产生这些事件,SurfaceComposerClient 就会通过 IBinder 发出信号,这个线程就会结束等待处理这些事件,处理完成以后会继续等待,如此循环。
SurfaceComposerClient 和 SurfaceFlinger 是通过 SurfaceFlingerSynchro 这个类来同步信号的,其实说穿了就是一个条件变量。监听线程等待条件的值一旦变成 OPEN 就结束等待并将条件置成 CLOSE 然后进行事件处理,处理完成以后再继续等待条件的值变成 OPEN ,而 Client 的Surface 一旦改变就通过 IBinder 通知 SurfaceFlinger 将条件变量的值变成 OPEN ,并唤醒等待的线程,这样就通过线程类和条件变量实现了一个动态处理机制。
Android 组合各个窗口的原理: Android 实际上是通过计算每一个窗口的可见区域,就是我们在屏幕上可见的窗口区域,然后将各个窗口的可见区域画到一个主 layer 的相应部分,最后就拼接成了一个完整的屏幕,然后将主 layer 输送到 FB 显示。在将各个窗口可见区域画到主 layer 过程中涉及到一个硬件实现和一个软件实现的问题,如果是软件实现则通过 Opengl 重新画图,其中还包括存在透明度的 alpha 计算;如果实现了 copybit hal 的话,可以直接将窗口的这部分数据直接拷贝过来,并完成可能的旋转,翻转,以及 alhpa计算等。
SurfaceFlinger服务运行在Android系统的System进程中,它负责管理Android系统的帧缓冲区(Frame Buffer)。Android应用程序为了能够将自己的UI绘制在系统的帧缓冲区上,它们就必须要与SurfaceFlinger服务进行通信。
在APP端执行draw的时候,数据很明显是要绘制到APP的进程空间,但是视图窗口要经过SurfaceFlinger图层混排才会生成最终的帧,而SurfaceFlinger又运行在另一个独立的服务进程,那么View视图的数据是如何在两个进程间传递的呢,普通的Binder通信肯定不行,因为Binder不太适合这种数据量较大的通信,那么View数据的通信采用的是什么IPC手段呢?答案就是共享内存,更精确的说是匿名共享内存。共享内存是Linux自带的一种IPC机制,Android直接使用了该模型,不过做出了自己的改进,进而形成了Android的匿名共享内存
APP进程同SurfaceFlinger共用一块内存,如此,就不需要进行数据拷贝,APP端绘制完毕,通知SurfaceFlinger端合成,再输出到硬件进行显示即可。在每一个Android应用程序与SurfaceFlinger服务之间的连接上加上一块用来传递UI元数据的匿名共享内存,这个共享内存就是 SharedClient.在每一个SharedClient里面,有至多31个SharedBufferStack。SharedBufferStack就是Android应用程序和SurfaceFlinger 的缓冲区堆栈。用来缓冲 UI 元数据。
一般我们就绘制UI的时候,都会采用一种称为“双缓冲”的技术。双缓冲意味着要使用两个缓冲区,其中一个称为Front Buffer,另外一个称为Back Buffer。UI总是先在Back Buffer中绘制,然后再和Front Buffer交换,渲染到显示设备中。这下就可以理解SharedBufferStack的含义了吧?SurfaceFlinger服务只不过是将传统的“双缓冲”技术升华和抽象为了一个SharedBufferStack。可别小看了这个升华和抽象,有了SharedBufferStack之后,SurfaceFlinger 服务就可以使用N个缓冲区技术来绘制UI了。N值的取值范围为2到16。
一个SharedClient对应一个Android应用程序,而一个Android应用程序可能包含有多个窗口,即Surface。从这里也可以看出,一个Android应用程序至多可以包含31个Surface。在SurfaceFlinger服务中,每一个SharedBufferStack都对应一个Surface.SharedBufferStack中的缓冲区只是用来描述UI元数据的,真正的UI数据保存在GraphicBuffer中.为了完整地描述一个UI,SharedBufferStack中的每一个已经使用了的缓冲区都对应有一个GraphicBuffer,用来描述真正的UI数据。当Android应用程序需要更新一个Surface的时候,它就会找到与它所对应的SharedBufferStack,并且从它的空闲缓冲区列表的尾部取出一个空闲的Buffer。。接下来Android应用程序就请求SurfaceFlinger服务为这个Buffer分配一个图形缓冲区GraphicBuffer.SurfaceFlinger 服务分配好图形缓冲区 GraphicBuffer 之后,会将它的编号设置为 index,然后再将这个图形缓冲区 GraphicBuffer 返回给 Android 应用程序访问。Android应用程序得到了 SurfaceFlinger 服务返回的图形缓冲区 GraphicBuffer 之后,就在里面写入UI数据。写完之后,就将与它所对应的缓冲区,即编号为 index 的 Buffer,插入到对应的 SharedBufferStack 的已经使用了的缓冲区列表的头部去。这一步完成了之后,Android 应用程序就通知 SurfaceFlinger 服务去绘制那些保存在已经使用了的缓冲区所描述的图形缓冲区GraphicBuffer了。当一个已经被使用了的Buffer被绘制了之后,它就重新变成一个空闲的 Buffer 了。SharedBufferStack 是在 Android 应用程序和 SurfaceFlinger 服务之间共享的,但是,Android 应用程序和 SurfaceFlinger 服务使用 SharedBufferStack 的方式是不一样的,具体来说,就是 Android 应用程序关心的是它里面的空闲缓冲区列表,而 SurfaceFlinger 服务关心的是它里面的已经使用了的缓冲区列表。从SurfaceFlinger服务的角度来看,保存在 SharedBufferStack中 的已经使用了的缓冲区其实就是在排队等待渲染。为了方便 SharedBufferStack 在 Android 应用程序和 SurfaceFlinger 服务中的访问,Android 系统分别使用 SharedBufferClient 和 SharedBufferServer 来描述 SharedBufferStack ,其中,SharedBufferClient 用来在Android 应用程序这一侧访问 SharedBufferStack 的空闲缓冲区列表,而 SharedBufferServer 用来在SurfaceFlinger 服务这一侧访问 SharedBufferStack 的排队缓冲区列表。
当 Android 应用程序通知 SurfaceFlinger 服务更新UI的时候,只要对应的 SharedBufferStack 中的 queued 的缓冲区的数量大于0,SharedBufferServer 就会将指针 head 的下一个Buffer绘制出来,并且将指针 head 向前移一步.
Dialog原理
Dialog一般在Acitivty启动,所以传入的是Activity的Context,任何创建方法都是基于Dialog基类,所以继续分析Dialog基类,因为 context 是Activity,所以获取到的 WindowManager 属于 Activity,所以Dialog与activity共用windowmanager对象,获得activity的windowmanager对象后,dialog又新建了一个window对象,然后将新创建dialog的window关联到了activity的windowmanager,所以Dialog 的 Window 由附属的 Acitivty WindowManager 对象统一管理。当diloag的show方法调用时,会切换到UI线程去调用create方法,然后会获取当前window的decorview,然后获取windowmanager.layoutparams参数,最后调用了windowmanager的addview方法并将decorview作为参数传入。当dialog被移除的时候也是最终会调用其windowmanager 的removeview方法完成销毁操作。
Toast原理
当我们调用了makeText方法时,其内部会new一个toast对象在,toast的构造方法内部会创建一个TN对象,它是binder对象,控制toast的显示、隐藏、取消操作,并且toast是一个系统窗口。当Toast的show方法调用的时候,会先通过getService获取通知管理服务,之后再将toast的显示请求发送给该服务,在发送的过程中会传递一个binder实体,提供给notificationmanagerservice回调使用,在NotificationManagerService中,真正的服务对象是INotificationManager.Stub,因此到Service端,真正请求的服务是INotificationManager.Stub的enqueueToast,这是个支持多线程操作的对象,通过一个toast队列对消息进行管理,当上一个toast显示超时后,再从队列中取出显示下一个toast。而取出的实际上是ToastRecord,也就是队列上的后续TaskRecord要依赖其他手段来显示,具体View添加到窗口显示的时机都是在APP端,而不是在服务端,对这里而言,就是通过CallBack回调,而前面传递进来的binder实体在NotificationManagerService端就是作为Proxy,以回调APP端,其实Android里面的系统服务都是采用这种处理模式APP与Service互为C/S,record.callback就是APP端TN的代理。其show函数,归根到底就是通过WindowManagerService,将View添加到Window, mWM.addView(mView, mParams);这样Toast就显示出来了。接着看NotificationManagerService端的showNextToastLocked函数,在callback后,会继续通过scheduleTimeoutLocked为Toast添加一个TimeOut监听,并利用该监听将过期的Toast从系统移出。
Android PMS 原理