Android|Android Notification

  1. 应用层调用过程,先生成一个notification对象,然后通过调用NotificationManager的norify方法,把nofication发布出去。
MainActivity.java
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationChannel notificationChannel = new NotificationChannel("1000", "test notification", NotificationManager.IMPORTANCE_DEFAULT); notificationManager.createNotificationChannel(notificationChannel); Notification.Builder builder = new Notification.Builder(this, notificationChannel.getId()); builder.setSmallIcon(R.mipmap.ic_launcher); builder.setContentTitle("hello"); builder.setContentText("for test"); Notification notification = builder.build(); notificationManager.notify(1000, notification);

  1. notify 【通知,公布】 是NotificationManager中的方法
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) { notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId())); }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); if (notification.sound != null) { notification.sound = notification.sound.getCanonicalUri(); if (StrictMode.vmFileUriExposureEnabled()) { notification.sound.checkFileUriExposed("Notification.sound"); } } fixLegacySmallIcon(notification, pkg); 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 + ")"); final Notification copy = Builder.maybeCloneStrippedForDelivery(notification); try { service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id, copy, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }

  1. 下面通过RPC到service端
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
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) { if (DBG) { Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); } checkCallerIsSystemOrSameApp(pkg); final int userId = ActivityManager.handleIncomingUser(callingPid, callingUid, incomingUserId, true, false, "enqueueNotification", pkg); final UserHandle user = new UserHandle(userId); if (pkg == null || notification == null) { throw new IllegalArgumentException("null not allowed: pkg=" + pkg + " id=" + id + " notification=" + notification); }// The system can post notifications for any package, let us resolve that. final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId); // Fix the notification as best we can. try { final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser( pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId); Notification.addFieldsFromContext(ai, notification); } catch (NameNotFoundException e) { Slog.e(TAG, "Cannot create a context for sending app", e); return; }mUsageStats.registerEnqueuedByApp(pkg); // setup local book-keeping String channelId = notification.getChannelId(); if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) { channelId = (new Notification.TvExtender(notification)).getChannelId(); } 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); doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" + "Failed to post notification on channel \"" + channelId + "\"\n" + "See log for more details"); return; }final StatusBarNotification n = new StatusBarNotification( pkg, opPkg, id, tag, notificationUid, callingPid, notification, user, null, System.currentTimeMillis()); final NotificationRecord r = new NotificationRecord(getContext(), n, channel); if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) { return; }// Whitelist pending intents. if (notification.allPendingIntents != null) { final int intentCount = notification.allPendingIntents.size(); if (intentCount > 0) { final ActivityManagerInternal am = LocalServices .getService(ActivityManagerInternal.class); final long duration = LocalServices.getService( DeviceIdleController.LocalService.class).getNotificationWhitelistDuration(); for (int i = 0; i < intentCount; i++) { PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i); if (pendingIntent != null) { am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), WHITELIST_TOKEN, duration); } } } }mHandler.post(new EnqueueNotificationRunnable(userId, r)); }protected class EnqueueNotificationRunnable implements Runnable { private final NotificationRecord r; private final int userId; EnqueueNotificationRunnable(int userId, NotificationRecord r) { this.userId = userId; this.r = r; }; @Override public void run() { synchronized (mNotificationLock) { mEnqueuedNotifications.add(r); scheduleTimeoutLocked(r); final StatusBarNotification n = r.sbn; if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey()); NotificationRecord old = mNotificationsByKey.get(n.getKey()); if (old != null) { // Retain ranking information from previous record r.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(); // Handle grouped notifications and bail out early if we // can to avoid extracting signals. handleGroupedNotificationLocked(r, old, callingUid, callingPid); // if this is a group child, unsnooze parent summary if (n.isGroup() && notification.isGroupChild()) { mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey()); }// 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; } EventLogTags.writeNotificationEnqueue(callingUid, callingPid, pkg, id, tag, userId, notification.toString(), enqueueStatus); }mRankingHelper.extractSignals(r); // tell the assistant service about the notification if (mNotificationAssistants.isEnabled()) { mNotificationAssistants.onNotificationEnqueued(r); mHandler.postDelayed(new PostNotificationRunnable(r.getKey()), DELAY_FOR_ASSISTANT_TIME); } else { mHandler.post(new PostNotificationRunnable(r.getKey())); } } } }protected class PostNotificationRunnable implements Runnable { private final String key; PostNotificationRunnable(String key) { this.key = key; }@Override public 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; } } if (r == null) { Slog.i(TAG, "Cannot find enqueued record for key: " + key); return; } NotificationRecord old = mNotificationsByKey.get(key); final StatusBarNotification n = r.sbn; final Notification notification = n.getNotification(); int index = indexOfNotificationLocked(n.getKey()); if (index < 0) { mNotificationList.add(r); mUsageStats.registerPostedByApp(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 & Notification.FLAG_FOREGROUND_SERVICE; r.isUpdate = true; }mNotificationsByKey.put(n.getKey(), r); // Ensure if this is a foreground service that the proper additional // flags are set. if ((notification.flags & Notification.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; mListeners.notifyPostedLocked(n, oldSbn); mHandler.post(new Runnable() { @Override public void run() { mGroupHelper.onNotificationPosted(n); } }); } else { Slog.e(TAG, "Not posting notification without small icon: " + notification); if (old != null && !old.isCanceled) { mListeners.notifyRemovedLocked(n, NotificationListenerService.REASON_ERROR); mHandler.post(new Runnable() { @Override public void run() { mGroupHelper.onNotificationRemoved(n); } }); } // ATTENTION: in a future release we will bail out here // so that we do not play sounds, show lights, etc. for invalid // notifications Slog.e(TAG, "WARNING: In a future release this will crash the app: " + n.getPackageName()); }buzzBeepBlinkLocked(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; } } } } } }

  1. NotificationManagerService 的内部类 NotificationListeners
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { // Lazily initialized snapshots of the notification. TrimCache trimCache = new TrimCache(sbn); for (final ManagedServiceInfo info : getServices()) { boolean sbnVisible = isVisibleToListener(sbn, info); boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; // This notification hasn't been and still isn't visible -> ignore. if (!oldSbnVisible && !sbnVisible) { continue; } final NotificationRankingUpdate update = makeRankingUpdateLocked(info); // This notification became invisible -> remove the old one. if (oldSbnVisible && !sbnVisible) { final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); mHandler.post(new Runnable() { @Override public void run() { notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED); } }); continue; }final StatusBarNotification sbnToPost =trimCache.ForListener(info); 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 { listener.onNotificationPosted(sbnHolder, rankingUpdate); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (posted): " + listener, ex); } }

至此,调用过程完成。
  1. 更进一步,需要了解getServices()返回的变量的初始化及赋值过程。
frameworks/base/services/core/java/com/android/server/notification/ManagedServices.java
protected List getServices() { synchronized (mMutex) { List services = new ArrayList<>(mServices); return services; } }

  1. 下面来看看mServices变量的初始化及赋值过程
    搜索过滤发现,ManagedServices类中一共有两处向mService中add something的地方。
    下面是一个倒序的过程
6.1 第一处
private void registerServiceLocked(final ComponentName name, final int userid, final boolean isSystem) { if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid); final String servicesBindingTag = name.toString() + "/" + userid; if (mServicesBinding.contains(servicesBindingTag)) { // stop registering this thing already! we're working on it return; } mServicesBinding.add(servicesBindingTag); final int N = mServices.size(); for (int i = N - 1; i >= 0; i--) { final ManagedServiceInfo info = mServices.get(i); if (name.equals(info.component) && info.userid == userid) { // cut old connections if (DEBUG) Slog.v(TAG, "disconnecting old " + getCaption() + ": " + info.service); removeServiceLocked(i); if (info.connection != null) { mContext.unbindService(info.connection); } } }Intent intent = new Intent(mConfig.serviceInterface); intent.setComponent(name); intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel); final PendingIntent pendingIntent = PendingIntent.getActivity( mContext, 0, new Intent(mConfig.settingsAction), 0); intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent); ApplicationInfo appInfo = null; try { appInfo = mContext.getPackageManager().getApplicationInfo( name.getPackageName(), 0); } catch (NameNotFoundException e) { // Ignore if the package doesn't exist we won't be able to bind to the service. } final int targetSdkVersion = appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE; try { if (DEBUG) Slog.v(TAG, "binding: " + intent); ServiceConnection serviceConnection = new ServiceConnection() { IInterface mService; @Override public void onServiceConnected(ComponentName name, IBinder binder) { boolean added = false; ManagedServiceInfo info = null; synchronized (mMutex) { mServicesBinding.remove(servicesBindingTag); try { mService = asInterface(binder); info = newServiceInfo(mService, name, userid, isSystem, this, targetSdkVersion); binder.linkToDeath(info, 0); added = mServices.add(info); } catch (RemoteException e) { // already dead } } if (added) { onServiceAdded(info); } }@Override public void onServiceDisconnected(ComponentName name) { Slog.v(TAG, getCaption() + " connection lost: " + name); } }; if (!mContext.bindServiceAsUser(intent, serviceConnection, BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT, new UserHandle(userid))) { mServicesBinding.remove(servicesBindingTag); Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent); return; } } catch (SecurityException ex) { Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex); return; } }public void registerSystemService(final ComponentName name, final int userid) { synchronized (mMutex) { registerServiceLocked(name, userid, true /* isSystem */); } }

6.2 第二处,registerService()和registerGuestService()是同级的,都是调用registerServiceImpl():
private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) { synchronized (mMutex) { try { info.service.asBinder().linkToDeath(info, 0); mServices.add(info); return info; } catch (RemoteException e) { // already dead } } return null; }public void registerService(IInterface service, ComponentName component, int userid) { checkNotNull(service); ManagedServiceInfo info = registerServiceImpl(service, component, userid); if (info != null) { onServiceAdded(info); } }public void registerGuestService(ManagedServiceInfo guest) { checkNotNull(guest.service); if (!checkType(guest.service)) { throw new IllegalArgumentException(); } if (registerServiceImpl(guest) != null) { onServiceAdded(guest); } }

【Android|Android Notification】再往下,就不能继续往下跟了。猜测应该是其它地方会调用这里的三个public方法中的一个,注册自己为这种类型的服务。
  1. 根据官方文档或经验,系统的默认notification listener service 是在SystemUI中实现的。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public void start() { ... ... // Set up the initial notification state. try { mNotificationListener.registerAsSystemService(mContext, new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), UserHandle.USER_ALL); } catch (RemoteException e) { Log.e(TAG, "Unable to register notification listener", e); } ... ... }

frameworks/base/core/java/android/service/notification/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); }

frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
public void registerListener(final INotificationListener listener, final ComponentName component, final int userid) { enforceSystemOrSystemUI("INotificationManager.registerListener"); mListeners.registerService(listener, component, userid); }

到这里,就看到了6.2的方法。
  1. 下面看一看notification的UI是如何显示出来的
    接着4中,最后会调用notification listener service 的onNotificationPosted()方法。下面来看一下SystemUI的这个方法:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
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() { processForRemoteInput(sbn.getNotification()); String key = sbn.getKey(); mKeysKeptForRemoteInput.remove(key); boolean isUpdate = mNotificationData.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 && mGroupManager.isChildInGroupWithSummary(sbn)) { if (DEBUG) { Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); }// Remove existing notification to avoid stale data. if (isUpdate) { removeNotification(key, rankingMap); } else { mNotificationData.updateRanking(rankingMap); } return; } try { if (isUpdate) { updateNotification(sbn, rankingMap); } else { addNotification(sbn, rankingMap); } } catch (InflationException e) { handleInflationException(sbn, e); } } }); } }public void addNotification(StatusBarNotification notification, RankingMap ranking) throws InflationException { String key = notification.getKey(); if (DEBUG) Log.d(TAG, "addNotification key=" + key); mNotificationData.updateRanking(ranking); Entry shadeEntry = createNotificationViews(notification); boolean isHeadsUped = shouldPeek(shadeEntry); if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) { if (shouldSuppressFullScreenIntent(key)) { if (DEBUG) { Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key); } } else if (mNotificationData.getImportance(key) < NotificationManager.IMPORTANCE_HIGH) { if (DEBUG) { Log.d(TAG, "No Fullscreen intent: not important enough: " + key); } } else { // Stop screensaver if the notification has a full-screen intent. // (like an incoming phone call) awakenDreams(); // not immersive & a full-screen alert should be shown if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); try { EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, key); notification.getNotification().fullScreenIntent.send(); shadeEntry.notifyFullScreenIntentLaunched(); mMetricsLogger.count("note_fullscreen", 1); } catch (PendingIntent.CanceledException e) { } } } abortExistingInflation(key); mForegroundServiceController.addNotification(notification, mNotificationData.getImportance(key)); mPendingNotifications.put(key, shadeEntry); }protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) throws InflationException { if (DEBUG) { Log.d(TAG, "createNotificationViews(notification=" + sbn); } NotificationData.Entry entry = new NotificationData.Entry(sbn); Dependency.get(LeakDetector.class).trackInstance(entry); entry.createIcons(mContext, sbn); // Construct the expanded view. inflateViews(entry, mStackScroller); return entry; }protected void inflateViews(Entry entry, ViewGroup parent) { PackageManager pmUser = getPackageManagerForUser(mContext, entry.notification.getUser().getIdentifier()); final StatusBarNotification sbn = entry.notification; if (entry.row != null) { entry.reset(); updateNotification(entry, pmUser, sbn, entry.row); } else { new RowInflaterTask().inflate(mContext, parent, entry, row -> { bindRow(entry, pmUser, sbn, row); updateNotification(entry, pmUser, sbn, row); }); }}private void updateNotification(Entry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row) { row.setNeedsRedaction(needsRedaction(entry)); boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey()); boolean isUpdate = mNotificationData.get(entry.key) != null; boolean wasLowPriority = row.isLowPriority(); row.setIsLowPriority(isLowPriority); row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority)); // bind the click event to the content area mNotificationClicker.register(row, sbn); // Extract target SDK version. try { ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0); entry.targetSdk = info.targetSdkVersion; } catch (NameNotFoundException ex) { Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); } row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); entry.autoRedacted = entry.notification.getNotification().publicVersion == null; // MStar Android Patch Begin if (((sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE)) != null) || ((sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TEXT)) != null)) { final StatusBarIcon ic = new StatusBarIcon( entry.notification.getUser(), entry.notification.getPackageName(), entry.notification.getNotification().getSmallIcon(), entry.notification.getNotification().iconLevel, entry.notification.getNotification().number, entry.notification.getNotification().tickerText); Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic); final View popupNotificationView = LayoutInflater.from(mContext).inflate( com.android.internal.R.layout.notification_template_material_base, null); popupNotificationView.setBackgroundColor(0xffffffff); final ImageView popupNotificationIcon = (ImageView) popupNotificationView.findViewById( com.android.internal.R.id.icon); final ImageView popupNotificationProfileBadge = (ImageView) popupNotificationView.findViewById( com.android.internal.R.id.profile_badge); popupNotificationIcon.setImageDrawable(iconDrawable); if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) { popupNotificationIcon.setBackgroundResource( com.android.internal.R.drawable.notification_template_icon_bg); int padding = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_large_icon_circle_padding); popupNotificationIcon.setPadding(padding, padding, padding, padding); if (sbn.getNotification().color != Notification.COLOR_DEFAULT) { popupNotificationIcon.getBackground().setColorFilter( sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP); } }if (popupNotificationProfileBadge != null) { Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity( entry.notification.getUser(), 0); if (profileDrawable != null) { popupNotificationProfileBadge.setImageDrawable(profileDrawable); popupNotificationProfileBadge.setVisibility(View.VISIBLE); } else { popupNotificationProfileBadge.setVisibility(View.GONE); } }final TextView popupNotificationTitle = (TextView) popupNotificationView.findViewById( com.android.internal.R.id.title); final TextView popupNotificationText = (TextView) popupNotificationView.findViewById( com.android.internal.R.id.text); if (sbn.getNotification().extras != null) { popupNotificationTitle.setText(sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE)); popupNotificationText.setText(sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TEXT)); } else if (sbn.getNotification().tickerText != null){ popupNotificationTitle.setText(sbn.getNotification().tickerText); }WindowManager.LayoutParams lp = new WindowManager.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888); lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp; mWindowManager.addView(popupNotificationView, lp); mNotificationClicker.register(row, popupNotificationView, sbn); mHandler.postDelayed(new Runnable() { @Override public void run() { mWindowManager.removeView(popupNotificationView); } }, 3000); } else { Log.e(TAG, "Notification String Title and Text is null "); } // MStar Android Patch Endentry.row = row; entry.row.setOnActivatedListener(this); boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn, mNotificationData.getImportance(sbn.getKey())); boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded; row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); row.updateNotification(entry); }

最终,可以知道,notification的通知UI是通过WindowManager添加的无焦点小窗口。
至此,整个过程完结。

    推荐阅读