自 android2.2 开始 , 本来存在与 framework-res.apk 中的状态栏和下拉通知栏界面控制被分割出一个单独的 apk文件 , 命名为 SystemUI.apk, 保存在 System/app 文件夹中。在 SystemUI.apk 中 , 是存在着状态栏的图标 ,XML 和控制文件等 , 这样的分割 , 使咱们能够更方便地去修改。 php
SystemUI 模块中主要包含了 USB 和 Statusbar 两个子模块,本文将以 Statusbar 为主导来向你们阐述 SystemUI 中Statusbar 的功能做用,使用方法,模块框架,以及模块内部的重要流程。 html
状态栏主要用来显示一些系统图标,应用的通知图标和系统时间。 Statusbar 模块就是控制和管理着这些图标,以及通知信息的显示和一些系统开关的。 java
Ⅰ、状态栏的通知功能(包括时间,通知,系统状态等) android
状态栏与 Toast 均可以起到通知、提醒的做用。但它们的实现原理和表现形式却彻底不同。 Toast 其实至关于一个 Widget 组件,有些相似于没有按钮的对话框。而 Statusbar 可与系统其它应用进行交互来显示在屏幕上方状态栏中的信息,而且 Statusbar 还可经过图标的显示变化来反应一些系统状态的变换,如电池电量, wifi ,系统音量,闹钟等。状态栏 是一种让你的应用程序或系统信息变化在不使用 Activity 的状况下给用户的提醒和通知。 windows
Ⅱ、状态栏的日期显示 并发
状态栏也会显示系统时间,当前日期也会在状态栏显示,只是在默认状况下日期是隐藏的,只有在点击状态栏时才会显示。 app
Ⅰ 、 Notification 主要做用和使用步骤: 框架
Notification 是看不见的程序组件( Broadcast Receiver , Service 和不活跃的 Activity )警示用户有须要注意的事件发生的最好途径 ide
下面主要介绍使用方法步骤: 函数
获取 NotificationManager 实例
获取 Notification 示例,设置属性,并发送通知
Ⅱ、步骤详解
获取 NotificationManager 实例
这个类主要负责将 Notification 在状态栏中显示出来和取消。主要包括 5 个函数:
void cancel(int id) , void cancel(String tag, int id) , void cancelAll() , void notify(int id, Notification notification) , notify(String tag, int id, Notification notification)
看看这五个函数就知道这个类的做用了。可是在初始化对象的时候要注意:
NotificationManager nm;
String service = NOTIFICATION_SERVICE;
nm = (NotificationManager)this.getSystemService(service);
获取 Notification 示例,设置属性,并发送通知
这个类主要是设置 Notification 的相关属性,初始化。
Notification n = new Notification();
Notification 里面有不少属性下面选择几个经常使用的介绍一下(表 1.1 )
icon |
这个是设置通知的图标。像天气预报图标。 |
|
sound |
这个是设置来通知时的提示音。 |
|
tickerText |
设置提示的文字。 |
|
vibrate |
来通知时振动。 |
|
when |
设置来通知时的时间。 |
|
contentIntent |
Notification 的 Intent ,即点击后转向的 Activity |
|
flag |
FLAG_NO_CLEAR |
设置为这个属性那么通知栏的那个清楚按钮就不会出现 |
FLAG_ONGOING_EVENT |
设置为这个属性那么通知就会像 QQ 图标同样一直在状态栏显示 |
|
DEFAULT_ALL |
将全部属性设置为默认 |
|
DEFAULT_SOUND |
将提示声音设置为默认 |
|
DEFAULT_VIBRATE |
将震动设置为默认 |
表 1.1
填充 Notification 的各个属性:
//Notification 的 Intent ,即点击后转向的 Activity
Intent notificationIntent1 = new Intent(this, this.getClass());
notificationIntent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent1 = PendingIntent.getActivity(this, 0, notificationIntent1, 0);
n.contentIntent=contentIntent1;
n.icon = R.drawable.notification_icon;
n.tickerText = "hello";
notification.sound = Uri.parse("file:///sdcard/notification/ringer.mp3");
notification.vibrate = vibrate;
发送通知:
private static final int ID_NOTIFICATION = 1;
mNotificationManager.notify(ID_NOTIFICATION, notification);
通知的更新
若是须要更新一个通知,只须要在设置好 notification 以后,再调用 setLatestEventInfo ,而后从新发送一次通知便可。
自定义通知视图
这部分能够参考官方文档,讲的很详细了。
AndroidSDK: docs/guide/topics/ui/notifiers/notifications.html
Notification.Builder
这个类通常用于管理 Notification ,动态的设置 Notification 的一些属性。即用 set 来设置。
问题:如何区分“正在进行的”和“通知”,谁决定一个事件是“正在进行的”仍是持续的“通知” ?
经过设置 Notification 的 flag 属性能够设定 notification 是正在进行的仍是持续的 notification 。
FLAG_INSISTENT 和 FLAG_ONGOING_EVENT 标志位可让 Notification 成为持续的或正在进行的Notification 。
. Notification 标记为 ONGOING, 以下面的代码所示,它就能用于表示当前正在进行的事件(如来电)。正在进行的事件与“通知” Notification 区别在扩展的状态条窗口中。
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
. 持续的 Notification 一直重复,直到用户取消。下面的代码给出了如何设置 Notification 为持续的:
notification.flags = notification.flags | Notification.FLAG_INSISTENT;
持续 Notification 反复重复开头的 Notification 效果,直到用户取消。持续的 Notification 应该保留给如闹钟的情形,它须要及时的采起响应.
这里主要向你们介绍如何添加一个在状态栏显示的系统图标,相似于系统默认的闹钟图标,声音图标等。
文件中加资源:
. frameworks\base\core\res\res\drawalbe 中添加系统图标的图片资源
. frameworks\base\core\res\res\values\config.xml 中添加图片引用,这些 icon 在这个 string array 的位置就决定了其在 status bar 上显示的位置了。咱们能够从 code 里面得出该结论。因此当你要调换 icon 的顺序时,改动这个 config.xml 就能够了。在 StatusBarManagerService 初始化的时候就会读取 config.xml 下 icons 的 String array 。
这个文件中加代码: StatusBarPolicy.java 以闹钟为例。
. 在 StatusbarPolicy.java 中初始化所增长的系统图标
. 在构造函数中 SetIcon
. StatusBarPolicy 调用 registerReceiver 注册了感兴趣的 intent, 当感兴趣的 intent 发生时,对图标进行更新。例如,设置一个闹钟后,闹钟模块会发出一个叫作 Intent.ACTION_ALARM_CHANGED 的广播,而后 StatusBarPolicy接收到此广播,继而更新状态栏上的闹钟图标。
………
// Alarm clock StatusBarPolicy 构造方法中初始化闹钟图标
mService.setIcon("alarm_clock",R.drawable.stat_notify_alarm, 0);
mService.setIconVisibility("alarm_clock", false);
……..
// StatusBarPolicy 构造方法中注册闹钟改变广播
filter.addAction(Intent.ACTION_ALARM_CHANGED);
…....
. 添加 图标更新函数
private final void updateAlarm(Intent intent) {
boolean alarmSet = intent.getBooleanExtra(“alarmSet”, false);
mService.setIconVisibility(“alarm_clock”, alarmSet);
}
以上是在状态栏添加显示的系统图标的步骤。
代码执行步骤:
StatusBarManagerService.java 中
StatusBarIconList mIcons = new StatusBarIconList();
………
mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));
StatusBarPolicy.java -- > setIcon(…)
StatusBarManager.java -- > setIcon(…)
StatusBarManagerService.java -- > setIcon(…)
在 StatusBarService 的onCreate 的时候调用StatusBarManagerService 中的 registerStatusBar (…)
Statusbar 中的控制开关会作详细的描述,这里就不在赘述。
Android 系统顶上的状态栏是属于 FrameWork 的内容,在此先对 statusbar 的的结构作必定描述。
StatusBar 的布局文件 status_bar.xml ,文件位置:frameworks/base/packages/SystemUI/res/layout/status_bar.xml
LinearLayout android:id="@+id/icons" 咱们看到的状态栏,系统默认是左边放通知图标 notificationIcons ,右边放状态图标 statusIcons
--1. 通知图标区域: IconMerger android:id="@+id/notificationIcons"
--2. 状态图标区域: LinearLayout android:id="@+id/statusIcons"
LinearLayout android:id="@+id/ticker" 显示。在正常状况下 ticker 是不显示的,只有在 StatusBarService 收到通知时它才显示
最后一个是 DateView ,它是在点击 statusbar 时才显示的,默认是隐藏的
Statusbar 内部各类交互以及模块与其余应用的交互都是创建在 StatusbarService 之上的,其中包括 Statusbar视图的建立(包括 Statusbar 、 TrackingView 和 StatusbarExpandedView ),视图动画,系统图标(闹钟、 wifi 、SIM 卡等)的加载和管理,其余应用通知信息的加载显示、更新、删除等,其余应用的远程接口控制(如当打电话时statusbar 处于禁用状态的)对 Android 系统其余应用的通知信息(包括图标、 tracker 、 notification 的布局等)的处理。 SIM 卡信息的控制等。
总之 StatusbarService 是 Statusbar 的灵魂所在,是 Statusbar 的核心,全部关于 Statusbar 的操做处理都是创建在 StatusbarService 这个基础之上的。
在整个 Statusbar 模块中包括了多个操做流程(例如 StatusbarService 的启动流程), Statusbar 与系统其余应用交互的处理流程(例如 Statusbar 对天气预报的通知的处理),还有系统图标的更新流程, statusbar 拖动时动画的绘制流程,以及远程接口的控制流程等。
首先,当系统进程 system_press 启动以后,调用系统 SystemServer.java ,在 SystemServer 中运行ServerThread.run() 方法时会注册 StatusBarManagerService 。
注 :在 SystemUI 模块的 SystemUiApp.java 的 onCreate 方法中也会 startService ,这是当 Statusbar 意外退出而致使 StatusbarService 中止服务时会从新启动 StatusbarService
在启动 StatusBarService 后 , StatusbarService 会调用一个 makeStatusBarView 的方法 , 在里面将建立StatusBarView 在建立 StatusbarView 的过程当中会加载系统图标。
在启动 StatusbarService 的过程当中会建立 StatusBarPolicy 的对象, StatusBarPolicy.java 主要负责状态栏显示策略的管理(如状态栏的图标何时显示,在什么位置显示等)。 StatusBarPolicy 的构造函数中初始化了不少系统图标(如电池信息图标,闹钟图标,声音图标,信号栏图标等)。 。 默认时有不少图标是不显示的,须要显示时再进行更新。
图标初始化,以电池电量显示为例,大概关键步骤以下:
经过 BroadcastReceiver 机制, StatusBarPolicy 中注册的 mIntentReceiver 收到 BatteryService 广播的ACTION_BATTERY_CHANGED 事件触发;
调用 updateBattery(intent) 开始更新电池状态栏;
从 intent 中解析须要的字段,调用 StatusBarManager 的 setIcon() 。 StatusBarManager 是客户端使用的状态栏管理类;
经过 IBinder 机制跨进程调用 StatusBarManagerService 的 setIcon() 。 StatusBarManagerService 派生于IStatusBarService.Stub ,是状态栏管理的服务端,是具体实现;
StatusBarManagerService 有一个 mIcons 成员,这个 list 成员在 StatusBarManagerService 建立时加载。StatusBarManagerService 的 setIcon() 过程当中,会又 "battery" 字段得到在 mIcons 中的索引,再由包名、图片id 和等级建立 StatusBarIcon 实例,并将这个实例更新 StatusBarIconList 中所得到索引对应项;
调用 CommandQueue 的 setIcon() 。 CommandQueue 派生于 IStatusBar.Stub ,有一个内部接口 Callbacks ,这个接口的实现就是 StatusBarService 。 CommandQueue 、 StatusBarService 和 StatusBarManager 属于同一个进程,而 StatusBarManagerService 是一个系统级服务,它们之间必然须要经过 IBinder 进程间通讯;
CommandQueue 用于处理状态栏、通知相关的请求,内部维护了一个事件队列, setIcon() 会建立一个 OP_SET_ICON的 massege ,发送给 Handler 处理;
CommandQueue 内部也有一个 StatusBarIconList 实例,这个实例是由 StatusBarService 建立。在处理OP_SET_ICON 的 massege 前,会先经过 getViewIndex 得到图标 View 的位置索引 viewIndex ,(由于有些图标有可能为空)再更新 StatusBarIconList ,最后调用 Callbacks ,也就是 StatusBarService 的 addIcon() 或者updateIcon() ;
以 addIcon() 为例, StatusBarService 的 addIcon() 会建立一个新的 StatusBarIconView ,将第步中所建立的StatusBarIcon 实例设置进去,而后把这个 view 添加到 LinearLayout 的 viewIndex 位置。
这样一个电池相关图标就在状态栏上添加或者更新了。删除操做相似。
在应用Activity 中实现通知栏图标变化的程序中。是用NotificationManager 对象mNotificationManager 来发送通知。通知为Notification mNotification 对象,填充mNotification 的图标和消息内容以及一个when ,而后构造了一个Intent 对象intent ,包含了本Activity 对象的引用,以及本Activity 的类名,一个PendingIntent pi对象,包含上述Intent 对象以及本Activity 对象的引用,是用于消息列表中显示本Activity 项。点击时从新激活Activity 。而后调用nm.setLatestEventInfo 设置状态栏下拉列表项内容。最后调用nm.notify(1,n) 方法来发送通知,接着改变状态栏的工做就由NotificationManager 和StatusBarManagerService 交互了。
下面来看看NotificationManager 是如何和StatusBarManagerService 交互的。
nm.notify(1,n) 方法作了最重要的事,就是所谓的发送通知 该方法的代码以下: |
public void notify(int id, Notification notification) { notify(null, id, notification); } |
其实是调用了下面这个函数: |
pu blic void notify(String tag, int id, Notification notification) { int[] idOut = new int[1]; INotificationManager service = getService(); String pkg = mContext.getPackageName(); if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut); if (id != idOut[0]) { Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]); } } catch (RemoteException e) { } } |
该函数中主要作了 2 件事:获取一个服务,用该服务将通知事件“入队”插入通知队列,因此应该在某个地方有人在不停的读取通知队列。 下面是 getService 的代码,这里的 INotificationManager.Stub.asInterface(b) 这个形式在好多地方出现,必定要详细理解该类代码,在 Binder 机制中。 |
static public INotificationManager getService() { if (sService != null) { return sService; } IBinder b = ServiceManager.getService("notification"); sService = INotificationManager.Stub.asInterface(b); return sService; } |
在StatusBarManagerService 中添加了该消息:
如上就是当应用发送完 notification 后 StatusbarService 是如何将发送的信息显示到 Statusbar 上的。
StatusBarPolicy 调用 registerReceiver 注册了感兴趣的 intent, 当感兴趣的 intent 发生时,对图标进行更新。例如,设置一个闹钟后,闹钟模块会发出一个叫作 Intent.ACTION_ALARM_CHANGED 的广播,而后 StatusBarPolicy接收到此广播,继而更新状态栏上的闹钟图标。
………
// Alarm clock StatusBarPolicy 构造方法中初始化闹钟图标
mService.setIcon("alarm_clock",R.drawable.stat_notify_alarm, 0);
mService.setIconVisibility("alarm_clock", false);
……..
// StatusBarPolicy 构造方法中注册闹钟改变广播
filter.addAction(Intent.ACTION_ALARM_CHANGED);
…....
// 改变闹钟图标
private final void updateAlarm(Intent intent) {
boolean alarmSet = intent.getBooleanExtra(“alarmSet”, false);
mService.setIconVisibility(“alarm_clock”, alarmSet);
}
StatusBarPolicy 只是一个策略管理,实际的功能是 StatusBarService 来实现的。 StatusBarService 初始化时初始化了一个用于显示 statusbar 的 StatusBarView 。 StatusBarView 里面定义了 icon 名字,的显示顺序,对应的png 图等,在 StatusBarService 调用 makeStatusBarView 方法时实现 statusbar 的初始化
StatusBarManager 有一个更新图标的方法: public void updateIcon(IBinder key, String slot, int iconId, int iconLevel) ,不过 StatusBarManager 并未把方法公开在 sdk 中,可是应该有方法能够访问的。
public void updateIcon(IBinder key, String slot, int iconId, int iconLevel) {
try {
mService.updateIcon(key, slot, mContext.getPackageName(), iconId, iconLevel);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
}
mService 是 StatusBarManager 的一个成员变量, StatusBarManager 被构建的时候被赋值,他是 IStatusBar 的一个代理对象
StatusBarManager(Context context) {
mContext = context;
mService = IStatusBar.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
从点击StatusBar 会出现新的View ,它的流程以下:
StatusBarView 就是StatusBar 所表明的View ,那么查看它的代码,看它处理点击的方法。
它属性变量保存了StatusBarService 的引用mService ,它的点击处理函数onTouchEvent()和onInterceptTouchEvent() 都会调用到StatusBarService 类中的interceptTouchEvent() 方法。
当咱们点击StatusBar 时,会先走到onInterceptTouchEvent() 这个函数,并且这个函数只会在第一次走到,而后会走到onTouchEvent() 方法,这个方法每收到一个TouchEvent() 就会走到,所以会走到屡次。
函数onInterceptTouchEvent() 的处理:
1 、调用到StatusBarService 中的interceptTouchEvent() ,在这里又会走到event.getAction() == MotionEvent.ACTION_DOWN 分支,在分支中,因为mExpanded == false 且y < hitSize 会继续调用prepareTracking(y) 。
2 、函数prepareTracking() 处理:这里因为mExpanded == false 因此会向H 中发送MSG_ANIMATE_REVEAL 消息,进入StatusBarService 本身的消息循环。执行doRevealAnimation() 函数。
3 、函数doRevealAnimation() 处理:这个实现的功能很简单,就是在TrackingView( 就是点击时StatusBar 下出现的View) 尚未彻底显示出来的时候,经过动画的方式,一点一点的将TrackingView 显示出来。
当咱们手指离开时调用顺序以下:
1 、StatusBarView :onTouchEvent() ,此时Action != MotionEvent.ACTION_DOWN 走到 StatusBarService:interceptTouchEvent() ;
2 、interceptTouchEvent() 中会走到分支 else if (mTracking) ;
3 、因为ACTION_UP 因此会调用performFling() ,在这里会向Handler 发送 MSG_ANIMATE 消息,而后进入函数doAnimation() 。
4 、在doAnimation() 因为mAnimY < mStatusBarView.getHeight() 分支成立,会继续调用updateExpandedViewPos(0) 和performCollapse();
5 、在performCollapse() 中,经过mTrackingView.setVisibility(View.GONE) 实现了 让mTrackingView 的隐藏,其实这个函数还实现了其余的View 的隐藏,好比咱们点击后进行拖动所出现的其余View 。
StatusBarManagerService 经过使用 IStatusBar 的 aidl 调用 CommandQueue 在 CommandQueue 中定义Callbacks
StatusBarService 实现了 CommandQueue 中 Callbacks 的回调
public interface Callbacks {
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon);
public void updateIcon(String slot, int index, int viewIndex,
StatusBarIcon old, StatusBarIcon icon);
public void removeIcon(String slot, int index, int viewIndex);
public void addNotification(IBinder key, StatusBarNotification notification);
public void updateNotification(IBinder key, StatusBarNotification notification);
public void removeNotification(IBinder key);
public void disable(int state);
public void animateExpand();
public void animateExpandToggles(boolean needForceStatusBar);
public void animateCollapse();
public void showSIMIndicator(String businessType);
public void hideSIMIndicator();
}
由上述源码咱们能够得出在 StatusbarService.java 中都有增长 / 删除状态栏图标、增长 / 更新 / 删除notification 、禁用 Statusbar 、 SIM 指示信息的隐藏和显示、还有 Statusbar 拖动动画的实现。
如 4.3.2 所讲,经过远程代理方式更新状态栏图标,由于 StatusBarManager 方法在 SDK 中并未公开以下就讲述对StatusBarManager 的使用方法。
在 StatusbarService.java 中的的 disable 方法,就实现并扩展了了 StatusbarManager 的 disable 所实现的功能(如 statusbar 的禁止拖动,不显示通知图标,不显示 ticker 等)。
权限:
<uses-permission android:name="android.permission.STATUS_BAR"/>
<uses-permission android:name="android.permission.DISABLE_STATUS_BAR"/>
这个方法也是禁用statusbar 的一种方法。
StatusBarManagerService.java |
StatusBarManagerService 是服务端 StatusBarService 的管理者 |
顾名思义, StatusBarManagerService 是 StatusBarService 的管理者,是StatusBarService 与外界通讯的桥梁,如4.2 所讲。 在 StatusBarManagerService.java 中,有 addNotification ,removeNotification,updateNotification 等方法用于管理传递给他的通知对象。这个类是一些管理方法,实际执行相关动做的是在 IStatusBar.java 里面,这个是framework/base/core/java/com /android/internal/statusbar/IStatusBar.aidl 自动生成的用于 IPC 的类。 |
表 5.1
StatusBarService.java |
StatusBarservice 是 Statusbar 的核心 |
StatusBarService 这个服务是Statusbar 模块的中心点,全部关于图标的加载、更新、删除等处理,与应用的交互,对通知信息的处理,动画的完成等都是创建在StatusBarService 这个基础之上的。 |
表 5.2
StatusBarPolicy.java |
StatusBarPolicy 负责状态栏显示的策略管理 |
Android 中状态栏上有不少图标,这些图标何时显示何时不显示 ,这些都是StatusBarPolicy 来管理的。 StatusBarPolicy 只是一个策略管理,实际的功能是StatusBarService 来实现的。StatusBarService 初始化时初始化了一个用于显示statusbar 的StatusBarView。StatusBarView 里面定义了icon 名字,的显示顺序,对应的png 图等,在StatusBarService 调用makeStatusBarView 方法时实现statusbar 的初始化。 |
表 5.3
CommandQueue.java |
CommandQueue 是 StatusBarservice 和 StatusBarManagerService 交互的枢纽 |
IStatusBar.java 里面对应的方法是用 CommandQueue 的接口 callback 的形式调用的,callback 的实如今对应的服务提供类也就是 StatusBarService.java 中提供的。 最终执行状态栏更新通知等事件都是在实现的 CommandQueue.Callbacks 里面执行。 |
表 5.4
本文档主要讲述了 SystemUI 模块中 Statusbar 模块的主要功能和实先步骤,文档中介绍了 Statusbar 的功能,使用方法,模块框架,以及模块一些实现的主要流程等。
但愿你们在阅读文档的过程当中,若是发现文档的缺点和错误,请及时反馈,我将以最快的速度加以改进。