Android Notification

  1. 應(yīng)用層調(diào)用過程盖灸,先生成一個(gè)notification對象均驶,然后通過調(diào)用NotificationManager的norify方法郑口,把nofication發(fā)布出去颓影。

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 的內(nèi)部類 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);
    }
}

至此,調(diào)用過程完成诡挂。

  1. 更進(jìn)一步碎浇,需要了解getServices()返回的變量的初始化及賦值過程临谱。

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

protected List<ManagedServiceInfo> getServices() {
    synchronized (mMutex) {
        List<ManagedServiceInfo> services = new ArrayList<>(mServices);
        return services;
    }
}
  1. 下面來看看mServices變量的初始化及賦值過程
    搜索過濾發(fā)現(xiàn),ManagedServices類中一共有兩處向mService中add something的地方奴璃。
    下面是一個(gè)倒序的過程

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()是同級的,都是調(diào)用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);
    }
}

再往下苟穆,就不能繼續(xù)往下跟了抄课。猜測應(yīng)該是其它地方會調(diào)用這里的三個(gè)public方法中的一個(gè),注冊自己為這種類型的服務(wù)雳旅。

  1. 根據(jù)官方文檔或經(jīng)驗(yàn)剖膳,系統(tǒng)的默認(rèn)notification listener service 是在SystemUI中實(shí)現(xiàn)的。

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中,最后會調(diào)用notification listener service 的onNotificationPosted()方法沦童。下面來看一下SystemUI的這個(gè)方法:

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 End

    entry.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添加的無焦點(diǎn)小窗口偷遗。
至此墩瞳,整個(gè)過程完結(jié)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末氏豌,一起剝皮案震驚了整個(gè)濱河市喉酌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泵喘,老刑警劉巖泪电,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異纪铺,居然都是意外死亡相速,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門鲜锚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來突诬,“玉大人,你說我怎么就攤上這事芜繁⊥叮” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵骏令,是天一觀的道長蔬捷。 經(jīng)常有香客問我,道長伏社,這世上最難降的妖魔是什么抠刺? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任塔淤,我火速辦了婚禮摘昌,結(jié)果婚禮上速妖,老公的妹妹穿的比我還像新娘。我一直安慰自己聪黎,他們只是感情好罕容,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稿饰,像睡著了一般锦秒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喉镰,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天旅择,我揣著相機(jī)與錄音,去河邊找鬼侣姆。 笑死生真,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捺宗。 我是一名探鬼主播柱蟀,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蚜厉!你這毒婦竟也來了长已?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤昼牛,失蹤者是張志新(化名)和其女友劉穎术瓮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贰健,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斤斧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霎烙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撬讽。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖悬垃,靈堂內(nèi)的尸體忽然破棺而出游昼,到底是詐尸還是另有隱情,我是刑警寧澤尝蠕,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布烘豌,位于F島的核電站,受9級特大地震影響看彼,放射性物質(zhì)發(fā)生泄漏廊佩。R本人自食惡果不足惜囚聚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望标锄。 院中可真熱鬧顽铸,春花似錦、人聲如沸料皇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽践剂。三九已至鬼譬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逊脯,已是汗流浹背优质。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留军洼,地道東北人巩螃。 一個(gè)月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像歉眷,于是被迫代替她去往敵國和親牺六。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容