android 状态栏(StatusBar)

简介:

一、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 Statusbar 的使用方法

1.2.1 notification 的使用

  Notification 主要作用和使用步骤:

Notification 是看不见的程序组件( Broadcast Receiver , Service 和不活跃的 Activity )警示用户有需要注意的事件发生的最好途径

下面主要介绍使用方法步骤:

获取 NotificationManager 实例

获取 Notification 示例,设置属性,并发送通知

Java代码   收藏代码
  1. public class Main extends Activity {  
  2.     private Button sendBtn , cancelBtn;  
  3.     private Notification n;  
  4.     private NotificationManager nm;  
  5.     //Notification的标示ID  
  6.     private static final int ID = 1;  
  7.      
  8.     @Override  
  9.     public void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.main);  
  12.          
  13.         //实例化按钮  
  14.         sendBtn = (Button)this.findViewById(R.id.sendBtn);  
  15.         cancelBtn = (Button)this.findViewById(R.id.cancelBtn);  
  16.          
  17.         //获取NotificationManager实例  
  18.         String service = NOTIFICATION_SERVICE;  
  19.         nm = (NotificationManager)this.getSystemService(service);  
  20.          
  21.         //实例化Notification  
  22.         n = new Notification();  
  23.         //设置显示图标,该图标会在状态栏显示  
  24.         int icon = R.drawable.icon;  
  25.         //设置显示提示信息,该信息也会在状态栏显示  
  26.         String tickerText = "Test Notifaction";  
  27.         //显示时间  
  28.         long when = System.currentTimeMillis();  
  29.          
  30.         n.icon = icon;  
  31.         n.tickerText = tickerText;  
  32.         n.when = when;  
  33.         n.flags = Notification.FLAG_NO_CLEAR;  
  34.         n.flags = Notification.FLAG_ONGOING_EVENT;  
  35.          
  36.         //为按钮添加监听器  
  37.         sendBtn.setOnClickListener(sendClickListener);  
  38.         cancelBtn.setOnClickListener(cancelClickListener);  
  39.     }  
  40.      
  41.     private OnClickListener sendClickListener = new OnClickListener() {  
  42.    
  43.   @Override  
  44.   public void onClick(View v) {  
  45.    //实例化Intent  
  46.    Intent intent = new Intent(Main.this, Main.class);  
  47.    //获取PendingIntent  
  48.    PendingIntent pi = PendingIntent.getActivity(Main.this0, intent, 0);  
  49.    //设置事件信息  
  50.    n.setLatestEventInfo(Main.this"My Title""My Content", pi);  
  51.    //发出通知  
  52.    nm.notify(ID, n);  
  53.     
  54.        }  
  55. };  
  56. private OnClickListener cancelClickListener = new OnClickListener(){  
  57.    
  58.   @Override  
  59.   public void onClick(View v) {  
  60.    nm.cancel(ID);  
  61.   }  
  62. };  
  63. }     

、步骤详解

获取 NotificationManager 实例

这个类主要负责将 Notification 在状态栏中显示出来和取消。主要包括 个函数:

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 应该保留给如闹钟的情形,它需要及时的采取响应.


1.2.2 系统图标的增加删除

这里主要向大家介绍如何添加一个在状态栏显示的系统图标,类似于系统默认的闹钟图标,声音图标等。

文件中加资源:

. 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 中的控制开关会做详细的描述,这里就不在赘述。

二、模块基本布局

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 这个基础之上的。

 

四、模块流程

在整个 Statusbar 模块中包括了多个操作流程(例如 StatusbarService 的启动流程), Statusbar 与系统其他应用交互的处理流程(例如 Statusbar 对天气预报的通知的处理),还有系统图标的更新流程, statusbar 拖动时动画的绘制流程,以及远程接口的控制流程等。

4.1 启动流程

4.1.1 StatusbarService 的启动流程

 

首先,当系统进程 system_press 启动之后,调用系统 SystemServer.java ,在 SystemServer 中运行ServerThread.run() 方法时会注册 StatusBarManagerService 

 

Java代码   收藏代码
  1. <span style="font-size: x-small;">try {  
  2.   
  3. Slog.i(TAG, "Status Bar");  
  4.   
  5. statusBar = new StatusBarManagerService(context);  
  6.   
  7. ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);  
  8.   
  9. catch (Throwable e) {  
  10.   
  11. Slog.e(TAG, "Failure starting StatusBarManagerService", e);  
  12.   
  13. }  
  14.   
  15. 让后调用StatusBarManagerService 的systemReady2() 方法,会在systemReady2() 方法中启动StatusbarService 。  
  16.   
  17. final StatusBarManagerService statusBarF = statusBar;  
  18.   
  19. if (statusBarF != null) statusBarF.systemReady2();  
  20.   
  21. public void systemReady2() {  
  22.   
  23. ComponentName cn = ComponentName.unflattenFromString(mContext.getString(com.android.internal.R.string.config_statusBarComponent));  
  24.   
  25. Intent intent = new Intent();  
  26.   
  27. intent.setComponent(cn);  
  28.   
  29. Slog.i(TAG, "Starting service: " + cn);  
  30.   
  31. mContext.startService(intent);  
  32.   
  33. } </span>  
 

 :在 SystemUI 模块的 SystemUiApp.java  onCreate 方法中也会 startService ,这是当 Statusbar 意外退出而导致 StatusbarService 停止服务时会重新启动 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 交互了。

下面来看看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) {

         }

     }

该函数中主要做了 件事:获取一个服务,用该服务将通知事件“入队”插入通知队列,所以应该在某个地方有人在不停的读取通知队列。

下面是 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 中添加了该消息:

位于 NotificationManagerService  enqueueNotificationInternal 函数中:

r.statusBarKey =   mStatusBar.addNotification(n);    其中 是由 notification 对象构造的 statusBarNotification 对象   mStatusBar 是一个 StutusBarManagerService 的引用。

 addNotification() 中执行了:

synchronized (mNotifications) {

             IBinder key = new Binder();

             mNotifications.put(key, notification);

             if (mBar != null) {

                 try {

                     mBar.addNotification(key, notification);

                 } catch (RemoteException ex) {

                 }

             }

             return key;

这里先执行了 mNotification.put 将该通知加入一个 hashMap 结构体中

然后执行了 mBar.addNotification(key,notification)   调用了 CommandQueue 中的addNotification 方法,该方法利用 Handler 调用了 mCallbacks.addNotification 方法,其实就是 StatusBarService 中的。

这个 mBar 是由方法:

public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,

             List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {

         enforceStatusBarService();


         Slog.i(TAG, "registerStatusBar bar=" + bar);

         mBar = bar;

         synchronized (mIcons) {

             iconList.copyFrom(mIcons);

         }

         synchronized (mNotifications) {

             for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {

                 notificationKeys.add(e.getKey());

                 notifications.add(e.getValue());

             }

         }

     }

 StatusBarService 启动的时候注册的 mCommandQueue 对象的引用  

mCommandQueue = new CommandQueue(this, iconList);

由该对象的实例化以及其构造函数

public CommandQueue(Callbacks callbacks, StatusBarIconList list) {

         mCallbacks = callbacks;

         mList = 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);

         public void disable(int state);

         public void animateExpand();

         public void animateCollapse();

     }

接口声明如上:

其中, StatusBarService  addNotification 的实现如下:

public void addNotification(IBinder key, StatusBarNotification notification) {

boolean shouldTick = true;

if (notification.notification.fullScreenIntent != null) {

shouldTick = false;

Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");

try {

notification.notification.fullScreenIntent.send();

} catch (PendingIntent.CanceledException e) {

}

}


StatusBarIconView iconView = addNotificationViews(key, notification);

if (iconView == null) return;


if (shouldTick) {

tick(notification);

}


// Recalculate the position of the sliding windows and the titles.

setAreThereNotifications();

updateExpandedViewPos(EXPANDED_LEAVE_ALONE);

}

所以到头来 enqueueNotificationInternal 方法中 mBar.addNotification(key, notification); 其实是调用了 StatusBarService 实现的 addNotification 方法,即上面的代码。

上面的代码中这句 StatusBarIconView iconView = addNotificationViews(key, notification);   以及 tick(notification)   可能是将图标以及信息显示在 StatusBarView 上的主要语句。接着进入这两个方法。

addNotificationViews():

StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {

         NotificationData list;

         ViewGroup parent;

         final boolean isOngoing = notification.isOngoing();

         if (isOngoing) {

             list = mOngoing;

             parent = mOngoingItems;

         } else {

             list = mLatest;

             parent = mLatestItems;

         }

         // Construct the expanded view.

         final View[] views = makeNotificationView(notification, parent);

         if (views == null) {

             handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "

                     + notification);

             return null;

         }

         final View row = views[0];

         final View content = views[1];

         final View expanded = views[2];

         // Construct the icon.

         final StatusBarIconView iconView = new StatusBarIconView(this,

                 notification.pkg + "/0x" + Integer.toHexString(notification.id));

         final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,

                     notification.notification.iconLevel, notification.notification.number);

         if (!iconView.set(ic)) {

             handleNotificationError(key, notification, "Coulding create icon: " + ic);

             return null;

         }

         // Add the expanded view.

         final int viewIndex = list.add(key, notification, row, content, expanded, iconView);

         parent.addView(row, viewIndex);

         // Add the icon.

         final int iconIndex = chooseIconIndex(isOngoing, viewIndex);

         mNotificationIcons.addView(iconView, iconIndex);

         return iconView;

     }

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接收到此广播,继而更新状态栏上的闹钟图标。


………

// 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 的初始化

 4.3.2 通过远程代理方式

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));
 
    }

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 走到 StatusBarServiceinterceptTouchEvent() ;

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 。

 

4.5 远程接口

4.5.1 Statusbar 远程接口简介

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.5.2 StatusBarManager 的使用

 4.3.2 所讲,通过远程代理方式更新状态栏图标,因为 StatusBarManager 方法在 SDK 中并未公开如下就讲述对StatusBarManager 的使用方法。

 StatusbarService.java 中的的 disable 方法,就实现并扩展了了 StatusbarManager  disable 所实现的功能(如 statusbar 的禁止拖动,不显示通知图标,不显示 ticker 等)。


Java代码   收藏代码
  1. <span style="font-size: x-small;">/** 
  2.    * State is one or more of the DISABLE constants from StatusBarManager. 
  3.    */  
  4.    public void disable(int state) {  
  5.         final int old = mDisabled;  
  6.         final int diff = state ^ old;  
  7.         mDisabled = state;  
  8.         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {  
  9.             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {  
  10.                 if (SPEW) Slog.d(TAG, "DISABLE_EXPAND: yes");  
  11.                 animateCollapse();  
  12.             }  
  13.         }  
  14.         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {  
  15.             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {  
  16.                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");  
  17.                 if (mTicking) {  
  18.                     mTicker.halt();  
  19.                 } else {  
  20.                     setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);  
  21.                 }  
  22.             } else {  
  23.                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");  
  24.                 if (!mExpandedVisible) {  
  25.                     setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);  
  26.                 }  
  27.             }  
  28.         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {  
  29.             if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {  
  30.                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");  
  31.                 mTicker.halt();  
  32.             }  
  33.         }  
  34.      }  
  35. 下面在将一种简单的对StatusBarManager的引用方法:  
  36. Object service = getSystemService ("statusbar");  
  37.     try {  
  38. Class <?> statusBarManager = Class.forName  
  39. ("android.app.StatusBarManager");  
  40. Method expand = statusBarManager.getMethod ("disable",int.class);  
  41. expand.invoke (service,0x00000001);  
  42. catch (Exception e) {  
  43. e.printStackTrace();  
  44. }</span>  

 权限:

<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
 
的构造函数里初始化了好几个图标,如闹钟icon ,信号栏icon 等。默认时有很多图标是不显示的,需要显示时再进行更新。StatusBarPolicy 调用 registerReceiver注册了感兴趣的intent, 当感兴趣的intent 发生时,对图标进行更新。

StatusBarPolicy 只是一个策略管理,实际的功能是StatusBarService 来实现的。StatusBarService 初始化时初始化了一个用于显示statusbar StatusBarViewStatusBarView 里面定义了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 的功能,使用方法,模块框架,以及模块一些实现的主要流程等。

希望大家在阅读文档的过程中,如果发现文档的缺点和错误,请及时反馈,我将以最快的速度加以改进。

相关文章
|
Java API Android开发
Android 最新实现沉浸式状态栏的效果
Android 最新实现沉浸式状态栏的效果
242 0
|
7月前
|
Android开发
Android 状态栏WiFi图标的显示逻辑
Android 状态栏WiFi图标的显示逻辑
190 0
|
4月前
|
API Android开发 开发者
Android经典实战之用WindowInsetsControllerCompat方便的显示和隐藏状态栏和导航栏
本文介绍 `WindowInsetsControllerCompat` 类,它是 Android 提供的一种现代化工具,用于处理窗口插入如状态栏和导航栏的显示与隐藏。此类位于 `androidx.core.view` 包中,增强了跨不同 Android 版本的兼容性。主要功能包括控制状态栏与导航栏的显示、设置系统窗口行为及调整样式。通过 Kotlin 代码示例展示了如何初始化并使用此类,以及如何设置系统栏的颜色样式。
244 2
|
4月前
|
API Android开发 Kotlin
Android实战经验分享之如何获取状态栏和导航栏的高度
在Android开发中,掌握状态栏和导航栏的高度对于优化UI布局至关重要。本文介绍两种主要方法:一是通过资源名称获取,简单且兼容性好;二是利用WindowInsets,适用于新版Android,准确性高。文中提供了Kotlin代码示例,并对比了两者的优缺点及适用场景。
393 1
|
6月前
|
API Android开发
31. 【Android教程】状态栏通知:Notification
31. 【Android教程】状态栏通知:Notification
597 1
|
7月前
|
XML 存储 测试技术
Android系统 添加动态控制SystemUI状态栏、导航栏和下拉菜单
Android系统 添加动态控制SystemUI状态栏、导航栏和下拉菜单
1334 1
|
7月前
|
API Android开发
Android 打开通知中心(StatusBar)
Android 打开通知中心(StatusBar)
118 0
|
XML Java API
Android 沉浸式状态栏必知必会
Android 沉浸式状态栏追根究底
827 0
|
Java 测试技术 API
Android透明状态栏和导航栏方案最终版
Android透明状态栏和导航栏方案最终版
895 0
|
Android开发
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景
889 0
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景