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 示例,设置属性,并发送通知

public class Main extends Activity {
    private Button sendBtn , cancelBtn;
    private Notification n;
    private NotificationManager nm;
    //Notification的标示ID
    private static final int ID = 1;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        //实例化按钮
        sendBtn = (Button)this.findViewById(R.id.sendBtn);
        cancelBtn = (Button)this.findViewById(R.id.cancelBtn);
       
        //获取NotificationManager实例
        String service = NOTIFICATION_SERVICE;
        nm = (NotificationManager)this.getSystemService(service);
       
        //实例化Notification
        n = new Notification();
        //设置显示图标,该图标会在状态栏显示
        int icon = R.drawable.icon;
        //设置显示提示信息,该信息也会在状态栏显示
        String tickerText = "Test Notifaction";
        //显示时间
        long when = System.currentTimeMillis();
       
        n.icon = icon;
        n.tickerText = tickerText;
        n.when = when;
        n.flags = Notification.FLAG_NO_CLEAR;
        n.flags = Notification.FLAG_ONGOING_EVENT;
       
        //为按钮添加监听器
        sendBtn.setOnClickListener(sendClickListener);
        cancelBtn.setOnClickListener(cancelClickListener);
    }
   
    private OnClickListener sendClickListener = new OnClickListener() {
 
  @Override
  public void onClick(View v) {
   //实例化Intent
   Intent intent = new Intent(Main.this, Main.class);
   //获取PendingIntent
   PendingIntent pi = PendingIntent.getActivity(Main.this, 0, intent, 0);
   //设置事件信息
   n.setLatestEventInfo(Main.this, "My Title", "My Content", pi);
   //发出通知
   nm.notify(ID, n);
  
          }
};
private OnClickListener cancelClickListener = new OnClickListener(){
 
  @Override
  public void onClick(View v) {
   nm.cancel(ID);
  }
};
}    

、步骤详解

获取 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

应该保留给如闹钟的情形,它需要及时的采取响应.

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 。

 

<span style="font-size: x-small;">try {

Slog.i(TAG, "Status Bar");

statusBar = new StatusBarManagerService(context);

ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);

} catch (Throwable e) {

Slog.e(TAG, "Failure starting StatusBarManagerService", e);

}

让后调用StatusBarManagerService 的systemReady2() 方法,会在systemReady2() 方法中启动StatusbarService 。

final StatusBarManagerService statusBarF = statusBar;

if (statusBarF != null) statusBarF.systemReady2();

public void systemReady2() {

ComponentName cn = ComponentName.unflattenFromString(mContext.getString(com.android.internal.R.string.config_statusBarComponent));

Intent intent = new Intent();

intent.setComponent(cn);

Slog.i(TAG, "Starting service: " + cn);

mContext.startService(intent);

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

} 

实际上是调用了下面这个函数:

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

位于 NotificationManagerService 的 enqueueNotificationInternal 函数中:

r.statusBarKey =   mStatusBar.addNotification(n);    其中 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 走到 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 。

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 等)。

<span style="font-size: x-small;">/**
   * State is one or more of the DISABLE constants from StatusBarManager.
   */
   public void disable(int state) {
        final int old = mDisabled;
        final int diff = state ^ old;
        mDisabled = state;
        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
                if (SPEW) Slog.d(TAG, "DISABLE_EXPAND: yes");
                animateCollapse();
            }
        }
        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
                if (mTicking) {
                    mTicker.halt();
                } else {
                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
                }
            } else {
                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
                if (!mExpandedVisible) {
                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
                }
            }
        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
                mTicker.halt();
            }
        }
        }
下面在将一种简单的对StatusBarManager的引用方法:
Object service = getSystemService ("statusbar");
    try {
Class <?> statusBarManager = Class.forName
("android.app.StatusBarManager");
Method expand = statusBarManager.getMethod ("disable",int.class);
expand.invoke (service,0x00000001);
} catch (Exception e) {
e.printStackTrace();
}</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 的类。

StatusBarService.java

StatusBarservice Statusbar 的核心

StatusBarService 这个服务是Statusbar 模块的中心点,所有关于图标的加载、更新、删除等处理,与应用的交互,对通知信息的处理,动画的完成等都是建立在StatusBarService 这个基础之上的。

StatusBarPolicy.java

StatusBarPolicy 负责状态栏显示的策略管理

Android 中状态栏上有很多图标,这些图标什么时候显示什么时候不显示 ,这些都是 StatusBarPolicy 来管理的。

StatusBarPolicy

的构造函数里初始化了好几个图标,如闹钟icon

,信号栏icon

等。默认时有很多图标是不显示的,需要显示时再进行更新。StatusBarPolicy

调用
registerReceiver

注册了感兴趣的intent,

当感兴趣的intent

发生时,对图标进行更新。

StatusBarPolicy

只是一个策略管理,实际的功能是StatusBarService

来实现的。StatusBarService

初始化时初始化了一个用于显示statusbar

的StatusBarView

。StatusBarView

里面定义了icon

名字,的显示顺序,对应的png

图等,在StatusBarService

调用makeStatusBarView

方法时实现statusbar

的初始化。

CommandQueue.java

CommandQueue StatusBarservice StatusBarManagerService 交互的枢纽

IStatusBar.java 里面对应的方法是用 CommandQueue 的接口 callback 的形式调用的, callback 的实现在对应的服务提供类也就是 StatusBarService.java 中提供的。

最终执行状态栏更新通知等事件都是在实现的 CommandQueue.Callbacks 里面执行。

六、总结

本文档主要讲述了 SystemUI 模块中 Statusbar 模块的主要功能和实先步骤,文档中介绍了 Statusbar 的功能,使用方法,模块框架,以及模块一些实现的主要流程等。

时间: 2024-10-14 03:32:18

android 状态栏(StatusBar)的相关文章

透明状态栏(StatusBar)的全适配

透明状态栏(StatusBar)的全适配 状态栏指定android手机上顶部显示手机状态信息的位置. 透明状态栏是android 自4.4开始新加入的功能,他可以是状态栏根据我们自己想要的颜色进行定义,使titleBar能够和状态栏融为一体,增加沉浸感. 实现中遇到的阻碍 因为状态栏是在4.4加入的,所以在4.4以前的系统上无法实现.现在世面上的手机android4.4一下的占比不是很大,所以不是太影响. 网上有很多关于透明状态栏实现的技术博客,以及我之前的博客透明状态栏(沉浸式状态栏)中,实现

android 状态栏操作

最近太忙了.好长时间没动博客,把原来的东西拿出来都重新温习下. private void collapseStatusBar() { int currentApiVersion = android.os.Build.VERSION.SDK_INT; try { Object service = getSystemService("statusbar"); Class<?> statusbarManager = Class .forName("android.app

张高兴的 UWP 开发笔记:手机状态栏 StatusBar

UWP 有关应用标题栏 TitleBar 的文章比较多,但介绍 StatusBar 的却没几篇,在这里随便写写.状态栏 StatusBar 用法比较简单,花点心思稍微设计一下,对应用会是个很好的点缀. 说明一下,当应用运行在 PC 上时我们叫 TitleBar ,运行在 Mobile 上时我们叫 StatusBar ,这是两个不同的玩意儿. 在使用 StatusBar 之前,你需要在项目的引用里添加 Windows Mobile Extensions for the UWP ,并且引用 Wind

01- - -1.获得项目中info.plist文件的内容 2.沙盒的数据存储及读取 3.控制器view的高度和状态栏statusBar的关系 4.[UIScreen mainScreen].applicationFrame的取值 5.按钮的状态 6.错误调试技巧 7.按钮的各种状态设置

1.获得项目中info.plist文件的内容 1> [NSBundle mainBundle].infoDictionary 2> 版本号在info.plist中的key:kCFBundleVersionKey 2.沙盒的数据存储及读取 1> 数据存储: [[NSUserDefaults standardUserDefaults] setObject:version forKey:versionKey]; 存储数据时记得同步一下 [[NSUserDefaults standardUser

获取Android状态栏高度的屡试不爽的方法

如下代码所示: [java] view plaincopy private int getStatusBarHeight() { Class<?> c = null; Object obj = null; Field field = null; int x = 0, sbar = 0; try { c = Class.forName("com.android.internal.R$dimen"); obj = c.newInstance(); field = c.getFi

Android状态栏透明(沉浸式效果)

Android状态栏透明(沉浸式效果) 默认效果 沉浸式效果 方式一 源码 下载地址(Android Studio工程):http://download.csdn.net/detail/q4878802/9058275 1. 修改状态栏和导航栏的属性为透明 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //透明状态栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_T

Android状态栏着色

版权声明:本文为博主原创文章,未经博主允许不得转载. 前言 状态栏着色,也就是我们经常听到的沉浸式状态栏,关于沉浸式的称呼网上也有很多吐槽的,这里就不做过多讨论了,以下我们统称状态栏着色,这样我觉得更加容易理解. 从Android4.4开始,才可以实现状态栏着色,并且从5.0开始系统更加完善了这一功能,可直接在主题中设置<item name="colorPrimaryDark">@color/colorPrimaryDark</item>或者getWindow(

Android获取系统顶部状态栏(StatusBar)与底部工具栏(NavigationBar)的高度

Android一些设备都有上下两条bar,我们可以获取这些bar的信息.下面放上获取高度的代码.代码注释和其他方法有空再放. 原文地址http://www.cnblogs.com/rossoneri/p/4142962.html 获取顶部status bar 高度 private int getStatusBarHeight() { Resources resources = mActivity.getResources(); int resourceId = resources.getIden

Android -- 状态栏高度

干货 Class<?> c = null; Object obj = null; Field field = null; int x = 0, sbar = 0; try { c = Class.forName("com.android.internal.R$dimen"); obj = c.newInstance(); field = c.getField("status_bar_height"); x = Integer.parseInt(field