系统窗口Toast显示源码解析

来源:互联网 发布:d3.js圆形动态加载 编辑:程序博客网 时间:2024/05/01 01:18

     让Android融入我的生活!

     最近这段时间一直比较闲,没事了就看看源码,研究一下Android框架,随便搞搞html5,就像打杂一样,东打打,西敲敲,前天跟另外一个Android开发同事闲聊,问问他在搞啥,他说在看看蓝牙、Wifi的一些研究,说是智能家居以后肯定是热潮,提前作点准备,各种软件硬件配合,实现各种控制,并且建议我,不要太多研究源码,因为研究源码以后能用是上的就是在华为那些大公司搞搞系统移植啥的,没有其他太多的用处,听人家一说觉得,觉得确实有道理,这也引发了我一些对Android以后道路的思考,不过短时间的思考肯定是无果的,大家如果有啥好的建议,请提一下哈,谢谢!

     好了,也就是一边看书,一边搞源码,没事了,看到人家用Toast的,就想到,其实我们要研究源码,根本不需要到处找方向,我们平时用的最基本的,都是可以研究的,比如Activity的setContentView()是如何添加了一个新窗口,并进行详细绘制,最终显示在界面上的;startActivity(intent)当中,intent所带的数据是如何传到下一个Activity的;Activity是如何启动的等等各种问题,都涉及到Android底层的,都值得我们研究,在此,也给大家推荐如下博主和博客:



     罗老师,我们公认的Android大牛的所有博客:

     Android系统默认Home应用程序(Launcher)的启动过程源代码分析,这博客写的真是太牛了,各种分析,我觉得可能每个看过人都想猛赞!

     Android应用程序启动过程源代码分析

     Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析


     qinjning:     

     Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起

     Android中View绘制流程以及invalidate()等相关方法分析


     还有一篇是介绍android view绘制流程的,深入到surfaceflinger中了,也讲的非常好,不过没找到链接地址了,网上资源太多了,大家可以各种搜!

     行吧,废话就说这么多了,开始进入我自己的时刻,Toast原理实现分析。

     我们在自己的Activity中要调用Toast是非常简单的,Toast.makeText(this, "this is toast", Toast.LENGTH_SHORT).show()这样一句代码就可以显示了,那我们来看一下系统调用过程是怎么样的呢?

     我们首先要清楚,添加一个Toast就只需要作好两件事,第一,建立一个窗口,上面设置好文字内容;第二,将这个窗口交给系统,显示出来。

     下面分为两步介绍:

     一、系统窗口Toast的创建

     系统窗口的含义有两个方面:1,系统窗口不依赖于应用,而应用类窗口必须要有一个Activity与之对应;2,系统窗口是由系统创建的,应用程序没有权限创建。但有三个系统窗口除外,分别为TYPE_TOAST、TYPE_INPUT_METHODTYPE_WALLPAPTER,从代码的角度看,为什么添加其他窗口需要权限检查,而这三个不需要呢?首先在WMS这端,当客户端请求添加窗口进,就会调用WMS中的addWindow()方法,这段代码在frameworks\base\services\core\java\com\android\server\wm\WindowManagerService中:

     如果权限检查失败,则返回错误标识res,这段代码会对添加权限进行检查,mPolicy对象是一个WindowManagerPolicy,而WindowManagerPolicy仅仅是一个interface,真正实现的是PhoneWindowManager类,于是调用到PhoneWindowManager类中checkAddPermission方法对权限进行检查,这段代码在frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager中:

     对权限进行检查的代码逻辑比较简单,第一种类型就是TYPE_TOAST,从中可以看出各种权限,TYPE_DREAM、TYPE_INPUT_METHOD、TYPE_WALLPAPER、TYPE_PRIVATE_PRESENTATION、TYPE_VOICE_INTERACTION、TYPE_PHONE、TYPE_PRIORITY_PHONE、TYPE_SYSTEM_ALERT、TYPE_SYSTEM_ERROR、TYPE_SYSTEM_OVERLAY,Framework中有一个系统进程system_process,该进程是有权限创建系统窗口的,常见的系统窗口包括状态栏窗口、系统错误对话框等,如果抛开权限问题, 应用程序其实也可以创建任何系统窗口,WmS在创建窗口时,检查完权限后,就会像创建普通窗口一样创建系统窗口。

     二、Toast调用流程

     Toast.makeText(this, "this is toast", Toast.LENGTH_SHORT).show()即可显示一个Toast信息,先看一下makeText()源码,在Toast类中:

     
该方法中会根据com.android.internal.R.layout.transient_notification布局文件创建一个View对象,并把该对象赋值给 Toast 的mNextView变量,这就为自定义Toast窗口提供了机会,当然,这仅限于Framework 层面的定制,应用程序是无法自定义Toast窗口风格的,其中inflate()方法的流程解析大家可在网上参考其他博客。
     创建了 Toast对象后,接下来调用show()方法,此方法在Toast中:
此方法中INotificationManager为一个远程接口,TN为Toast的内部类,继承ITransientNotification.Stub,
该方法则通过调用“ 通知管理器(NotificationManager) ” 的enqueueToast(),把该Toast对象加入到管理器的消息队列中,实现在frameworks\base\services\core\java\com\android\server\notification\NotificationManagerService类中:
     创建了一个IBinder对象,new INotificationManager.Stub(),构建了一个INotificationManager的代理,调用enqueueToast方法往通知管理器所在的队列变量mToastQueue中添加客户端的Toast对象,所有应用程序都是通过调用NotificationManagerService该方法添加客户端的Toast请 
求的。因此,该方法中使用了 synchronized(mToastQueue)解决重入问题。 
添加完Toast后,接下来就会执行NotificationManagerService类中的showNextToastLoced()方法:

     该方法第一步会从mToastQueue中取出最前面的一个Toast对象,mToastQueue.get(0),ToastRecord为NotificationManagerService的一个内部类,并且有final ITransientNotification callback这个成员变量,show方法就是它来执行的,ITransientNotification代码在frameworks\base\core\java\android\app\ITransientNotification目录下,本身是一个接口,只有show()和hide()两个方法,实现类为Toast的内部类TN extends ITransientNotification.Stub,TN对象的show()方法,该对象是一个客户端的Binder, 其show()方法真正完成向WmS添加窗口。

     第二步会发送一个异步消息,也就是在Toast显示一定时间后清除该Toast窗口,该消息的处理函数是cancelToastLocked()。 cancelToastLocked()方法第一步会取出mToastQueue中第一个Toast对象,然后调用其内部的TN对象的hide()方法,该方法会通知WmS移除Toast窗口第二步会从stQueue中移除该Toast对象, 并检查是否还有Toast对象,如果有的话,则继续调用showNextToastLocked()。 以上流程实际上是NotificationManager把客户端显示Toast的工作进行了串行化。串行化的原因是WmS在给系统窗口分配层值时,不会像其他类型窗口那样去动态调整备口的层值,而且从产品设计的角度来讲,每一种系统要求仅有一个显示在屏幕上,所以,客户端的请求被串行化,以保证只有一个该类窗口。


     


0 0