android 状态栏(StatusBar)

转自:http://wpf814533631.iteye.com/blog/1585085

一、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. }     
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 来设置。

 

问题:如何区分“正在进行的”和“通知”,谁决定一个事件是“正在进行的”还是持续的“通知”?

通过设置Notificationflag属性可以设定notification是正在进行的还是持续的notification

FLAG_INSISTENTFLAG_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 = newStatusBarIconList();

………

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

} 
  

:在SystemUI模块的SystemUiApp.javaonCreate方法中也会startService,这是当Statusbar意外退出而导致StatusbarService停止服务时会重新启动StatusbarService

4.1.2系统图标初始化流程

 

在启动StatusBarServiceStatusbarService会调用一个makeStatusBarView的方法在里面将创建StatusBarView在创建StatusbarView的过程中会加载系统图标。

在启动StatusbarService的过程中会创建StatusBarPolicy的对象,StatusBarPolicy.java主要负责状态栏显示策略的管理(如状态栏的图标什么时候显示,在什么位置显示等)。StatusBarPolicy的构造函数中初始化了很多系统图标(如电池信息图标,闹钟图标,声音图标,信号栏图标等)。默认时有很多图标是不显示的,需要显示时再进行更新。

图标初始化,以电池电量显示为例,大概关键步骤如下:

通过BroadcastReceiver机制,StatusBarPolicy中注册的mIntentReceiver收到BatteryService广播的ACTION_BATTERY_CHANGED事件触发;

调用updateBattery(intent)开始更新电池状态栏;

intent中解析需要的字段,调用StatusBarManagersetIcon()StatusBarManager是客户端使用的状态栏管理类;

通过IBinder机制跨进程调用StatusBarManagerServicesetIcon()StatusBarManagerService派生于IStatusBarService.Stub,是状态栏管理的服务端,是具体实现;

StatusBarManagerService有一个mIcons成员,这个list成员在StatusBarManagerService创建时加载。StatusBarManagerServicesetIcon()过程中,会又"battery"字段获得在mIcons中的索引,再由包名、图片id和等级创建StatusBarIcon实例,并将这个实例更新StatusBarIconList中所获得索引对应项;

调用CommandQueuesetIcon()CommandQueue派生于IStatusBar.Stub,有一个内部接口Callbacks,这个接口的实现就是StatusBarServiceCommandQueueStatusBarServiceStatusBarManager属于同一个进程,而StatusBarManagerService是一个系统级服务,它们之间必然需要通过IBinder进程间通信;

CommandQueue用于处理状态栏、通知相关的请求,内部维护了一个事件队列,setIcon()会创建一个OP_SET_ICONmassege,发送给Handler处理;

CommandQueue内部也有一个StatusBarIconList实例,这个实例是由StatusBarService创建。在处理OP_SET_ICONmassege前,会先通过getViewIndex获得图标View的位置索引viewIndex,(因为有些图标有可能为空)再更新StatusBarIconList,最后调用Callbacks,也就是StatusBarServiceaddIcon()或者updateIcon()

addIcon()为例,StatusBarServiceaddIcon()会创建一个新的StatusBarIconView,将第步中所创建的StatusBarIcon实例设置进去,然后把这个view添加到LinearLayoutviewIndex位置。

这样一个电池相关图标就在状态栏上添加或者更新了。删除操作类似。

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

位于 NotificationManagerServiceenqueueNotificationInternal函数中:

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 notificationKeys, List notifications) {

        enforceStatusBarService();


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

        mBar = bar;

        synchronized (mIcons) {

            iconList.copyFrom(mIcons);

        }

        synchronized (mNotifications) {

            for (Map.Entry 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();

     }

接口声明如上:

其中,StatusBarServiceaddNotification的实现如下:

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


Java代码 复制代码  收藏代码
  1. "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. }  
/**
   * 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();
}

 权限:

这个方法也是禁用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的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的功能,使用方法,模块框架,以及模块一些实现的主要流程等。

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

你可能感兴趣的:(android开发,StatusBar,SystemUI,状)