赞
踩
自己新浪博客转移
一、SystemUI 概述
自android2.2 开始, 原本存在与framework-res.apk 中的状态栏和下拉通知栏界面控制被分割出一个单独的apk 文件, 命名为SystemUI.apk, 保存在System/app 文件夹中。在SystemUI.apk 中, 是存在着状态栏的图标,XML 和控制文件等, 这样的分割, 使我们可以更方便地去修改。
SystemUI 模块中主要包含了USB 和Statusbar 两个子模块,本文将以Statusbar 为主导来向大家阐述SystemUI 中Statusbar 的功能作用,使用方法,模块框架,以及模块内部的重要流程。
1.1 Statusbar 的功能作用
状态栏主要用来显示一些系统图标,应用的通知图标和系统时间。Statusbar 模块就是控制和管理着这些图标,以及通知信息的显示和一些系统开关的。
Ⅰ、状态栏的通知功能(包括时间,通知,系统状态等)
状态栏与 Toast 都可以起到通知、提醒的作用。但它们的实现原理和表现形式却完全不一样。 Toast 其实相当于一个 Widget 组件,有些类似于没有按钮的对话框。而 Statusbar 可与系统其它应用进行交互来显示在屏幕上方状态栏中的信息,并且 Statusbar 还可通过图标的显示变化来反应一些系统状态的变换,如电池电量, wifi ,系统音量,闹钟等。状态栏是一种让你的应用程序或系统信息变化在不使用Activity 的情况下给用户的提醒和通知。
Ⅱ、状态栏的日期显示
状态栏也会显示系统时间,当前日期也会在状态栏显示,只是在默认情况下日期是隐藏的,只有在点击状态栏时才会显示。
1.2 Notification的使用方法
Notification 里面有很多属性下面选择几个常用的介绍一下
icon 这个是设置通知的图标。像天气预报图标。
sound 这个是设置来通知时的提示音。
tickerText 设置提示的文字。
vibrate 来通知时振动。
when 设置来通知时的时间。
contentIntent Notification 的 Intent ,即点击后转向的 Activity
flag
FLAG_NO_CLEAR 设置为这个属性那么通知栏的那个清楚按钮就不会出现
FLAG_ONGOING_EVENT 设置为这个属性那么通知就会像 QQ 图标一样一直在状态栏显示
DEFAULT_ALL 将所有属性设置为默认
DEFAULT_SOUND 将提示声音设置为默认
DEFAULT_VIBRATE 将震动设置为默认
填充 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);
1.3 系统图标的增加删除
这里主要向大家介绍如何添加一个在状态栏显示的系统图标
文件中加资源:
. frameworks\base\core\res\res\drawalbe 中添加系统图标的图片资源
. frameworks\base\core\res\res\values\config.xml 中添加图片引用,这些icon 在这个string array 的位置就决定了其在status bar 上显示的位置。在StatusBarManagerService 初始化的时候就会读取config.xml 下icons
以闹钟为例:
. 在StatusbarPolicy.java 构造函数中初始化所增加的系统图标
. 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);
. 添加接收ACTION图标更新函数
private final void updateAlarm(Intent intent) {
boolean alarmSet = intent.getBooleanExtra(“alarmSet”, false);
mService.setIconVisibility(“alarm_clock”, alarmSet);
}
以上是在状态栏添加显示的系统图标的步骤。
代码执行步骤:
StatusBarPolicy.java -- > setIcon(…)
StatusBarManager.java -- > setIcon(…)
StatusBarManagerService.java -- > setIcon(…)
在 StatusBarService 的onCreate 的时候调用StatusBarManagerService 中的 registerStatusBar (…)
二、模块基本布局
2.1 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 这个基础之上的。
StatusbarService.java是SystemUI的灵魂,增加/ 删除状态栏图标、增加/ 更新/ 删除notification ,Statusbar 拖动动画的实现.
StatusBarView.java(status_bar.xml),状态栏布局,加载系统图标。
StatusBarPolicy.java 图标显示管理器(广播接收者接受系统消息,StatusBarManager mService.setIcon显示)
StatusBarIconView.java 没个图标都要构造一个次view对象。
四、模块流程
在整个Statusbar 模块中包括了多个操作流程(例如StatusbarService 的启动流程),Statusbar 与系统其他应用交互的处理流程(例如Statusbar 对天气预报的通知的处理),还有系统图标的更新流程,statusbar 拖动时动画的绘制流程,以及远程接口的控制流程等。
4.1 启动流程
4.1.1 StatusbarService 的启动流程
首先,当系统进程system_press 启动之后,调用系统SystemServer.java ,在SystemServer 中运行ServerThread.run() 方法时会注册StatusBarManagerService 。StatusBarManagerService中会启动 StatusbarService
4.1.2 系统图标初始化流程
在启动 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 位置。
这样一个电池相关图标就在状态栏上添加或者更新了。删除操作类似。
4.2 通知处理
在应用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 交互了。
在 StatusBarService 启动的时候注册的 mCommandQueue 对象的引用
mCommandQueue = new CommandQueue(Callbacks callbacks, StatusBarIconList list);
注册的 mCommandQueue 中的 callbacks 接口,是由 StatusBarService 实现的
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);
}
其中, StatusBarService 对 addNotification 的实现如下:
public void addNotification(IBinder key, StatusBarNotification notification) {
StatusBarIconView iconView = addNotificationViews(key, notification);
if (shouldTick) {
tick(notification);
}
}
上面的代码中这句 StatusBarIconView iconView = addNotificationViews(key, notification); 以及 tick(notification) 是将图标以及信息显示在 StatusBarView 上的主要语句。
final StatusBarIconView iconView = new StatusBarIconView(this,
notification.pkg + "/0x" + Integer.toHexString(notification.id));
其中这一句利用传来的 notification 构造了图标 view
mNotificationIcons.addView(iconView, iconIndex); 其中 mNotificationIcons 是一个 IconMerger 对象, IconMerger 是继承 LinearLayout 的类。
这一句将图标显示在 StatusBar 上。
如上就是当应用发送完notification 后StatusbarService 是如何将发送的信息显示到Statusbar 上的。
4.3 图标更新
4.3.1 通过广播接收器的方式
StatusBarPolicy 调用 registerReceiver 注册了感兴趣的intent, 当感兴趣的intent 发生时,对图标进行更新。例如,设置一个闹钟后,闹钟模块会发出一个叫做Intent.ACTION_ALARM_CHANGED 的广播,然后StatusBarPolicy 接收到此广播,继而更新状态栏上的闹钟图标。
StatusBarPolicy 只是一个策略管理,实际的功能是StatusBarService 来实现的。StatusBarService 初始化时初始化了一个用于显示statusbar 的StatusBarView 。StatusBarView 里面定义了icon 名字,的显示顺序,对应的png 图等,在StatusBarService 调用makeStatusBarView 方法时实现statusbar 的初始化
4.4 拖动刷新
4.4.1 StatusbarView 从被点击到拖动
从点击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 。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。