Android通知系统源码解析

  • 1. 概述
  • 2. 流程图
    • 2.1. 发送通知流程图
  • 3. 源码解析
    • 3.1. 使用通知--APP进程
      • 3.1.1. 创建通知:
      • 3.1.2. 发送(更新)通知:
      • 3.1.3. 取消通知:
      • 3.1.4. 创建通知源码解析:
    • 3.2. 管理通知--SystemServer进程
      • 3.2.1. 发送通知:
      • 3.2.2. Service处理和推送通知:
    • 3.3. 展示通知--SystemUI进程
  • 4. 小结

1. 概述

Android 通知系统是应用与系统UI交互的重要系统,方便应用告知用户有新的通知或正在运行的后台程序,用户可通过通知面板直接或间接与应用交互,并可以随时跳转。
本文基于Android P的代码,只讲述发送通知和SystemUI注册流程,与取消通知流程类似不再赘述,可自行查看源码。

2. 流程图

2.1. 发送通知流程图

3. 源码解析

整个通知系统主要涉及到三个进程,分别是:

  • APP进程:负责更新、创建、发送或取消通知;
  • SystemServer进程:负责管理(添加、取消、控制等)通知,类似通知的管理中心;
  • SystemUI进程:负责显示通知,并保持与用户的交互;
3.1. 使用通知–APP进程
3.1.1. 创建通知:
Intent intent = new Intent();
intent.setClass(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this,0, intent, PendingIntent.FLAG_CANCEL_CURRENT);Notification.Builder builder = new Notification.Builder(MainActivity.this, channelId).setSmallIcon(R.mipmap.ic_launcher).setContentTitle("Test Title").setContentText("Test Content").setTicker("Test Ticker").setAutoCancel(true).setContentIntent(pendingIntent).setVisibility(Notification.VISIBILITY_PUBLIC);Notification notification = builder.build();
3.1.2. 发送(更新)通知:
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(0, notification);
3.1.3. 取消通知:
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel(0);
// 或取消全部通知
// nm.cancelAll();
3.1.4. 创建通知源码解析:
  • 构建一个Notification.Builder 对象,然后初始化Notification部分字段。典型的建造者模式,将构建复杂对象的操作分解成一步步简单的操作。

    Notification.Builder

        public Builder(Context context, String channelId) {this(context, (Notification) null);mN.mChannelId = channelId;}public Builder(Context context, Notification toAdopt) {....if (toAdopt == null) {mN = new Notification();if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);}mN.priority = PRIORITY_DEFAULT;mN.visibility = VISIBILITY_PRIVATE;}...}
    
  • 通过Notification.Builder 建造并封装好Notification对象,然后返回。发现Android 5.0 之后,构建Notification的时候,不初始化和携带contentView、bigContentView等RemoteView(处理应用自定义RemoteView);
    保留问题1: 为什么呢?什么时候去加载RemoteView?
    在下面SystemUI侧的源码分析中会讲到这点。原因我认为是,Notification需要跨进程被传输,携带过多数据会降低性能。

    Notification.java

    public Notification build() {// first, add any extras from the calling codeif (mUserExtras != null) {mN.extras = getAllExtras();}mN.creationTime = System.currentTimeMillis();// lazy stuff from mContext; see comment in Builder(Context, Notification)Notification.addFieldsFromContext(mContext, mN);buildUnstyled();// 初始化mStyle内容,在这里可以发现,可以通过setStyle()接口为Notification设置不同的style,比如我们最常见的BigPictureStyle,以及播放器MediaStyle等等,可自行去查询相关接口if (mStyle != null) {mStyle.reduceImageSizes(mContext);mStyle.purgeResources();mStyle.validate(mContext);mStyle.buildStyled(mN);}mN.reduceImageSizes(mContext);// Android 5.0 之后,构建Notification的时候,不初始化和携带contentView、bigContentView等RemoteViewif (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N && (useExistingRemoteView())) {if (mN.contentView == null) {mN.contentView = createContentView();mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,mN.contentView.getSequenceNumber());}if (mN.bigContentView == null) {mN.bigContentView = createBigContentView();if (mN.bigContentView != null) {mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,mN.bigContentView.getSequenceNumber());}}if (mN.headsUpContentView == null) {mN.headsUpContentView = createHeadsUpContentView();if (mN.headsUpContentView != null) {mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,mN.headsUpContentView.getSequenceNumber());}}}if ((mN.defaults & DEFAULT_LIGHTS) != 0) {mN.flags |= FLAG_SHOW_LIGHTS;}mN.allPendingIntents = null;return mN;
    }
    
3.2. 管理通知–SystemServer进程
3.2.1. 发送通知:
  • 应用调用Notification.notify()接口发送通知,最终会进入system_server进程,调用NotificationManagerService的enqueueNotificationWithTag()。

    NotificationManager.java

    public void notify(int id, Notification notification){notify(null, id, notification);
    }public void notify(String tag, int id, Notification notification){notifyAsUser(tag, id, notification, mContext.getUser());
    }/**
    * @hide
    */
    public void notifyAsUser(String tag, int id, Notification notification, UserHandle user){INotificationManager service = getService();String pkg = mContext.getPackageName();// Fix the notification as best we can.Notification.addFieldsFromContext(mContext, notification);...fixLegacySmallIcon(notification, pkg);// Android 5.0之后必须要设置small icon 否则抛异常if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {if (notification.getSmallIcon() == null) {throw new IllegalArgumentException("Invalid notification (no valid small icon): "+ notification);}}if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");notification.reduceImageSizes(mContext);ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);boolean isLowRam = am.isLowRamDevice();final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam,mContext);try {service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,copy, user.getIdentifier());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
    }
    
3.2.2. Service处理和推送通知:
  • 最终通过mHandler执行EnqueueNotificationRunnable。

    NotificationManagerService.java

    @Override
    public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,Notification notification, int userId) throws RemoteException {enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),Binder.getCallingPid(), tag, id, notification, 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 incomingUserId) {...// 检查发送通知的App是不是同一个App或系统AppcheckCallerIsSystemOrSameApp(pkg);// The system can post notifications for any package, let us resolve that.final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);.....// setup local book-keepingString channelId = notification.getChannelId();...// 在Android 9.0上如果没有创建channel,则无法发送通知final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,notificationUid, channelId, false /* includeDeleted */);if (channel == null) {final String noChannelStr = "No Channel found for "+ "pkg=" + pkg+ ", channelId=" + channelId+ ", id=" + id+ ", tag=" + tag+ ", opPkg=" + opPkg+ ", callingUid=" + callingUid+ ", userId=" + userId+ ", incomingUserId=" + incomingUserId+ ", notificationUid=" + notificationUid+ ", notification=" + notification;Log.e(TAG, noChannelStr);boolean appNotificationsOff = mRankingHelper.getImportance(pkg, notificationUid)== NotificationManager.IMPORTANCE_NONE;if (!appNotificationsOff) {doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +"Failed to post notification on channel \"" + channelId + "\"\n" +"See log for more details");}return;}// 将通知信息封装到StatusBarNotification对象final StatusBarNotification n = new StatusBarNotification(pkg, opPkg, id, tag, notificationUid, callingPid, notification,user, null, System.currentTimeMillis());// 然后再次创建NotificationRecord对象,该对象仅仅在system_server进程中流通        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid));...// 通知发送速率的检查、黑名单的检查、通知上限的检查,每个应用当前的通知数量最多为50个if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,r.sbn.getOverrideGroupKey() != null)) {return;}...mHandler.post(new EnqueueNotificationRunnable(userId, r));
    }
    
  • EnqueueNotificationRunnable最终又通过Handler 执行 PostNotificationRunnable进行最后的推送通知。

    NotificationManagerService.EnqueueNotificationRunnable

    protected class EnqueueNotificationRunnable implements Runnable {...@Overridepublic void run() {synchronized (mNotificationLock) {mEnqueuedNotifications.add(r);scheduleTimeoutLocked(r);final StatusBarNotification n = r.sbn;if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());// 获取旧的NotificationRecordNotificationRecord old = mNotificationsByKey.get(n.getKey());if (old != null) {// Retain ranking information from previous recordr.copyRankingInformation(old);}final int callingUid = n.getUid();final int callingPid = n.getInitialPid();final Notification notification = n.getNotification();final String pkg = n.getPackageName();final int id = n.getId();final String tag = n.getTag();.....// This conditional is a dirty hack to limit the logging done on//     behalf of the download manager without affecting other apps.if (!pkg.equals("com.android.providers.downloads")|| Log.isLoggable("DownloadManager", Log.VERBOSE)) {int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;if (old != null) {enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;}// 打印Event Log,一般在进行通知的问题分析过程中,可通过该LOG查看当前通知的信息,判断当前通知是否发送成功;EventLogTags.writeNotificationEnqueue(callingUid, callingPid,pkg, id, tag, userId, notification.toString(),enqueueStatus);}// 更新NotificationRecord内容,RankingHelper管理了通知的开关、channel维护,比如横幅通知是否开启,在此处更新;mRankingHelper.extractSignals(r);// tell the assistant service about the notificationif (mAssistants.isEnabled()) {mAssistants.onNotificationEnqueued(r);mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),DELAY_FOR_ASSISTANT_TIME);} else {// Post 通知出去mHandler.post(new PostNotificationRunnable(r.getKey()));}}}
    }
  • 推送通知最后又交由NotificationListeners mListeners处理,通过类名可以判断出NotificationListeners应该维护了通知的监听者,那么我们可以猜测SystemUI的注册类应该有该Listerners维护。

    NotificationManagerService.PostNotificationRunnable

    protected class PostNotificationRunnable implements Runnable {....@Overridepublic void run() {synchronized (mNotificationLock) {try {NotificationRecord r = null;int N = mEnqueuedNotifications.size();for (int i = 0; i < N; i++) {final NotificationRecord enqueued = mEnqueuedNotifications.get(i);if (Objects.equals(key, enqueued.getKey())) {r = enqueued;break;}}....NotificationRecord old = mNotificationsByKey.get(key);final StatusBarNotification n = r.sbn;final Notification notification = n.getNotification();// 通知列表mNotificationList查看是否存在该通知 int index = indexOfNotificationLocked(n.getKey());if (index < 0) {mNotificationList.add(r);mUsageStats.registerPostedByApp(r);r.setInterruptive(isVisuallyInterruptive(null, r));} else {old = mNotificationList.get(index);mNotificationList.set(index, r);mUsageStats.registerUpdatedByApp(r, old);// Make sure we don't lose the foreground service state.notification.flags |=old.getNotification().flags & FLAG_FOREGROUND_SERVICE;r.isUpdate = true;r.setTextChanged(isVisuallyInterruptive(old, r));}// 更新通知列表  mNotificationsByKey.put(n.getKey(), r);// 再一次确认前台通知的标签没有被清除掉// Ensure if this is a foreground service that the proper additional// flags are set.if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {notification.flags |= Notification.FLAG_ONGOING_EVENT| Notification.FLAG_NO_CLEAR;}applyZenModeLocked(r);// 对通知列表进行排序mRankingHelper.sort(mNotificationList);if (notification.getSmallIcon() != null) {StatusBarNotification oldSbn = (old != null) ? old.sbn : null;// 关键步骤:将通知推送出去,让NotificationListeners:mListeners处理mListeners.notifyPostedLocked(r, old);if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {mHandler.post(new Runnable() {@Overridepublic void run() {mGroupHelper.onNotificationPosted(n, hasAutoGroupSummaryLocked(n));}});}} else {........}if (!r.isHidden()) {// 处理该通知,主要是是否发声,震动,Led灯buzzBeepBlinkLocked(r);}maybeRecordInterruptionLocked(r);} finally {int N = mEnqueuedNotifications.size();for (int i = 0; i < N; i++) {final NotificationRecord enqueued = mEnqueuedNotifications.get(i);if (Objects.equals(key, enqueued.getKey())) {mEnqueuedNotifications.remove(i);break;}}}}}
    }
  • NotificationListeners 的notifyPosted中将通知推送至SystemUI中显示,在该方法中listener数据类型是NotificationListenerWrapper的代理对象,NotificationListenerWrapper在SystemUI进程,在此处listener是client, NotificationListenerWrapper是server。
    保留问题2: listener是什么时候成为SystemUI进程处理通知的代理类?

    NotificationManagerService.NotificationListeners

    public class NotificationListeners extends ManagedServices {public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) {notifyPostedLocked(r, old, true);}private void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) {// Lazily initialized snapshots of the notification.StatusBarNotification sbn = r.sbn;StatusBarNotification oldSbn = (old != null) ? old.sbn : null;TrimCache trimCache = new TrimCache(sbn);// 遍历所有ManagedServiceInfofor (final ManagedServiceInfo info : getServices()) {....// 如果该通知变得不可见,则移除老的通知// This notification became invisible -> remove the old one.if (oldSbnVisible && !sbnVisible) {final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();mHandler.post(new Runnable() {@Overridepublic void run() {notifyRemoved(info, oldSbnLightClone, update, null, REASON_USER_STOPPED);}});continue;}// 推送通知mHandler.post(new Runnable() {@Overridepublic void run() {notifyPosted(info, sbnToPost, update);}});}}private void notifyPosted(final ManagedServiceInfo info,final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {// listener数据类型是NotificationListenerWrapper的代理对象,NotificationListenerWrapper在SystemUI进程,在此处listener是client, NotificationListenerWrapper是server    final INotificationListener listener = (INotificationListener) info.service;StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);try {// 跨进程调用,进入SystemUI进程listener.onNotificationPosted(sbnHolder, rankingUpdate);} catch (RemoteException ex) {Log.e(TAG, "unable to notify listener (posted): " + listener, ex);}}
    }
    
3.3. 展示通知–SystemUI进程

首先我们来解决上面留下的保留问题2,SystemUI是怎么接收和处理通知的。

3.3.1. 注册监听

  • SystemUI进程在起来的时候,会执行StatusBar.start()方法,start()方法里面会执行mNotificationListener.setUpWithPresenter()在注册监听,其中mNotificationListener是NotificationListener类型,然后我们来看看NotificationListener类。

    StatusBar.java

    @Override
    public void start() {// Set up the initial notification state.mNotificationListener.setUpWithPresenter(this, mEntryManager);
    }
    
  • NotificationListener类继承于NotificationListenerWithPlugins,而NotificationListenerWithPlugins又继承于NotificationListenerService,同时也看到非常熟悉的方法,比如onNotificationPosted 等等。
    注册最终会调到NotificationListenerService.registerAsSystemService()方法;

    NotificationListener.java

    public class NotificationListener extends NotificationListenerWithPlugins {private static final String TAG = "NotificationListener";// Dependencies:private final NotificationRemoteInputManager mRemoteInputManager =Dependency.get(NotificationRemoteInputManager.class);private final Context mContext;protected NotificationPresenter mPresenter;protected NotificationEntryManager mEntryManager;public NotificationListener(Context context) {mContext = context;}@Overridepublic void onListenerConnected() {if (DEBUG) Log.d(TAG, "onListenerConnected");onPluginConnected();final StatusBarNotification[] notifications = getActiveNotifications();if (notifications == null) {Log.w(TAG, "onListenerConnected unable to get active notifications.");return;}final RankingMap currentRanking = getCurrentRanking();mPresenter.getHandler().post(() -> {for (StatusBarNotification sbn : notifications) {mEntryManager.addNotification(sbn, currentRanking);}});}// 接收通知,然后交由mEntryManager进行处理@Overridepublic void onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap) {if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {mPresenter.getHandler().post(() -> {processForRemoteInput(sbn.getNotification(), mContext);String key = sbn.getKey();mEntryManager.removeKeyKeptForRemoteInput(key);boolean isUpdate =mEntryManager.getNotificationData().get(key) != null;// In case we don't allow child notifications, we ignore children of// notifications that have a summary, since` we're not going to show them// anyway. This is true also when the summary is canceled,// because children are automatically canceled by NoMan in that case.if (!ENABLE_CHILD_NOTIFICATIONS&& mPresenter.getGroupManager().isChildInGroupWithSummary(sbn)) {if (DEBUG) {Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);}// Remove existing notification to avoid stale data.if (isUpdate) {mEntryManager.removeNotification(key, rankingMap);} else {mEntryManager.getNotificationData().updateRanking(rankingMap);}return;}if (isUpdate) {mEntryManager.updateNotification(sbn, rankingMap);} else {mEntryManager.addNotification(sbn, rankingMap);}});}}// 通知移除的回调,同样交由mEntryManager进行处理@Overridepublic void onNotificationRemoved(StatusBarNotification sbn,final RankingMap rankingMap) {if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {final String key = sbn.getKey();mPresenter.getHandler().post(() -> {mEntryManager.removeNotification(key, rankingMap);});}}@Overridepublic void onNotificationRankingUpdate(final RankingMap rankingMap) {if (DEBUG) Log.d(TAG, "onRankingUpdate");if (rankingMap != null) {RankingMap r = onPluginRankingUpdate(rankingMap);mPresenter.getHandler().post(() -> {mEntryManager.updateNotificationRanking(r);});}}// 注册回调public void setUpWithPresenter(NotificationPresenter presenter,NotificationEntryManager entryManager) {mPresenter = presenter;mEntryManager = entryManager;try {registerAsSystemService(mContext,new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),UserHandle.USER_ALL);} catch (RemoteException e) {Log.e(TAG, "Unable to register notification listener", e);}}
    }
    
  • 创建NotificationListenerWrapper实例,NotificationListenerWrapper类就是上面所述的server端,然后将mWrapper对象通过NotificationManagerService传递出去;再来看看NotificationManagerService 的 registerListener 方法

    NotificationListenerService.java

    public void registerAsSystemService(Context context, ComponentName componentName,int currentUser) throws RemoteException {if (mWrapper == null) {mWrapper = new NotificationListenerWrapper();}mSystemContext = context;INotificationManager noMan = getNotificationInterface();mHandler = new MyHandler(context.getMainLooper());mCurrentUser = currentUser;noMan.registerListener(mWrapper, componentName, currentUser);
    }protected class NotificationListenerWrapper extends INotificationListener.Stub {@Overridepublic void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,NotificationRankingUpdate update) {StatusBarNotification sbn;...// protect subclass from concurrent modifications of (@link mNotificationKeys}.synchronized (mLock) {applyUpdateLocked(update);if (sbn != null) {SomeArgs args = SomeArgs.obtain();args.arg1 = sbn;args.arg2 = mRankingMap;// 发送通知,mHandler处理MSG_ON_NOTIFICATION_POSTED事件时,会调用自身的onNotificationPosted 方法,即NotificationListener 重写的onNotificationPosted 方法; mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,args).sendToTarget();} else {// still pass along the ranking map, it may contain other informationmHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,mRankingMap).sendToTarget();}}}@Overridepublic void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,NotificationRankingUpdate update, NotificationStats stats, int reason) {........}@Overridepublic void onListenerConnected(NotificationRankingUpdate update) {.....}@Overridepublic void onNotificationRankingUpdate(NotificationRankingUpdate update) throws RemoteException {.....    }@Overridepublic void onListenerHintsChanged(int hints) throws RemoteException {mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_HINTS_CHANGED,hints, 0).sendToTarget();}.....
    }
    
  • mListeners 会将INotificationListener封装到ManagedServices.ManagedServiceInfo ,看到这里一切就明了。

    NotificationManagerService.java

    public void registerListener(final INotificationListener listener,final ComponentName component, final int userid) {enforceSystemOrSystemUI("INotificationManager.registerListener");mListeners.registerService(listener, component, userid);
    }
    
  • 保留问题1
    SystemUI什么时候去创建RemoteView,SystemUI里面有个创建通知视图的类NotificationInflater,在类里面会根据传递过来的StatusBarNotification 数据在自己进程端构建RemoteView。
    这是非常聪明的做法,对于标准通知视图,根本不用携带着在进程间通信流转,非常浪费资源甚至造成卡顿。

    NotificationInflater.java

    // 重建通知的RemoteView
    private static InflationProgress createRemoteViews(int reInflateFlags,Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,Context packageContext) {InflationProgress result = new InflationProgress();isLowPriority = isLowPriority && !isChildInGroup;if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);}if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {result.newExpandedView = createExpandedView(builder, isLowPriority);}if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);}if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {result.newPublicView = builder.makePublicContentView();}if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification(): builder.makeAmbientNotification();}result.packageContext = packageContext;result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(true /* showingPublic */);return result;
    }// 最终也是调用Notification.Builder来创建
    private static RemoteViews createContentView(Notification.Builder builder,boolean isLowPriority, boolean useLarge) {if (isLowPriority) {return builder.makeLowPriorityContentView(false /* useRegularSubtext */);}return builder.createContentView(useLarge);
    }
    

4. 小结

  • Notification系统用了典型的建造者模式;
  • 针对标准视图样式,使用了传参的方式在SystemUI进程创建RemoteView,提高跨进程通讯的效率;

Android通知系统源码解析相关推荐

  1. Android FrameWork 系统源码调试

    这是很久以前访问掘金的时候 无意间看到的一个关于Android的文章,作者更细心,分阶段的将学习步骤记录在自己博客中,我觉得很有用,想作为分享同时也是留下自己知识的一些欠缺收藏起来,今后做项目的时候会 ...

  2. BAT高级架构师合力熬夜15天,肝出了这份PDF版《Android百大框架源码解析》,还不快快码住。。。

    前言 为什么要阅读源码? 现在中高级Android岗位面试中,对于各种框架的源码都会刨根问底,从而来判断应试者的业务能力边际所在.但是很多开发者习惯直接搬运,对各种框架的源码都没有过深入研究,在面试时 ...

  3. Mybatis 缓存系统源码解析

    Mybatis 缓存系统源码解析 转载于:https://juejin.im/post/5bfa50905188251d0920006c

  4. http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

    http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

  5. SEAL全同态加密开源库(七) rns剩余数系统-源码解析

    SEAL全同态加密开源库(七) rns剩余数系统-源码解析 2021SC@SDUSC 2021-11-14 前言 这是SEAL开源库代码分析报告第六篇,本篇将分析util文件夹中的rns.h和rns. ...

  6. Window 10 使用WSL2下载编译Android 10 系统源码,并用sourceInsight 4 看系统源码

    一.Window 10 安装WSL2 安装教程来自微软官网:https://docs.microsoft.com/zh-cn/windows/wsl/install-win10 步骤 1 - 启用适用 ...

  7. Android之EasyPermissions源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 我们知道在Android中想要申请权限就需要在AndroidManifest ...

  8. Android 12系统源码_SystemUI(八)SystemUIVisibility属性

    前言 在Android系统中,很多应用都需要根据具体情况来控制状态栏和导航栏的显示和隐藏,又或者将状态栏透明,实现诸如沉浸式.全面屏灯效果,而要实现这些效果,都离不开SystemUIVisibilit ...

  9. Android 12系统源码_SystemUI(一)SystemUI的启动流程

    前言 Android 的 SystemUI 其实就是 Android 的系统界面,它包括了界面上方的状态栏 status bar,下方的导航栏Navigation Bar,锁屏界面 Keyguard ...

最新文章

  1. C#读取SHAPEFILE文件总结0--大纲
  2. 运算方法和运算部件一
  3. Linux常用Shell脚本珍藏
  4. 插入排序 链表 java_JAVA单链表(多项式)直接插入排序,大家看看我的怎么不行呢...
  5. 【整理】ABAP 7.40新特性介绍(上)
  6. GraphQL一些hello world级别的例子
  7. TCP协议-如何保证传输可靠性
  8. 微软企业库5.0学习笔记(三十三)数据访问模块
  9. 你担心大家会滥用的全局变量,大家(包括你自己)一定会滥用
  10. php加密码访问,怎么给一个PHP密码访问页面加超链接
  11. Zabbix 对接 LDAP 实现用户统一登录的方法
  12. 2012-2-24 《数据结构》读书笔记2 线性表
  13. ORACLE SQL调优案例一则
  14. JavaScript中的数据类型及数据类型转换(附实例)
  15. MyBatis3详细教程-从入门到精通
  16. 用plsql将excel导入oracle,使用工具 plsqldev将Excel导入Oracle数据库
  17. 不卡顿成用户购机第一要素,Mate 9深得人心
  18. 实在不行来瓶敌敌畏吧!Scrapy框架爬取某农药肥料网站+异步Mysql数据库存储
  19. LDA NCA: 降维与度量学习
  20. matlab三维画图总结

热门文章

  1. 【Monthly Recording】2021, MAY
  2. 2021-4-29 NTD第二阶段第一天
  3. Mybatis 一对多返回重复数据解决方式
  4. ios layer的一些学习
  5. 离散型制造业如何做生产质量管控和智能追溯?
  6. HTML展开头文件,html头文件css的写法
  7. Unity MOBA类型游戏的战争迷雾效果
  8. 计算机编程与ug编程,如何学习ug编程
  9. Redis practise(二)使用Docker部署Redis高可用,分布式集群
  10. 计算机三员培训个人总结,信息技术培训个人工作总结