android Notification分析

通常,在手机有未接电话,收到消息或者挂着退出主界面的QQ,在状态栏会有一个Notification,那么,这个notification如何产生的?

通常做法:

Intent intent = new Intent();

        intent.setClass(this, Noti.class);

//一般而言,对于需要点击Notification需要迁移到对应的View的需要下面这个操作

        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        

        PendingIntent mPendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

        

        Notification mNotification = new Notification();

        mNotification.icon = R.drawable.presence_online;//icon id

        mNotification.tickerText = “Online”;

        mNotification.defaults = Notification.DEFAULT_SOUND ;

        mNotification.flags = Notification.FLAG_AUTO_CANCEL;

        

        mNotification.setLatestEventInfo(this, “QQ”, “Online”, mPendingIntent);

    mNotificationManager.notify(0, mNotification);

在android手机中,有一个Notification类,这个类从根本上讲只是一个记录我们需要在状态栏显示Notification icon的一些信息,比如:要显示的Icon的id,led灯闪烁以及闪烁颜色和闪烁时间,让手机产生振动等。Notification的flag有FLAG_SHOW_LIGHTS,FLAG_AUTO_CANCEL等。有关Notification有三个主要的函数:setLatestEventInfo,notify和cancel

在下面这段代码中,RemoteViews 是显示在扩展状态栏上的,也就是将状态栏拉下时显示的Notification,

contentView.setImageViewResource(com.android.internal.R.id.icon, this.icon)则是将我们设定的icon赋给id为com.android.internal.R.id.icon的ImageView,以便在statusBar中调用显示。同样下面都是将本地信息设置为全局信息以便在statusbar中显示。特别要提一下contentIntent是一个PendingIntent,它负责在点击Notification时迁移的View。Notification.java:

public void setLatestEventInfo(Context context,

            CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {

  RemoteViews contentView = new RemoteViews(context.getPackageName(),

              com.android.internal.R.layout.status_bar_latest_event_content);

  if (this.icon != 0) {//实际上就是要显示的消息对应的icon

          contentView.setImageViewResource(com.android.internal.R.id.icon, this.icon);

  }

  if (contentTitle != null) {//Notification对应的title

            contentView.setTextViewText(com.android.internal.R.id.title, contentTitle);

  }

  if (contentText != null) {//状态所对应的下标题

           contentView.setTextViewText(com.android.internal.R.id.text, contentText);

 }

 if (this.when != 0) {

          contentView.setLong(com.android.internal.R.id.time, “setTime”, when);

     }

 this.contentView = contentView;

 this.contentIntent = contentIntent;

}   

那么它是如何显示到状态栏上的?

NotificationManager调用Notify函数 :

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) {

        }

    }

最重要的是,在NotificationManagerService.java中,service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);它调用NotificationManagerService中方法enqueueNotificationInternal发出声音振动和灯光,这都只要调用相关的系统服务做就可以了。

以何种方式进行通知状态栏显示的?

   if (notification.icon != 0) {

                StatusBarNotification n = new StatusBarNotification(pkg, id, tag,

                        r.uid, r.initialPid, notification);

                if (old != null && old.statusBarKey != null) {

                    r.statusBarKey = old.statusBarKey;

                    long identity = Binder.clearCallingIdentity();

                    try {

                        mStatusBar.updateNotification(r.statusBarKey, n);

                    }

                    finally {

                        Binder.restoreCallingIdentity(identity);

                    }

                } else {

                    long identity = Binder.clearCallingIdentity();

                    try {

                        r.statusBarKey = mStatusBar.addNotification(n);

                        mAttentionLight.pulse();

                    }

                    finally {

                        Binder.restoreCallingIdentity(identity);

                    }

                }

                sendAccessibilityEvent(notification, pkg);

在这里调用了statusbarService.java的updateNotification方法,又调用addNotificationViews将icon显示到状态栏并显示相关信息。

updateNotification这个方法中调用addNotificationViews:

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

        }

至此,就完成了添加一个icon到Statusbar,同时还有text、title等等。

以DownLoadProvider下载完成后点击下载的Notification,然后消失Notification为例解释FLAG_AUTO_CANCEL原因:

在DownLoadProvider这个app中,DownLoadService会创建一个用于更新下载完成Notification的类DownloadNotification,还有一个开启的线程:updateThread,在这个线程中调用了mNofier.updateNofication(mDownloads.values());然后再DownloadNotification.java中更新下载进度和完成状态,当下载完成时会在StatusBar上显示下载完成这个icon。

那么,系统如何维护状态栏上的Notification呢?也就是如何点击后取消这个Notification呢?

在StatusBarService下private class Launcher implements View.OnClickListener

是监听点击StatusBarService的函数,当点击这个Notification时,在void OnClick()方法中有这样的callback:mBarService.onNotificationClick(mPkg, mTag, mId);

mBarService是这样定义的:

IStatusBarService mBarService = IStatusBarService.Stub.asInterface(

                ServiceManager.getService(Context.STATUS_BAR_SERVICE));

这就意味着mBarService使用的是StatusbarManagerService.java中定义的接口,如下:

public interface NotificationCallbacks {

        void onSetDisabled(int status);

        void onClearAll();

        void onNotificationClick(String pkg, String tag, int id);

        void onPanelRevealed();

        void onNotificationError(String pkg, String tag, int id,

                int uid, int initialPid, String message);

}

onNotificationClick是在NotificationManagerService.java中实现的。因此,当点击statusbar上的Notification时就调用NotificationManagerService中的这个响应函数:

 public void onNotificationClick(String pkg, String tag, int id) {

        cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,

                 Notification.FLAG_FOREGROUND_SERVICE);

 }

cancelNotification定义如下:

    private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,

            int mustNotHaveFlags) {

        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);

        synchronized (mNotificationList) {

            int index = indexOfNotificationLocked(pkg, tag, id);

            if (index >= 0) {

                NotificationRecord r = mNotificationList.get(index);

                if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {

                    return;

                }

                if ((r.notification.flags & mustNotHaveFlags) != 0) {

                    return;

                }

                mNotificationList.remove(index);

                cancelNotificationLocked(r);

                updateLightsLocked();

            }

        }

这就解释了为什么只有设置Notification 的flag为Notification.FLAG_AUTO_CANCEL才能取消Notification。

            

    原文作者:kakaback
    原文地址: https://blog.csdn.net/kakaback/article/details/7246253
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞