Notification框架简介

目录

  • Notification介绍
  • Notification框架原理
  • Notification框架服务端启动过程
  • SystemUI进程启动和绑定NotificationManagerService服务端过程
  • Notification调用过程
  • Notification通知提示音响起过程
  • 总结

Notification介绍

功能作用

  1. 显示接收到短息、即时消息等信息 (如QQ、微信、新浪、短信)
  2. 显示客户端的推送消息(如有新版本发布,广告,推荐新闻等)
  3. 显示正在进行的事务(例如:后台运行的程序)(如音乐播放器、版本更新时候的下载进度等)

通知的基本组成

Notification.png
1. 标题
2. 大图标
3. 内容文字 
4. 内容信息
5. 小图标
6. 通知的时间(Timestamp,默认为系统发出通知的时间,也可通过setWhen()来设置)

Notification框架原理

通知栏框架(Notificaiton),它适用于Android系统中交互事件的通知。它主要由三部分组成:系统服务端NotificationManagerService,通知显示端SystemUI,还有创建和更新通知的App端。
NotificationManagerService作为框架的服务端,在系统启动时就会启动并在后台运行,显示端SystemUI作为系统UI进程,也是Notification框架的客户端,在系统启动时也会启动并一直运行。
其它模块需调用Notification时,只需要调用NotificationManager.notify(int,Notification)就可以发出通知。

模块关系图.png

根据Notification框架的原理,我们就分别按以下几点来分析:

1. Notification框架服务端启动过程
2. SystemUI进程启动和绑定NotificationManagerService服务端过程
3. Notification调用过程
4. Notification通知提示音响起过程

Notification框架相关的包

android/frameworks/base/services/java/com/android/server/SystemServer.java      //系统服务类启动的地方
android/frameworks/base/core/java/com/android/server/LocalServices.java         //系统服务类通信的辅助类
android/frameworks/base/core/java/android/service/notification/                 //Notification服务的接口类和监听类
android/frameworks/base/services/core/java/com/android/server/notification/     //NotificationManagerService服务相关的类
android/frameworks/base/core/java/android/app/      //NotificationManager、Notification类和INotificationManager.aidl的包
android/frameworks/base/packages/SystemUI/                                      //Notification显示的UI进程

Notification框架的关系类图

类图.jpg

Notification框架服务端启动过程

SystemServer启动的Notification管理服务类是NotificationManagerService,保存到SystemServiceManager的是NotificationManagerService服务对象中的INotificationManager.Stub(),但是绑定到ServiceManager中Context.NOTIFICATION_ SERVICE的服务类是NotificationManager,所有开发者通过Context.getSystemService(Context.NOTIFICATION_SERVICE)获取回来的服务类不是NotificationManagerServiced服务对象,而是NotificationManager对象,需要再通过NotificationManager对象中的getService()方法,获取SystemServiceManager系统服务管理对象中保存的INotificationManager.Stub()对象。这样NotificationManager就能通过INotificationManager.Stub()对象和NotificationManagerService服务对象进行远程通信了

系统启动时,SystemServiceRegistry类中会把NotificationManager注册为系统服务提供给其它服务或者应用获取系统服务使用,

/android/frameworks/base/core/java/android/app/SystemServiceRegistry.java 

private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
        new HashMap<Class<?>, String>();
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
        new HashMap<String, ServiceFetcher<?>>();

static {
    //...
    registerService(Context.NOTIFICATION_SERVICE, NotificationManager.class,
            new CachedServiceFetcher<NotificationManager>() {
        @Override
        public NotificationManager createService(ContextImpl ctx) {
            final Context outerContext = ctx.getOuterContext();
            return new NotificationManager(
                new ContextThemeWrapper(outerContext,
                        Resources.selectSystemTheme(0,
                                outerContext.getApplicationInfo().targetSdkVersion,
                                com.android.internal.R.style.Theme_Dialog,
                                com.android.internal.R.style.Theme_Holo_Dialog,
                                com.android.internal.R.style.Theme_DeviceDefault_Dialog,
                                com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),
                ctx.mMainThread.getHandler());
        }});
    //...
}

private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);   //把注册的服务保存到列表对象中
}

public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

在Activity或者Service中,可以直接通过Context.getSystemService(Context.NOTIFICATION_SERVICE)就可以获取系统服务使用。ActivityThread.performLaunchActivity()方法中创建ContextImpl对象并通过activity.attach()传递给Activity对象,再通过attachBaseContext()方法赋值给父类ContextWrapper中Context mBase对象,在Activity或者Service中调用getSystemService()方法,最终是调用ContextImpl中的getSystemService()方法。

//android/frameworks/base/core/java/android/app/ContextImpl.java 

@Override
public Object getSystemService(String name) {                   //从注册到系统的服务列表中获取对应的服务
    //...
    Object registrySystemService = SystemServiceRegistry.getSystemService(this, name);
    return registrySystemService;
}

SystemServiceRegistry中注册的NotificationManager对象,其实不是真正的Notification服务,它只是一个调用接口对象,需要通过远程调用来实现和NotificationManagerService服务对象进行通信,真正实现相应的操作。以下是NotificationManagerService服务的启动流程。系统启动时会调用SystemServer来启动相应的系统服务对象。

/android/frameworks/base/services/java/com/android/server/SystemServer.java 

private SystemServiceManager mSystemServiceManager;                                     //系统服务管理类

private void run() {
    //...
    //创建系统服务管理对象
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    //把系统服务管理对象保存到本地服务列表对象中,这样在系统进程中就可以通过LocalServices.getService(Class<T> type)
    //直接返回本地服务列表中的服务对象进行使用
    LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
    //...
}

private void startOtherServices() {
    //...
    INotificationManager notification = null;
    //...
    mSystemServiceManager.startService(NotificationManagerService.class);               //启动NotificationManagerService服务对象
    //获取NotificationManagerService服务对象的桩,用于进行远程调用
    notification = INotificationManager.Stub.asInterface(
            ServiceManager.getService(Context.NOTIFICATION_SERVICE));
    //...
}


/android/frameworks/base/services/core/java/com/android/server/SystemServiceManager.java //系统服务管理类

public <T extends SystemService> T startService(Class<T> serviceClass) {
    final String name = serviceClass.getName();
    final T service;

    Constructor<T> constructor = serviceClass.getConstructor(Context.class);
    service = constructor.newInstance(mContext);    //实例化服务

    // Register it.
    mServices.add(service);     //注册服务

    // Start it.
    service.onStart();          //启动服务

    return service;
}


//系统服务类,NotificationManagerService的父类
/android/frameworks/base/services/core/java/com/android/server/SystemService.java       

public abstract void onStart();     //抽象方法,在子类中实现

protected final void publishBinderService(String name, IBinder service) {
    publishBinderService(name, service, false);
}

protected final void publishBinderService(String name, IBinder service,
        boolean allowIsolated) {
    ServiceManager.addService(name, service, allowIsolated);    
}   

//绑定和发布key为Context.NOTIFICATION_SERVICE的NotificationManager系统服务类
protected final <T> void publishLocalService(Class<T> type, T service) {
    LocalServices.addService(type, service);
}   

//添加到内部系统服务类的集合类LocalServices中
private SystemServiceManager getManager() {
    return LocalServices.getService(SystemServiceManager.class);
}

SystemServer中调用NotificationManagerService的onStart()方法来启动NotificationManagerService服务。

/android/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java 

public class NotificationManagerService extends SystemService {

    @Override
    public void onStart() {

        mListeners = new NotificationListeners();
    
        //绑定和发布key为Context.NOTIFICATION_SERVICE的NotificationManager系统服务类
        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
        //添加到内部系统服务类的集合类LocalServices中
        publishLocalService(NotificationManagerInternal.class, mInternalService);
    }

    //只开放给系统内部调用的API
    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
        @Override
        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
                String tag, int id, Notification notification, int[] idReceived, int userId) {
            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
                    idReceived, userId);
        }

        @Override
        public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
                int userId) {
            checkCallerIsSystem();
            synchronized (mNotificationList) {
                int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
                if (i < 0) {
                    return;
                }
                NotificationRecord r = mNotificationList.get(i);
                StatusBarNotification sbn = r.sbn;
                sbn.getNotification().flags =
                        (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
                mRankingHelper.sort(mNotificationList);
                mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
            }
        }
    };

    //INotificationManager.Stub用于与NotificationManager类和SystemUI进程进行远程通信的桩 (或者其它模块)
    private final IBinder mService = new INotificationManager.Stub() {

        @Override
        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
                Notification notification, int[] idOut, int userId) throws RemoteException {
            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                    Binder.getCallingPid(), tag, id, notification, idOut, userId);  //添加或更新Notification
        }

        @Override
        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
            checkCallerIsSystemOrSameApp(pkg);
            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
                    Binder.getCallingUid() == Process.SYSTEM_UID
                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
                    null);                                                          //删除Notification
        }

        @Override
        public void cancelAllNotifications(String pkg, int userId) {
            checkCallerIsSystemOrSameApp(pkg);
            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
                    REASON_NOMAN_CANCEL_ALL, null);                                 //删除所有Notification
        }

        @Override
        public void registerListener(final INotificationListener listener,
                final ComponentName component, final int userid) {
            enforceSystemOrSystemUI("INotificationManager.registerListener");
            //把NotificationListenerService对象注册到ManagedServices服务管理子类NotificationListeners对象中
            mListeners.registerService(listener, component, userid);
        }

        @Override
        public void unregisterListener(INotificationListener listener, int userid) {
            //把NotificationListenerService对象从ManagedServices服务管理子类NotificationListeners对象中解除
            mListeners.unregisterService(listener, userid);
        }
    };

    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
            final int callingPid, final String tag, final int id, final Notification notification,
            int[] idOut, int incomingUserId) {
        //...
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                synchronized (mNotificationList) {
                    //...
                    //调用服务管理对象mListeners来更新所有注册到mListeners中的NotificationListenerService对象
                    mListeners.notifyPostedLocked(n, oldSbn);
                    //实现播放notification的铃声,使led灯亮起来或者震动等操作。buzz:嗡嗡叫,beep: 嘟嘟响,blink: 闪烁
                    buzzBeepBlinkLocked(r);
                }
            }
        });
        idOut[0] = id;
    }

    //ManagedServices服务管理类,就是用于一类服务的管理对象,例如:如需要管理几个同一类的服务对象NotificationListenerService
    //只需要把相关的NotificationListenerService对象注册到ManagedServices服务管理对象中,需要更新的时候,只需要调用
    //ManagedServices服务管理对象对注册的NotificationListenerService对象进行更新即可
    public class NotificationListeners extends ManagedServices {    

        public NotificationListeners() {
            super(getContext(), mHandler, mNotificationList, mUserProfiles);
        }

        @Override
        protected IInterface asInterface(IBinder binder) {
            return INotificationListener.Stub.asInterface(binder);
        }

        //新添加的方法,调用这个方法,就会更新所有注册进来的NotificationListenerService对象来更新
        //调用服务管理对象mListeners来更新所有注册到mListeners中的NotificationListenerService对象
        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
            StatusBarNotification sbnClone = null;
            StatusBarNotification sbnCloneLight = null;

            for (final ManagedServiceInfo info : mServices) {
                //...
                if (trim == TRIM_LIGHT && sbnCloneLight == null) {
                    sbnCloneLight = sbn.cloneLight();
                } else if (trim == TRIM_FULL) {
                    sbnClone = sbn.clone();
                }
                final StatusBarNotification sbnToPost =
                        (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyPosted(info, sbnToPost, update);  //调用更新通知方法
                    }
                });
            }
        }

        public void notifyRemovedLocked(StatusBarNotification sbn) {
            final StatusBarNotification sbnLight = sbn.cloneLight();
            for (final ManagedServiceInfo info : mServices) {
                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyRemoved(info, sbnLight, update);
                    }
                });
            }
        }
        
        //调用更新通知方法
        private void notifyPosted(final ManagedServiceInfo info,
                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
            final INotificationListener listener = (INotificationListener)info.service;
            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
            try {
                //回调NotificationListenerService对象中的方法onNotificationPosted(),在SystemUI中显示Notification
                listener.onNotificationPosted(sbnHolder, rankingUpdate);
            } catch (RemoteException ex) {
                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
            }
        }

        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
                NotificationRankingUpdate rankingUpdate) {
            if (!info.enabledAndUserMatches(sbn.getUserId())) {
                return;
            }
            final INotificationListener listener = (INotificationListener) info.service;
            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
            try {
                listener.onNotificationRemoved(sbnHolder, rankingUpdate);
            } catch (RemoteException ex) {
                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
            }
        }
        //...
    }
}

//系统服务类,NotificationManagerService的父类
/android/frameworks/base/services/core/java/com/android/server/notification/ManagedServices.java        

abstract protected IInterface asInterface(IBinder binder);

    protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>();

    abstract public class ManagedServices {
    abstract protected void onServiceAdded(ManagedServiceInfo info);
    protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }

    //把Service对象从ManagedServices服务管理类对象中删除
    public void unregisterService(IInterface service, int userid) {
        checkNotNull(service);
        unregisterServiceImpl(service, userid);
    }

    //把Service对象注册到ManagedServices服务管理类对象中
    public void registerService(IInterface service, ComponentName component, int userid) {
        checkNotNull(service);
        ManagedServiceInfo info = registerServiceImpl(service, component, userid);
        if (info != null) {
            onServiceAdded(info);
        }
    }
    
    private ManagedServiceInfo registerServiceImpl(final IInterface service,
            final ComponentName component, final int userid) {
        synchronized (mMutex) {
            try {
                ManagedServiceInfo info = newServiceInfo(service, component, userid,
                        true /*isSystem*/, null, Build.VERSION_CODES.LOLLIPOP);
                service.asBinder().linkToDeath(info, 0);
                mServices.add(info);
                return info;
            } catch (RemoteException e) {
            }
        }
        return null;
    }

    private void unregisterServiceImpl(IInterface service, int userid) {
        ManagedServiceInfo info = removeServiceImpl(service, userid);
        if (info != null && info.connection != null) {
            mContext.unbindService(info.connection);
        }
    }
}

SystemUI进程启动和绑定NotificationManagerService服务端过程:

至此,Notification框架的服务端就已经启动完毕,NotificationManagerService类只是管理Notification的逻辑,显示端是在SystemUI进程中实现的,那么NotificationManagerService服务对象和SystemUI进程间是怎么通信的呢?两个不同进程间通信,很多同学可能就会想到Android的远程过程调用(Remote Procedure Call,RPC)方式来实现,这种猜测是合理的,而且这里也的确是这么实现的。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。所以我们先来看下NotificationManagerService服务和SystemUI进程通信的服务接口文件:

/android/frameworks/base/core/java/android/app/INotificationManager.aidl 

interface INotificationManager
{
    void cancelAllNotifications(String pkg, int userId);

    void enqueueToast(String pkg, ITransientNotification callback, int duration);
    void cancelToast(String pkg, ITransientNotification callback);
    void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
            in Notification notification, inout int[] idReceived, int userId);                          //发出通知的方法
    void cancelNotificationWithTag(String pkg, String tag, int id, int userId);                         //取消通知的方法

    void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);                       //Settings中关闭应用通知
    boolean areNotificationsEnabledForPackage(String pkg, int uid);                                     //判断应用是否可发通知

    void setPackagePriority(String pkg, int uid, int priority);
    int getPackagePriority(String pkg, int uid);

    void setPackagePeekable(String pkg, int uid, boolean peekable);
    boolean getPackagePeekable(String pkg, int uid);

    void setPackageVisibilityOverride(String pkg, int uid, int visibility);
    int getPackageVisibilityOverride(String pkg, int uid);

    // TODO: Remove this when callers have been migrated to the equivalent
    // INotificationListener method.
    StatusBarNotification[] getActiveNotifications(String callingPkg);
    StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);

    //注册INotificationListener服务的方法
    void registerListener(in INotificationListener listener, in ComponentName component, int userid);   
    //解除INotificationListener服务的方法
    void unregisterListener(in INotificationListener listener, int userid);                             

    void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
    void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);

    void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);

    ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
    void requestHintsFromListener(in INotificationListener token, int hints);
    int getHintsFromListener(in INotificationListener token);
    void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
    int getInterruptionFilterFromListener(in INotificationListener token);
    void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
    void setInterruptionFilter(String pkg, int interruptionFilter);

    ComponentName getEffectsSuppressor();
    boolean matchesCallFilter(in Bundle extras);
    boolean matchesMessageFilter(in Bundle extras);
    boolean isSystemConditionProviderEnabled(String path);

    int getZenMode();
    ZenModeConfig getZenModeConfig();
    boolean setZenModeConfig(in ZenModeConfig config, String reason);
    oneway void setZenMode(int mode, in Uri conditionId, String reason);
    oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
    oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
    boolean isNotificationPolicyAccessGranted(String pkg);
    NotificationManager.Policy getNotificationPolicy(String pkg);
    void setNotificationPolicy(String pkg, in NotificationManager.Policy policy);
    String[] getPackagesRequestingNotificationPolicyAccess();
    boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
    void setNotificationPolicyAccessGranted(String pkg, boolean granted);

    byte[] getBackupPayload(int user);
    void applyRestore(in byte[] payload, int user);

    ParceledListSlice getAppActiveNotifications(String callingPkg, int userId);

    //***************************************************************//
    // ++ @noti.sysui [START] NotificationManager APIs for SEC ONLY  //
    //***************************************************************//
    boolean isNotificationInterruptable(String pkg, String opPkg, String tag, int id, 
                in Notification notification, in long time, int userId);
    void cancelSummaryNotificationWithTag(String pkg, String tag, int id, int userId);
    //***************************************************************///
    // -- @noti.sysui [START] NotificationManager APIs for SEC ONLY  //
    //***************************************************************//
        
    //WTL_EDM_START
    void clearAllNotificationsAsUser(int userId);
    //WTL_EDM_END

    void enqueueEdgeNotification(String pkg, String opPkg, int id, in Bundle extras, int userId);   //发侧屏通知的方法
    void removeEdgeNotification(String pkg, int id, in Bundle extras, int userId);                  //删除侧屏通知的方法
}


/android/frameworks/base/core/java/android/service/notification/INotificationListener.aidl 

oneway interface INotificationListener
{
    void onListenerConnected(in NotificationRankingUpdate update);
    void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
            in NotificationRankingUpdate update);
    void onNotificationRemoved(in IStatusBarNotificationHolder notificationHolder,
            in NotificationRankingUpdate update);
    void onNotificationRankingUpdate(in NotificationRankingUpdate update);
    void onListenerHintsChanged(int hints);
    void onInterruptionFilterChanged(int interruptionFilter);

    void onEdgeNotificationPosted(String pkg, int id, in Bundle extra);
    void onEdgeNotificationRemoved(String pkg, int id, in Bundle extra);
}

这个接口类的服务端就是NotificationManagerService服务对象中的INotificationManager.Stub对象mService

private final IBinder mService = new INotificationManager.Stub(){};

客户端可以通过以下方式来获取和服务端通信的桩对象:

IBinder b = ServiceManager.getService(Context.NOTIFICATION_SERVICE);
INotificationManager service = INotificationManager.Stub.asInterface(b);

SystemUI进程在初始化过程中,会创建一个NotificationListenerService服务类,服务对象中创建一个INotificationListener对象并通过远程过程调用把这个INotificationListener对象注册到NotificationManagerService服务对象的服务管理类子类NotificationListeners对象mListeners中

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java 

private final NotificationListenerService mNotificationListener =
        new NotificationListenerService() {
    //...
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
        if (sbn != null) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    String key = sbn.getKey();
                    boolean isUpdate = mNotificationData.get(key) != null;
                    if (isUpdate) {
                        updateNotification(sbn, rankingMap);                    //更新Notification
                    } else {
                        addNotification(sbn, rankingMap, null /* oldEntry */);  //添加Notification
                    }
                }
            });
        }
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
        if (sbn != null) {
            final String key = sbn.getKey();
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    removeNotification(key, rankingMap);                        //删除Notification
                }
            });
        }
    }
    //...
};

public void start() {
    //...
    mNotificationListener.registerAsSystemService(mContext,
            new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
            UserHandle.USER_ALL);   //把NotificationListenerService对象注册为系统服务并通过和NotificationManagerService服务远程通信
    //...
}


/android/frameworks/base/core/java/android/service/notification/NotificationListenerService.java 

public void registerAsSystemService(Context context, ComponentName componentName,
        int currentUser) throws RemoteException {
    mSystemContext = context;
    if (mWrapper == null) {
        mWrapper = new INotificationListenerWrapper();
    }
    INotificationManager noMan = getNotificationInterface();
    //通过远程过程把INotificationListener注册到NotificationManagerService服务对象中,
    //这样NotificationManagerService对象就可以通过INotificationListener通信对象
    //直接回调SystemUI进程中的NotificationListenerService对象来操作显示UI
    noMan.registerListener(mWrapper, componentName, currentUser);
    mCurrentUser = currentUser;
}

private final INotificationManager getNotificationInterface() {
    if (mNoMan == null) {
        mNoMan = INotificationManager.Stub.asInterface(
                ServiceManager.getService(Context.NOTIFICATION_SERVICE));   //这里就是上面客户端可以获取和服务端通信的桩对象的过程
    }
    return mNoMan;
}

public void onNotificationPosted(StatusBarNotification sbn) {
    // optional     //在SystemUI中BaseStatusBar的NotificationListenerService重写了这个方法
}

private class INotificationListenerWrapper extends INotificationListener.Stub {
    @Override
    public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
            NotificationRankingUpdate update) {
        StatusBarNotification sbn;
        try {
            sbn = sbnHolder.get();
        } catch (RemoteException e) {
            return;
        }
        synchronized (mWrapper) {
            applyUpdate(update);
            try {
                if (sbn != null) {
                    NotificationListenerService.this.onNotificationPosted(sbn, mRankingMap);
                } else {
                    NotificationListenerService.this.onNotificationRankingUpdate(mRankingMap);
                }
            } catch (Throwable t) {
                Log.w(TAG, "Error running onNotificationPosted", t);
            }
        }
    }

    @Override
    public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,NotificationRankingUpdate update) {}

    @Override
    public void onListenerConnected(NotificationRankingUpdate update) {}

    @Override
    public void onNotificationRankingUpdate(NotificationRankingUpdate update) throws RemoteException {}

    @Override
    public void onListenerHintsChanged(int hints) throws RemoteException {}

    @Override
    public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {}

    @Override
    public void onEdgeNotificationPosted(String pkg, int id, Bundle extra) {}

    @Override
    public void onEdgeNotificationRemoved(String pkg, int id, Bundle extra) {}
}

Notification调用过程

Notification调用过程可以从应用开始,通过NotificationManager.notify()来发出通知,NotificationManager通过和NotificationManagerService服务对象通信,NotificationManagerService服务对象再利用通过NotificationListeners中监听的服务列表与SystemUI进程启动的系统服务NotificationListenerService中的INotificationListener对象通信,就可以调用SystemUI进程进行显示。

调用过程如下图所示:

时序图3.jpg

对应的代码,如以下所示:

应用程序要发通知或取消通知,只需要获取系统的通知管理服务,调用notify或者cancel来操作通知即可。

NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);   //获取通知管理服务
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.drawable.smallicon)
        .setContentTitle("This is Title")
        .setContentText("This is Content");
Notification n = b.build();                                                                     //创建Notification
nm.notify(ID, n);                                                                               //发通知

NotificationManager是管理通知的服务类,它负责与NotificationManagerService服务对象通信,并通过调用NotificationManagerService服务对象添加、更新、删除通知等等,所支持的功能可以参照远程通信服务接口INotificationManager.aidl中公开的方法。

/android/frameworks/base/core/java/android/app/NotificationManager.java

private static INotificationManager sService;

static public INotificationManager getService()
{
    if (sService != null) {
        return sService;
    }
    IBinder b = ServiceManager.getService("notification");  //获取系统服务的桩对象
    sService = INotificationManager.Stub.asInterface(b);    //把桩对象转化成远程通信对象
    return sService;
}

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();
    //...
    Notification stripped = notification.clone();
    Builder.stripForDelivery(stripped);
    try {
        //调用远程服务对象的enqueueNotificationWithTag()方法来调用NotificationManagerService对象发出通知
        service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                stripped, idOut, UserHandle.myUserId());
    } catch (RemoteException e) {
    }
}

NotificationManagerService服务对象只是处理逻辑,显示的逻辑是放在系统UI的进程SystemUI中去的,所以NotificationManagerService服务对象处理完逻辑后,还需要远程调用SystemUI进程去更新显示。所以SystemUI进程需要把INotificationListener服务对象注册到NotificationManagerService服务对象中来,当需要更新UI是,就可以通过INotificationListener服务对象回调SystemUI进程中的方法来更新。

/android/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java 

    @Override
public void onStart() {
    mListeners = new NotificationListeners();
}

private final IBinder mService = new INotificationManager.Stub() {
    @Override
    public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
            Notification notification, int[] idOut, int userId) throws RemoteException {
        enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                Binder.getCallingPid(), tag, id, notification, idOut, userId);  //添加或更新Notification
    }
}

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
        final int callingPid, final String tag, final int id, final Notification notification,
        int[] idOut, int incomingUserId) {
    //...
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            synchronized (mNotificationList) {
                //...
                //调用服务管理对象mListeners来更新所有注册到mListeners中的NotificationListenerService对象
                mListeners.notifyPostedLocked(n, oldSbn);
                //实现播放notification的铃声,使led灯亮起来或者震动等操作。buzz:嗡嗡叫,beep: 嘟嘟响,blink: 闪烁
                buzzBeepBlinkLocked(r);
            }
        }
    });
    idOut[0] = id;
}

public class NotificationListeners extends ManagedServices {    

    public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
        for (final ManagedServiceInfo info : mServices) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    notifyPosted(info, sbnToPost, update);  //调用更新通知方法
                }
            });
        }
    }

    private void notifyPosted(final ManagedServiceInfo info,
            final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
        final INotificationListener listener = (INotificationListener)info.service;
        StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
        try {
            //回调NotificationListenerService对象中的方法onNotificationPosted(),在SystemUI中显示Notification
            listener.onNotificationPosted(sbnHolder, rankingUpdate);
        } catch (RemoteException ex) {
            Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
        }
    }

INotificationListener服务对象是从BaseStatusBar对象中启动的系统服务BNotificationListenerService注册到NotificationManagerService对象中的,当NotificationManagerService服务对象通过INotificationListener服务对象回调SystemUI进程中的方法时,就可以调用BaseStatusBar对象中的方法来更新UI显示了。

/android/frameworks/base/core/java/android/service/notification/NotificationListenerService.java    

private class INotificationListenerWrapper extends INotificationListener.Stub {
    @Override
    public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
            NotificationRankingUpdate update) {
        StatusBarNotification sbn;
        try {
            sbn = sbnHolder.get();
        } catch (RemoteException e) {
            return;
        }
        synchronized (mWrapper) {
            applyUpdate(update);
            try {
                if (sbn != null) {
                    NotificationListenerService.this.onNotificationPosted(sbn, mRankingMap);
                } else {
                    NotificationListenerService.this.onNotificationRankingUpdate(mRankingMap);
                }
            } catch (Throwable t) {
                Log.w(TAG, "Error running onNotificationPosted", t);
            }
        }
    }
}

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java 

private final NotificationListenerService mNotificationListener =
        new NotificationListenerService() {
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (sbn != null) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    String key = sbn.getKey();
                    boolean isUpdate = mNotificationData.get(key) != null;
                    if (isUpdate) {
                        updateNotification(sbn, rankingMap);                    //更新Notification,
                    } else {
                        addNotification(sbn, rankingMap, null /* oldEntry */);  //添加Notification
                    }
                }
            });
        }
    }
};

PhoneStatusBar是BaseStatusBar的子类,实现了BaseStatusBar中的相关方法,addNotification()就是其中之一,这个方法是用来添加Notification和状态栏通知图标的。

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java 

public void addNotification(StatusBarNotification notification, RankingMap ranking,
        Entry oldEntry) {
    //...
    Entry shadeEntry = createNotificationViews(notification);   //创建状态栏通知图标和通知列表行布局
    //...
    //把创建的状态栏通知图标和通知列表行布局分别添加到状态栏通知栏和通知列表NotificatioStackScrollLayout中
    addNotificationViews(shadeEntry, ranking);
    setAreThereNotifications();
    //...
}

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {   //创建状态栏通知图标和通知列表行布局
    final StatusBarIconView iconView = createIcon(sbn);         //创建状态栏通知图标
    if (iconView == null) {
        return null;
    }

    NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
    if (!inflateViews(entry, mStackScroller)) {                 //创建展开通知布局列表中的通知一行的布局
        handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
        return null;
    }
    return entry;
}

protected StatusBarIconView createIcon(StatusBarNotification sbn) {                 //创建状态栏通知图标
    Notification n = sbn.getNotification();
    final StatusBarIconView iconView = new StatusBarIconView(mContext,
            sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);    //创建状态栏通知图标布局View
    iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);

    final Icon smallIcon = n.getSmallIcon();
    if (smallIcon == null) {
        handleNotificationError(sbn,
                "No small icon in notification from " + sbn.getPackageName());
        return null;
    }
    final StatusBarIcon ic = new StatusBarIcon(
            sbn.getUser(),
            sbn.getPackageName(),
            smallIcon,
            n.iconLevel,
            n.number,
            n.tickerText);
    if (!iconView.set(ic)) {                                    //把StatusBarIcon传到状态栏通知图标布局View中
        handleNotificationError(sbn, "Couldn't create icon: " + ic);
        return null;
    }
    return iconView;
}

protected boolean inflateViews(Entry entry, ViewGroup parent) { //创建展开通知布局列表中的通知一行的布局
    final StatusBarNotification sbn = entry.notification;

    RemoteViews contentView = sbn.getNotification().contentView;              //通知布局的contentView布局
    RemoteViews bigContentView = sbn.getNotification().bigContentView;        //通知布局的bigContentView布局
    RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;//通知布局的headsUpContentView布局

    Notification publicNotification = sbn.getNotification().publicVersion;

    ExpandableNotificationRow row;                              //创建通知布局列表中的一行的布局
    if (entry.row != null) {
    } else {
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
                parent, false);
        row.setExpansionLogger(this, entry.notification.getKey());
        row.setGroupManager(mGroupManager);
    }
  
    NotificationContentView contentContainer = row.getPrivateLayout();
    NotificationContentView contentContainerPublic = row.getPublicLayout();
    NotificationContentView expandedKnox = row.getKnoxLayout();

    mNotificationClicker.register(row, sbn);

    View contentViewLocal = null;
    View bigContentViewLocal = null;
    View headsUpContentViewLocal = null;
    try {
        contentViewLocal = contentView.apply(
                sbn.getPackageContext(mContext),
                contentContainer,
                mOnClickHandler);                               //把contentView添加到通知布局列表中通知行容器中
        if (bigContentView != null) {
            bigContentViewLocal = bigContentView.apply(
                    sbn.getPackageContext(mContext),
                    contentContainer,
                    mOnClickHandler);                           //把bigContentView添加到通知布局列表中通知行容器中
        }
        if (headsUpContentView != null) {
            headsUpContentViewLocal = headsUpContentView.apply(
                    sbn.getPackageContext(mContext),
                    contentContainer,
                    mOnClickHandler);                           //把headsUpContentView添加到通知布局列表中通知行容器中
        }
    } catch (RuntimeException e) {
        return false;
    }

    View publicViewLocal = null;
    if (publicNotification != null) {
        try {
            publicViewLocal = publicNotification.contentView.apply(
                    sbn.getPackageContext(mContext),
                    contentContainerPublic, mOnClickHandler);
        } catch (RuntimeException e) {
            publicViewLocal = null;
        }
    }
    //...
    return true;
}

//把创建的状态栏通知图标和通知列表行布局分别添加到状态栏通知栏和通知列表NotificatioStackScrollLayout中
protected void addNotificationViews(Entry entry, RankingMap ranking) {
    if (entry == null) {
        return;
    }
    mNotificationData.add(entry, ranking);  //先把通知添加到NotificationData中去
    updateNotifications();//根据更新后的NotificationData数据更新状态栏通知图标和通知列表NotificationStackScrollLayout布局
}

@Override
protected void updateNotifications() {
    mNotificationData.filterAndSort();      //过滤和排序通知的顺序

    updateNotificationShade();              //添加或者更新NotificationStackScrollLayout中的Notification
    mIconController.updateNotificationIcons(mNotificationData); //添加或者更新状态栏左上角通知栏图标

    mNotificationPanel.updateCarrierAndClearLayout();           //更新通信类型和清除布局
}

private void updateNotificationShade() {
    ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
    ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
    
    //把所有需要显示的Notification添加到toShow列表中
    final int N = activeNotifications.size();
    for (int i=0; i<N; i++) {
        Entry ent = activeNotifications.get(i);
        toShow.add(ent.row);
    }

    //通过判断mStackScroller中各个child是否在toShow列表;不在的话,就添加toRemove列表中,待会一起删除
    ArrayList<View> toRemove = new ArrayList<>();
    for (int i=0; i< mStackScroller.getChildCount(); i++) {
        View child = mStackScroller.getChildAt(i);
        if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {//child是否在toShow列表
            toRemove.add(child);
        }
    }

    //把toRemove列表中需要删除的child,都删除掉
    for (View remove : toRemove) {
        mStackScroller.removeView(remove);
    }
    
    //判断toShow列表中,有哪些是新添加的通知(可以通过通知View是否有父容器来判断),新的通知就添加到mStackScroller中
    for (int i=0; i<toShow.size(); i++) {
        View v = toShow.get(i);
        if (v.getParent() == null) {    //这里通过通知View是否有父容器来判断这条通知是否是新的
            mStackScroller.addView(v);  //新通知就添加到mStackScroller中
        }
    }

    //以上已经把需要显示或者删除的通知都处理完了,但是还需要重新调整顺序
    //从mStackScroller中顺序判断每个child的顺序是否与toShow列表中的顺序一样
    //不一样的,就把顺序调整下
    int j = 0;
    for (int i = 0; i < mStackScroller.getChildCount(); i++) {
        View child = mStackScroller.getChildAt(i);
        ExpandableNotificationRow targetChild = toShow.get(j);
        if (child != targetChild) {     //顺序不对
            mStackScroller.changeViewPosition(targetChild, i);  //调整顺序
        }
        j++;
    }

    updateNotificationShadeForChildren();   //更新每个Group Notification中的child Notification
    updateRowStates();                      //更新Notification布局中的Item展开情况、dim情况和锁屏状态下的通知布局情况,很重要的一个方法
    updateClearAll();                       //更新清楚所有按钮布局
    updateEmptyShadeView();                 //隐藏"No Notification"
    updateQsExpansionEnabled();             //关闭QS功能
    mShadeUpdates.check();
}


/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java 

public void filterAndSort() {
    mSortedAndFiltered.clear();         //清除mSortedAndFiltered列表

    final int N = mEntries.size();
    for (int i = 0; i < N; i++) {
        Entry entry = mEntries.valueAt(i);
        StatusBarNotification sbn = entry.notification;

        if (shouldFilterOut(sbn)) {     //判断是否需要过滤掉
            continue;
        }

        mSortedAndFiltered.add(entry);  //添加到mSortedAndFiltered列表中
    }

    Collections.sort(mSortedAndFiltered, mRankingComparator);   //重新排序mSortedAndFiltered中的选项
}

StatusBarIconController是状态栏图标控制的类,用来控制状态栏通知图标显示和系统图标显示等等。

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java 

private IconMerger mNotificationIcons;      //状态栏通知图标容器对象

public StatusBarIconController(Context context, View statusBar, View keyguardStatusBar,
        PhoneStatusBar phoneStatusBar) {
    //...
    mNotificationIcons = (IconMerger) statusBar.findViewById(R.id.notificationIcons);   //获取状态栏图标的容器类
    //...
}

public void updateNotificationIcons(NotificationData notificationData) {
    int iconSize = mContext.getResources().getDimensionPixelSize(R.dimen.notification_icon_view_width); //图标宽度
    final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
            iconSize + 2*mIconHPadding, mPhoneStatusBar.getStatusBarHeight());  //状态栏图标的布局参数(宽度x高度)

    ArrayList<NotificationData.Entry> activeNotifications =
            notificationData.getActiveNotifications();
    final int N = activeNotifications.size();
    ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);

    //把所有需要显示的Notification添加到toShow列表中
    for (int i = 0; i < N; i++) {
        NotificationData.Entry ent = activeNotifications.get(i);
        //过滤环境通知,例如:插USB或者充电线时的通知,是不需要显示状态栏中的图标的
        if (notificationData.isAmbient(ent.key)
                && !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
            continue;
        }
        if (!PhoneStatusBar.isTopLevelChild(ent)) {                         //过滤分组的组图标
            continue;
        }
        if ((ent.notification.getNotification().secFlags
                & Notification.SEC_FLAG_HIDE_NOTIFICATION_ICON) !=0) {      //过滤掉设置了隐藏状态栏图标的通知
            continue;
        }
        if(!mPhoneStatusBar.shouldShowOnIndicator(ent.notification.getKey())) { //过滤设置了隐藏图标的包的通知
            continue;
        }
        toShow.add(ent.icon);
    }

    //通过判断mNotificationIcons中各个child是否在toShow列表;不在的话,就添加toRemove列表中,待会一起删除
    ArrayList<View> toRemove = new ArrayList<>();
    for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
        View child = mNotificationIcons.getChildAt(i);
        if (!toShow.contains(child)) {                                      //child是否在toShow列表
            toRemove.add(child);
        }
    }

    //把toRemove列表中需要删除的child,都删除掉
    final int toRemoveCount = toRemove.size();
    for (int i = 0; i < toRemoveCount; i++) {
        mNotificationIcons.removeView(toRemove.get(i));
    }

    //判断toShow列表中,有哪些是新添加的通知(可以通过通知View是否有父容器来判断),新的通知就添加到mNotificationIcons中
    for (int i=0; i<toShow.size(); i++) {
        View v = toShow.get(i);
        if (v.getParent() == null) {                            //这里通过通知View是否有父容器来判断这条通知是否是新的
            mNotificationIcons.addView(v, i, params);           //新通知就添加到mStackScroller中
        }
    }

    //以上已经把需要显示或者删除的通知都处理完了,但是还需要重新调整顺序
    //从mNotificationIcons中顺序判断每个child的顺序是否与toShow列表中的顺序一样
    //不一样的,就把顺序调整下
    final int childCount = mNotificationIcons.getChildCount();
    for (int i = 0; i < childCount; i++) {
        View actual = mNotificationIcons.getChildAt(i);
        StatusBarIconView expected = toShow.get(i);
        if (actual == expected) {   //顺序正确的就不处理
            continue;
        }
        mNotificationIcons.removeView(expected);    //把顺序错误的View先删除
        mNotificationIcons.addView(expected, i);    //再添加到正确的顺序位置上
    }

    applyNotificationIconsTint();                   //更新状态栏图标的颜色
}


/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java 

public class IconMerger extends LinearLayout{       //状态栏通知图标容器类

}

把状态栏通知图标和通知行布局都分别添加到相应的容器类后,显示部分的逻辑也就完成了。

Notification通知提示音响起过程

SystemUI的RingtonePlayer服务通过IAudioService.setRingtonePlayer(IRingtonePlayer)把IRingtonePlayer实现的回调接口对象注册到AudioService服务中,第三方App调用NotificationManager.notify()时,会调用NotificationManagerService中的enqueueNotificationInternal方法中的buzzBeepBlinkLocked()方法,这个方法会通过IAudioService.getRingtonePlayer()获取AudioServoce中的IRingtonePlayer对象,并调用回调方法来调用SystenUI.RingtonePlayer.mCallback的playAsync()来实现播放notification铃声、震动、闪烁灯功能。

调用过程如下图所示:

时序图2.jpg

对应的代码,如以下所示:

启动AudioService服务过程,

/android/frameworks/base/services/java/com/android/server/SystemServer.java 

private void startOtherServices() {
    AudioService audioService = null;
    audioService = new AudioService(context);
    ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
}

/android/frameworks/base/services/core/java/com/android/server/audio/AudioService.java 

public class AudioService extends IAudioService.Stub {

    private volatile IRingtonePlayer mRingtonePlayer;

    @Override
    public void setRingtonePlayer(IRingtonePlayer player) {
        mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
        mRingtonePlayer = player;
    }

    @Override
    public IRingtonePlayer getRingtonePlayer() {
        return mRingtonePlayer;
    }
}

启动SystemUI的RingtonePlayer服务过程,

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java 

@Override
public void onCreate() {
    super.onCreate();
    ((SystemUIApplication) getApplication()).startServicesIfNeeded();
}


/android/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java 

private final Class<?>[] SERVICES = new Class[] {
        com.android.systemui.tuner.TunerService.class,
        com.android.systemui.keyguard.KeyguardViewMediator.class,
        com.android.systemui.recents.Recents.class,
        com.android.systemui.volume.VolumeUI.class,
        com.android.systemui.statusbar.SystemBars.class,
        com.android.systemui.usb.StorageNotification.class,
        com.android.systemui.power.PowerUI.class,
        com.android.systemui.media.RingtonePlayer.class,        //RingtinePlayer服务
        com.android.systemui.keyboard.KeyboardUI.class,
};

public void startServicesIfNeeded() {
    //...
    final int N = SERVICES.length;
    for (int i=0; i<N; i++) {
        Class<?> cl = SERVICES[i];
        mServices[i] = (SystemUI)cl.newInstance();
        mServices[i].mContext = this;
        mServices[i].mComponents = mComponents;
        mServices[i].start();                                   //启动RingtinePlayer服务
        if (mBootCompleted) {
            mServices[i].onBootCompleted();
        }
    }
    //...
}

RingtonePlayer是运行在SystemUI进程的服务,RingtonePlayer服务会获取AudioService服务对象,并把IRingtonePlayer对象传给AudioService服务对象中去,其它模块通过AudioService.getRingtonePlayer()来控制RingtonePlayer服务播放提示音的功能。

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java 

//相关的远程过程调用接口:
/android/frameworks/base/media/java/android/media/IRingtonePlayer.aidl
/android/frameworks/base/media/java/android/media/IAudioService.aidl

private IAudioService mAudioService;

private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();

@Override
public void start() {
    mAsyncPlayer.setUsesWakeLock(mContext);

    mAudioService = IAudioService.Stub.asInterface(
            ServiceManager.getService(Context.AUDIO_SERVICE));  //获取AudioService服务的远程对象

    mAudioService.setRingtonePlayer(mCallback); //把IRingtonePlayer传到AudioService服务对象中去
}

//每个Client代表一个播放的任务
private class Client implements IBinder.DeathRecipient {
    private final IBinder mToken;
    private final Ringtone mRingtone;

    public Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa) {
        mToken = token;

        mRingtone = new Ringtone(getContextForUser(user), false);   //创建一个Ringtone对象
        mRingtone.setAudioAttributes(aa);
        mRingtone.setUri(uri);
    }

    @Override
    public void binderDied() {
        if (LOGD) Log.d(TAG, "binderDied() token=" + mToken);
        synchronized (mClients) {
            mClients.remove(mToken);
        }
        mRingtone.stop();
    }
}

private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
    @Override
    public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping)
            throws RemoteException {
        Client client;
        synchronized (mClients) {
            client = mClients.get(token);
            if (client == null) {
                final UserHandle user = Binder.getCallingUserHandle();
                client = new Client(token, uri, user, aa);
                token.linkToDeath(client, 0);
                mClients.put(token, client);
            }
        }
        client.mRingtone.setLooping(looping);
        client.mRingtone.setVolume(volume);
        client.mRingtone.play();                                            //同步播放提示音
    }

    @Override
    public void stop(IBinder token) {
        Client client;
        synchronized (mClients) {
            client = mClients.remove(token);
        }
        if (client != null) {
            client.mToken.unlinkToDeath(client, 0);
            client.mRingtone.stop();
        }
    }

    @Override
    public boolean isPlaying(IBinder token) {
        Client client;
        synchronized (mClients) {
            client = mClients.get(token);
        }
        if (client != null) {
            return client.mRingtone.isPlaying();
        } else {
            return false;
        }
    }

    @Override
    public void setPlaybackProperties(IBinder token, float volume, boolean looping) {
        Client client;
        synchronized (mClients) {
            client = mClients.get(token);
        }
        if (client != null) {
            client.mRingtone.setVolume(volume);
            client.mRingtone.setLooping(looping);
        }
    }

    @Override
    public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Async playback only available from system UID.");
        }
        mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);   //把播放任务放到异步队列中
    }

    @Override
    public void stopAsync() {
        if (LOGD) Log.d(TAG, "stopAsync()");
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Async playback only available from system UID.");
        }
        mAsyncPlayer.stop();
    }

    @Override
    public String getTitle(Uri uri) {
        final UserHandle user = Binder.getCallingUserHandle();
        return Ringtone.getTitle(getContextForUser(user), uri,
                false /*followSettingsUri*/, false /*allowRemote*/);
    }

    @Override
    public IBinder setOnCompletionListener(INotificationPlayerOnCompletionListener l) {
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException(
                    "setOnCompletionListener only available from system UID.");
        }
        return mAsyncPlayer.setOnCompletionListener(l);
    }
};

如果需要调用异步方式来播放提示音,就需要用到NotificationPlayer这个类,它会把播放任务保存到队列中,通过线程一个一个为队列中每个提示音播放任务创建一个播放线程并执行。

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java 

private static final class Command {    //一个异步任务对应一个Command对象
    int code;
    Context context;
    Uri uri;
    boolean looping;
    AudioAttributes attributes;
    long requestTime;
}

private LinkedList<Command> mCmdQueue = new LinkedList();       //异步任务队列对象

public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
    Command cmd = new Command();
    cmd.requestTime = SystemClock.uptimeMillis();
    cmd.code = PLAY;
    cmd.context = context;
    cmd.uri = uri;
    cmd.looping = looping;
    cmd.attributes = attributes;
    synchronized (mCmdQueue) {
        enqueueLocked(cmd);             //把异步任务加入队列中
        mState = PLAY;
    }
}

private void enqueueLocked(Command cmd) {
    mCmdQueue.add(cmd);                 //把异步任务加入队列对象mCmdQueue中
    if (mThread == null) {              //如果执行任务线程已经停止,创建线程并开始执行
        acquireWakeLock();
        mThread = new CmdThread();      //创建执行任务的线程
        mThread.start();                //启动线程
    }
}

private final class CmdThread extends java.lang.Thread {
    CmdThread() {
        super("NotificationPlayer-" + mTag);
   }

    public void run() {
        while (true) {
            Command cmd = null;

            synchronized (mCmdQueue) {
                cmd = mCmdQueue.removeFirst();  //取出队列中第一个任务
            }

            switch (cmd.code) {                 //任务类型
            case PLAY:                          //播放提示音任务
                startSound(cmd);                //播放提示音
                break;
            case STOP:                          //停止提示音任务
                if (mPlayer != null) {
                    long delay = SystemClock.uptimeMillis() - cmd.requestTime;
                    if (delay > 1000) {         //如果异步时间超过1s,打印出来,方便调试
                        Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
                    }
                    mPlayer.stop();
                    mPlayer.release();
                    mPlayer = null;
                    synchronized(mQueueAudioFocusLock) {
                        if (mAudioManagerWithAudioFocus != null) {
                            mAudioManagerWithAudioFocus.abandonAudioFocus(null);
                            mAudioManagerWithAudioFocus = null;
                        }
                    }
                    if((mLooper != null)
                            && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
                        mLooper.quit();
                    }
                } else {
                    Log.w(mTag, "STOP command without a player");
                }
                break;
            }

            synchronized (mCmdQueue) {
                if (mCmdQueue.size() == 0) {
                    mThread = null;
                    releaseWakeLock();
                    return;
                }
            }
        }
    }
}

private void startSound(Command cmd) {
    try {
        synchronized(mCompletionHandlingLock) {
            if((mLooper != null)
                    && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
                mLooper.quit();
            }
            //为每个播放任务创建一个播放提示音线程
            mCompletionThread = new CreationAndCompletionThread(cmd);
            synchronized(mCompletionThread) {
                mCompletionThread.start();  //开始执行线程
                mCompletionThread.wait();   //等待线程执行完
            }
        }
        long delay = SystemClock.uptimeMillis() - cmd.requestTime;
        if (delay > 1000) {                 //如果异步时间超过1s,打印出来,方便调试
            Log.w(mTag, "Notification sound delayed by " + delay + "msecs");
        }
    }
    catch (Exception e) {
        Log.w(mTag, "error loading sound for " + cmd.uri, e);
    }
}

private final class CreationAndCompletionThread extends Thread {
    public Command mCmd;
    public CreationAndCompletionThread(Command cmd) {
        super();
        mCmd = cmd;
    }
    public void run() {
        Looper.prepare();
        mLooper = Looper.myLooper();
        synchronized(this) {
            AudioManager audioManager =
                (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
            try {
                MediaPlayer player = new MediaPlayer();         //创建一个MediaPlayer对象
                player.setAudioAttributes(mCmd.attributes);
                player.setDataSource(mCmd.context, mCmd.uri);
                player.setLooping(mCmd.looping);
                player.prepare();
                if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
                        && (mCmd.uri.getEncodedPath().length() > 0)) {
                    if (!audioManager.isMusicActiveRemotely()) {
                        synchronized(mQueueAudioFocusLock) {
                            if (mAudioManagerWithAudioFocus == null) {
                                if (mDebug) Log.d(mTag, "requesting AudioFocus");
                                if (mCmd.looping) {             //获取长时间音频焦点
                                    audioManager.requestAudioFocus(null,
                                            AudioAttributes.toLegacyStreamType(mCmd.attributes),
                                            AudioManager.AUDIOFOCUS_GAIN);  
                                } else {                        //获取临时音频焦点
                                    audioManager.requestAudioFocus(null,
                                            AudioAttributes.toLegacyStreamType(mCmd.attributes),
                                            AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
                                }
                                mAudioManagerWithAudioFocus = audioManager;
                            } else {
                                if (mDebug) Log.d(mTag, "AudioFocus was previously requested");
                            }
                        }
                    }
                }
                player.setOnCompletionListener(NotificationPlayer.this);
                player.setOnErrorListener(NotificationPlayer.this);
                player.start();                             //开始播放提示音
                if (mPlayer != null) {
                    mPlayer.release();
                }
                mPlayer = player;
            }
            catch (Exception e) {
                Log.w(mTag, "error loading sound for " + mCmd.uri, e);
            }
            this.notify();
        }
        Looper.loop();
    }
};

应用或者服务通过NotificationManager调用notify()发出通过,NotificationManager通过和NotificationManagerService服务通信,调用发出通知,并调用buzzBeepBlinkLocked()方法来触发通知提示音、震动或者led闪烁。

/android/frameworks/base/core/java/android/app/NotificationManager.java

public void notify(int id, Notification notification)
{
    notify(null, id, notification);
}

public void notify(String tag, int id, Notification notification)
{
    INotificationManager service = getService();
    service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
            stripped, idOut, UserHandle.myUserId());
}


/android/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java 

private final IBinder mService = new INotificationManager.Stub() {
    @Override
    public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
            Notification notification, int[] idOut, int userId) throws RemoteException {

        enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                Binder.getCallingPid(), tag, id, notification, idOut, userId);
    }
}

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
        final int callingPid, final String tag, final int id, final Notification notification,
        int[] idOut, int incomingUserId) {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            synchronized (mNotificationList) {
                final StatusBarNotification n = new StatusBarNotification(
                        pkg, opPkg, id, tag, callingUid, callingPid, score, notification, user);
                NotificationRecord r = new NotificationRecord(n, score);
                NotificationRecord old = mNotificationsByKey.get(n.getKey());
                //调用服务管理对象mListeners来更新所有注册到mListeners中的NotificationListenerService对象
                mListeners.notifyPostedLocked(n, oldSbn);
                //实现播放notification的提示音,使led灯亮起来或者震动等操作。buzz:嗡嗡叫,beep: 嘟嘟响,blink: 闪烁
                buzzBeepBlinkLocked(r);
            }
        }
    });
}

//实现播放notification的提示音,使led灯亮起来或者震动等操作。buzz:嗡嗡叫,beep: 嘟嘟响,blink: 闪烁
private void buzzBeepBlinkLocked(NotificationRecord record) {
    boolean buzz = false;   //震动
    boolean beep = false;   //提示音
    boolean blink = false;  //闪烁
    final Notification notification = record.sbn.getNotification();
    // Should this notification make noise, vibe, or use the LED?
    final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
    final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
    // If we're not supposed to beep, vibrate, etc. then don't.
    final String disableEffects = disableNotificationEffects(record);
    if (disableEffects != null) {
        ZenLog.traceDisableEffects(record, disableEffects);
    }
    boolean smsRingtone = getContext().getResources().getBoolean(
            com.android.internal.R.bool.config_sms_ringtone_incall);
    if ((disableEffects == null || (smsRingtone && mInCall))    //通话期间来短信铃声
            && (!(record.isUpdate
                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
            && (record.getUserId() == UserHandle.USER_ALL ||
                record.getUserId() == currentUser ||
                mUserProfiles.isCurrentProfile(record.getUserId()))
            && canInterrupt
            && mSystemReady
            && mAudioManager != null) {     //判断是否需要提示音或者震动

        //这里会发出一个Notification的AccessibilityEvent,这样在辅助服务中才能收到这个事件,
        //微信抢红包的功能就是通过这个辅助事件才得以实现的
        sendAccessibilityEvent(notification, record.sbn.getPackageName());

        //提示音相关
        if(!isPrayModeNotiOn(mContext)) {       //判断是否是在祈祷模式下
            //判断Notification是否设置了使用默认提示音或者Notification设置的提示音文件刚好是默认提示音文件
            final boolean useDefaultSound =
                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
                           Settings.System.DEFAULT_NOTIFICATION_URI
                                   .equals(notification.sound);
            Uri soundUri = null;
            boolean hasValidSound = false;
            if (useDefaultSound) {  //判断是否使用默认Notification提示音
                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;    //获取默认提示音Uri
                ContentResolver resolver = getContext().getContentResolver();
                hasValidSound = Settings.System.getString(resolver,
                       Settings.System.NOTIFICATION_SOUND) != null;     //默认提示音文件是否有效
            } else if (notification.sound != null) {
                soundUri = notification.sound;                          //获取Notification的提示音Uri
                hasValidSound = (soundUri != null);                     //提示音文件是否有效
            }

            if (hasValidSound) {    //判断是否是有效提示音
                boolean looping =
                        (notification.flags & Notification.FLAG_INSISTENT) != 0;  //是否设置了循环
                AudioAttributes audioAttributes;
                if (notification.audioAttributes != null) {
                    audioAttributes = notification.audioAttributes;
                } else {
                    audioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
                }
                mSoundNotificationKey = record.getKey();
                //Notification提示音音量是否为0和音频焦点是否可用
                if ((mAudioManager.getStreamVolume(
                        AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
                            && !mAudioManager.isAudioFocusExclusive()) {
                    final long identity = Binder.clearCallingIdentity();
                    try {
                        //-----这里或通过AudioService来获取IRingtonePlayer对象,最终会调用SystemUI进程中的RingtonePlayer来播放提示音
                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();   
                        if (player != null) {
                            player.playAsync(soundUri, record.sbn.getUser(), looping,
                                    audioAttributes);                                       //异步方式播放提示音
                            beep = true;
                            mIsPlaying = true;

                                if (mMethodRingtonePlayer != null) {                        //翻转停止播放提示音功能的相关逻辑
                                if (mOverTurnPlayer != null && mOverTurnPlayer.isEnable() && !mOverTurnPlayer.isRegister()) {
                                    mOverTurnPlayer.register();
                                    //...                   //翻转停止播放提示音功能的相关逻辑,这里先不赘述
                                }
                            }
                        }
                    } catch (RemoteException e) {
                    } finally {
                        Binder.restoreCallingIdentity(identity);
                    }
                }
            }

            //震动相关
            final boolean hasCustomVibrate = notification.vibrate != null;              //Notification是否设置震动
            final boolean convertSoundToVibration =
                       !hasCustomVibrate
                    && hasValidSound
                    && (mAudioManager.getRingerMode()
                               == AudioManager.RINGER_MODE_VIBRATE);                    //震动模式下,需要把通知提示音变成震动

            final boolean useDefaultVibrate =
                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;        //Notification是否设置了默认震动

            final boolean useHaptic = doesItUseHaptic(notification.haptic);             //Notification是否设置了触屏反馈

            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate ||useHaptic)
                    && !(mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {   //判断是否需要震动
                mVibrateNotificationKey = record.getKey();
                buzz = true;    
                doVibrate((useDefaultVibrate || convertSoundToVibration), useHaptic, notification); //执行震动
            }
        }

        if(beep || buzz) {
            AccessibilityManager accManager = AccessibilityManager.getInstance(getContext());
            accManager.onFlashNotification(record.getNotification().category);
        }
    }

    //led灯相关
    boolean wasShowLights = mLights.remove(record.getKey());
    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
        mLights.add(record.getKey());
        updateLightsLocked();       //更新led灯闪烁
        if (mUseAttentionLight) {
            mAttentionLight.pulse();
        }
        blink = true;
    } else if (wasShowLights) {
        updateLightsLocked();       //更新led灯闪烁
    }

    if ((buzz || beep || blink) && !isPrayModeNotiOn(mContext)) {
        EventLogTags.writeNotificationAlert(record.getKey(),
                buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
        mHandler.post(mBuzzBeepBlinked);
    }
}

private void doVibrate(boolean useDefaultVibrate, boolean useHaptic, Notification n) {
    if (useHaptic) {
        mVibrator.vibrate(n.haptic, -1, null,
               Vibrator.MagnitudeTypes.NotificationMagnitude);          //执行震动
    } else if (useDefaultVibrate) {
        long identity = Binder.clearCallingIdentity();
        try {
            mVibrator.vibrate(HapticFeedbackConstants.VIBE_NOTIFICATION, -1, null,
                    Vibrator.MagnitudeTypes.NotificationMagnitude);     //执行震动
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    } else {
        long identity2 = Binder.clearCallingIdentity();
        try {
            mVibrator.vibrate(n.vibrate, ((n.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1, null,
                    Vibrator.MagnitudeTypes.NotificationMagnitude);     //执行震动
        } finally {
            Binder.restoreCallingIdentity(identity2);
        }
    }
}

NotificationManagerService服务中就是通过AudioService服务获取IRingtonePlayer对象来控制SystemUI进程进行播放提示音的,

final IRingtonePlayer player = mAudioManager.getRingtonePlayer();   
player.playAsync(soundUri, record.sbn.getUser(), looping, audioAttributes); 

SystemUI进程播放提示音的流程如下:

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java   

private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);

private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
    @Override
    public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
        mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);   //异步播放提示音
    }
}


/android/frameworks/base/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java   

public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
    Command cmd = new Command();
    cmd.requestTime = SystemClock.uptimeMillis();
    cmd.code = PLAY;
    cmd.context = context;
    cmd.uri = uri;
    cmd.looping = looping;
    cmd.attributes = attributes;
    synchronized (mCmdQueue) {
        enqueueLocked(cmd);                         //把异步播放提示音放到播放任务队列中
        mState = PLAY;
    }
}

private void enqueueLocked(Command cmd) {
    mCmdQueue.add(cmd);
    if (mThread == null) {
        acquireWakeLock();
        mThread = new CmdThread();                  //创建处理队列任务的线程
        mThread.start();                            //启动线程开始处理队列中的任务
    }
}

private final class CmdThread extends java.lang.Thread {
    public void run() {
        while (true) {
            Command cmd = null;
            synchronized (mCmdQueue) {
                cmd = mCmdQueue.removeFirst();      //取队列中第一条播放任务
            }
            switch (cmd.code) {
            case PLAY:
                startSound(cmd);                    //开始处理播放提示音任务
                break;
        }
    }
}

private void startSound(Command cmd) {
    synchronized(mCompletionHandlingLock) {
        mCompletionThread = new CreationAndCompletionThread(cmd);
        synchronized(mCompletionThread) {
            mCompletionThread.start();              //执行播放提示音线程
            mCompletionThread.wait();
        }
    }          
}

private final class CreationAndCompletionThread extends Thread {
    public Command mCmd;
    public CreationAndCompletionThread(Command cmd) {
        super();
        mCmd = cmd;
    }
    public void run() {
        Looper.prepare();
        mLooper = Looper.myLooper();
        synchronized(this) {
            try {
                MediaPlayer player = new MediaPlayer();
                player.setAudioAttributes(mCmd.attributes);
                player.setDataSource(mCmd.context, mCmd.uri);
                player.setLooping(mCmd.looping);
                player.prepare();
                player.start();                     //播放提示音
                if (mPlayer != null) {
                    mPlayer.release();
                }
            catch (Exception e) {
                Log.w(mTag, "error loading sound for " + mCmd.uri, e);
            }
            this.notify();
        }
        Looper.loop();
    }
};

总结

要理解Notification框架的原理,需要理清NotificationManager和NotificationManagerService之间是怎么通信的,NotificationManagerService和SystemUI之间是怎么通信的。INotificationManager.Stub不仅作为NotificationManagerService和NotificationManager的远程通信方式,也是NotificationManagerService和SystemUI的远程通信方式,不过SystemUI进程会创建和启动一个系统服务NotificationListenerService,这个系统服务通过INotificationManager.Stub把INotificationListener.Stub对象远程传给NotificationListenerService服务中,让NotificationListenerService服务通过INotificationListener.Stub对象和系统服务NotificationListenerService通信,系统服务NotificationListenerService再调用SystemUI进程来更新UI。

通信图.jpg
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 原文出处: http://www.androidchina.net/6174.html Notification在...
    木木00阅读 12,287评论 3 32
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,849评论 6 13
  • 为深入学习贯彻党的十八大精神,进一步明确就业创业工作重点,切实抓好学院学生就业创业工作,帮助学生树立创业意识,提升...
    LD丹哥阅读 264评论 0 0
  • 天气说冷就冷,不过,还算没有冷得猝不及防。 每天清早天微亮出门送小烟,也会常遇一团团的雾。尤其行至桥上的时候,雾气...
    煙煙阅读 320评论 0 1
  • 最近项目中需要获取一些权限,于是就大致整理搜集了下,都是基于系统最基本的 这些都是不需要获取用户权限的 手机型号 ...
    iSongWei阅读 2,521评论 0 5