1. 状态栏上闹钟图标显示过程简介
闹钟App设置闹钟时,都是通过AlarmManager,即AlarmManagerService服务来实现的,
每次创建或者更新闹钟时,都会调用AlarmManager.setExact()来向AlarmManagerService服务设置闹钟响的时间;
AlarmManagerService服务通过native函数set()来设置内核中闹钟,并且发送广播AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED来通知SystemUI进行系统图标的显示,同时会循环调用native函数waitForAlarm()监听到Alarm变化,当监听到有Alarm时间到了,也会发送AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED广播出去;
SystemUI的PhoneStatusBarPolicy.mIntentReceiver会监听这个广播,当接收到AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED广播后,调用StatusBarManagerService服务来设置状态上显示的system icon和icon visibility:
mService.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, 0, null);
mService.setIconVisibility(SLOT_ALARM_CLOCK, mCurrentUserSetup && hasAlarm);
在StatusBarManagerService.setIcon()中,服务会通过IStatusBar mBar来处理icon的显示情况,
mBar就是SystemUI中BaseStatusBar的IStatusBar.Stub子类CommandQueue类mCommandQueue对象,通过注册到StatusBarManagerService服务中的,
BaseStatusBar创建mCommandQueue对象时,还把实现了CommandQueue.Callbacks接口的BaseStatusBar对象也传递过去,
CommandQueue mCommandQueue = new CommandQueue(this,mIconList);
这样StatusBarManagerService服务就可以通过SystemUI中的IStatusBar.Stub子类CommandQueue类mCommandQueue对象来调用SystemUI来处理icon显示
注意:因为notification icon和system icon是状态栏上左右两边的不同的图标,左边是通知图标(notification icon),右边的是系统图标(system icon); system icon显示是不受notification影响的,所以调用NotificationManager发出通知是不会在system icon区域显示图标的,只会在notification icon区域显示,所以这里实现闹钟图标显示时是Alarm通过AlarmManagerService发广播给SystemUI的PhoneStatusBarPolicy,SystemUI再调用StatusBarManagerService服务添加icon,StatusBarManagerService服务再通过SystemUI的CommandQueue对象来实现在SystemUI中显示icon的。
2. 状态栏上闹钟图标显示过程的时序图

3. 状态栏上闹钟图标显示过程的实现代码
1).闹钟App中调用设置闹钟:
AlarmProvider.java
1 | public static void enableNextAlert(Context context) { |
2).AlarmManager设置闹钟:
在闹钟App中调用AlarmManager的setExact()方法后,这里AlarmManager就会通过IAlarmManager.Stub和AlarmManagerServie通信Binder通信。
AlarmManager.java
1 | public void setExact(int type, long triggerAtMillis, PendingIntent operation) { |
3).AlarmManagerService设置闹钟:
更新SystemUI状态栏中系统图标闹钟图标的广播是从AlarmManagerService这个服务中发出来了,这样可以所有App中设置闹钟的逻辑都会集中在这个服务中来处理。
AlarmManagerService.java
1 | private final IBinder mService = new IAlarmManager.Stub() { |
以下是如何调用内核来设置闹钟和闹钟时间到后的处理逻辑,可以参考如下,不过这里我们只是跟踪状态栏上系统图标中闹钟图标的显示过程,所以就不深入跟踪了,有兴趣的同事可以自己研究。
1 | void rescheduleKernelAlarmsLocked() { //调用内核的native方法设置闹钟 |
4). SystemUI接收广播:
PhoneStatusBarPolicy.java
1 | private final StatusbarManager mService; |
5). StatusBarManagerService调用SystemUI的通信对象来添加系统图标:
StatusBarManagerService.java
1 | private volatile IStatusBar mBar; |
这里的IStatusBar其实就是SystemUI中的CommandQueue类,它是IStatusBar.Stub的子类,它是在BaseStatusBar的start()方法中通过获取StatusBarManagerService服务对象,调用服务对象的registerStatusBar(IStatusBar bar, StatusBarIconList iconList,int switches[], List
1 | protected IStatusBarService mBarService; |
6). SystemUI中CommandQueue通信接口:
CommandQueue.java
1 | private Callbacks mCallbacks; |
7). SystemUI中PhoneStatusBar调用状态栏图标控制类来添加系统图标:
PhoneStatusBar.java
1 | StatusBarIconController mIconController; //状态栏图标控制类 |
8). SystemUI中StatusBarIconController添加系统图标:
StatusBarIconController.java & StatusBarIconView.java
1 | //StatusBarIconController.java |
4.总结
通过以上对状态栏中闹钟图标的显示过程,我们可以知道,状态栏右边的系统图标的显示,是可以由系统App控制来显示的,系统App可以通过获取StatusBarManagerService这个服务对象来设置状态栏中的系统图标。
代码实现如下:1
2
3
4private static final String SLOT_ALARM_CLOCK = "alarm_clock";
private final StatusBarManager mService = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
mService.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, 0, null);
mService.setIconVisibility(SLOT_ALARM_CLOCK, mCurrentUserSetup && hasAlarm);
还要记得在AndroidManifest.xml文件中加入以下权限:
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<uses-permission android:name="android.permission.STATUS_BAR" />