android notification
前言
在做应用时,时常需要在通知栏显示一条通知,那么具体流程是怎样的呢,怀着这样的探究目的,来进行一步步源码分析。
源码梳理
package com.android.server;
...
public final class SystemServer implements Dumpable {...private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {dpms = null;} else {...t.traceBegin("StartNotificationManager");mSystemServiceManager.startService(NotificationManagerService.class);SystemNotificationChannels.removeDeprecated(context);SystemNotificationChannels.createAll(context);notification = INotificationManager.Stub.asInterface(ServiceManager.getService(Context.NOTIFICATION_SERVICE));t.traceEnd();...}...}...
}
package com.android.server.notification;
...
public class NotificationManagerService extends SystemService {...@Overridepublic void onStart() {...publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);publishLocalService(NotificationManagerInternal.class, mInternalService);}...@VisibleForTestingfinal IBinder mService = new INotificationManager.Stub() {...}...
}
package com.android.server;
...
@SystemApi(client = Client.SYSTEM_SERVER)
public abstract class SystemService {...protected final void publishBinderService(String name, IBinder service,boolean allowIsolated, int dumpPriority) {ServiceManager.addService(name, service, allowIsolated, dumpPriority);}...
}
应用开发都比较熟悉,显示一条通知是调用NotificationManager
的notify
接口
package android.app;
...
@SystemService(Context.NOTIFICATION_SERVICE)
public class NotificationManager {.../*** @hide*/@UnsupportedAppUsagestatic 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) {notifyAsUser(tag, id, notification, mContext.getUser());}/*** @hide*/@UnsupportedAppUsagepublic void notifyAsUser(String tag, int id, Notification notification, UserHandle user) {INotificationManager service = getService();String pkg = mContext.getPackageName();try {if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,fixNotification(notification), user.getIdentifier());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}...
}
这里比较明朗,直接跳转至NotificationManagerService
的mService#enqueueNotificationWithTag
package com.android.server.notification;
...
public class NotificationManagerService extends SystemService {...@VisibleForTestingfinal IBinder mService = new INotificationManager.Stub() {...@Overridepublic 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) {enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,incomingUserId, false);}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, boolean postSilently) {...final StatusBarNotification n = new StatusBarNotification(pkg, opPkg, id, tag, notificationUid, callingPid, notification,user, null, System.currentTimeMillis());...final NotificationRecord r = new NotificationRecord(getContext(), n, channel);r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));r.setPostSilently(postSilently);r.setFlagBubbleRemoved(false);r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));...mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));}...
}
最终,通过Handler
调度EnqueueNotificationRunnable
package com.android.server.notification;
...
public class NotificationManagerService extends SystemService {...protected class EnqueueNotificationRunnable implements Runnable {private final NotificationRecord r;private final int userId;private final boolean isAppForeground;EnqueueNotificationRunnable(int userId, NotificationRecord r, boolean foreground) {this.userId = userId;this.r = r;this.isAppForeground = foreground;}@Overridepublic void run() {synchronized (mNotificationLock) {...// tell the assistant service about the notificationif (mAssistants.isEnabled()) {mAssistants.onNotificationEnqueuedLocked(r);mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),DELAY_FOR_ASSISTANT_TIME);} else {mHandler.post(new PostNotificationRunnable(r.getKey()));}}}}...
}
接下来是PostNotificationRunnable
package com.android.server.notification;
...
public class NotificationManagerService extends SystemService {...private NotificationListeners mListeners;...protected class PostNotificationRunnable implements Runnable {private final String key;PostNotificationRunnable(String key) {this.key = key;}@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;}}...final StatusBarNotification n = r.getSbn();final Notification notification = n.getNotification();...if (notification.getSmallIcon() != null) {StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;mListeners.notifyPostedLocked(r, old);...} else {...}...} finally {...}}}}...
}
执行到mListeners.notifyPostedLocked(r, old);
,继续跳转
package com.android.server.notification;
...
public class NotificationManagerService extends SystemService {...public class NotificationListeners extends ManagedServices {...@GuardedBy("mNotificationLock")public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) {notifyPostedLocked(r, old, true);}@GuardedBy("mNotificationLock")private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,boolean notifyAllListeners) {try {...for (final ManagedServiceInfo info : getServices()) {...final NotificationRankingUpdate update = makeRankingUpdateLocked(info);...final StatusBarNotification sbnToPost = trimCache.ForListener(info);mHandler.post(() -> notifyPosted(info, sbnToPost, update));}} catch (Exception e) {Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e);}}...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) {Slog.e(TAG, "unable to notify listener (posted): " + info, ex);}}...}...
}
走到这里,接下来就是通过aidl调用onNotificationPosted
接口
package android.service.notification;
...
public abstract class NotificationListenerService extends Service {...private Handler mHandler;...@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);mHandler = new MyHandler(getMainLooper());}...protected class NotificationListenerWrapper extends INotificationListener.Stub {@Overridepublic void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,NotificationRankingUpdate update) {StatusBarNotification sbn;...synchronized (mLock) {applyUpdateLocked(update);if (sbn != null) {SomeArgs args = SomeArgs.obtain();args.arg1 = sbn;args.arg2 = mRankingMap;mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,args).sendToTarget();} else {...}}}...}...private final class MyHandler extends Handler {public static final int MSG_ON_NOTIFICATION_POSTED = 1;...@Overridepublic void handleMessage(Message msg) {if (!isConnected) {return;}switch (msg.what) {case MSG_ON_NOTIFICATION_POSTED: {SomeArgs args = (SomeArgs) msg.obj;StatusBarNotification sbn = (StatusBarNotification) args.arg1;RankingMap rankingMap = (RankingMap) args.arg2;args.recycle();onNotificationPosted(sbn, rankingMap);}break;...}}}
}
到这里,接下来就是调用onNotificationPosted
接口,那么就要转至SystemUI的NotificationListener
中去
- 有机会单独记录下为啥是
NotificationListener
package com.android.systemui.statusbar;
...
public class NotificationListener extends NotificationListenerWithPlugins {...@Overridepublic void onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap) {if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {mMainHandler.post(() -> {processForRemoteInput(sbn.getNotification(), mContext);for (NotificationHandler handler : mNotificationHandlers) {handler.onNotificationPosted(sbn, rankingMap);}});}}...
}
再转至这里的NotificationHandler
中去
package com.android.systemui.statusbar.notification;
...
public class NotificationEntryManager implementsCommonNotifCollection,Dumpable,VisualStabilityManager.Callback {...private final NotificationHandler mNotifListener = new NotificationHandler() {@Overridepublic void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {final boolean isUpdateToInflatedNotif = mActiveNotifications.containsKey(sbn.getKey());if (isUpdateToInflatedNotif) {updateNotification(sbn, rankingMap);} else {addNotification(sbn, rankingMap);}}...}...
}
显然,接下来进入addNotification
环节
package com.android.systemui.statusbar.notification;
...
public class NotificationEntryManager implementsCommonNotifCollection,Dumpable,VisualStabilityManager.Callback {...private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;...private void addNotificationInternal(StatusBarNotification notification,RankingMap rankingMap) throws InflationException {...NotificationEntry entry = mPendingNotifications.get(key);...// Construct the expanded view.if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);}...}public void addNotification(StatusBarNotification notification, RankingMap ranking) {try {addNotificationInternal(notification, ranking);} catch (InflationException e) {handleInflationException(notification, e);}}...
}
可以看到,这里开始调用NotificationRowBinder
的inflateViews
创建通知栏控件,而NotificationRowBinder
接口的实现类是NotificationRowBinderImpl
package com.android.systemui.statusbar.notification.collection.inflation;
...
public class NotificationRowBinderImpl implements NotificationRowBinder {...private final Provider<RowInflaterTask> mRowInflaterTaskProvider;...@Overridepublic void inflateViews(NotificationEntry entry,NotificationRowContentBinder.InflationCallback callback)throws InflationException {...if (entry.rowExists()) {...} else {mIconManager.createIcons(entry);mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,row -> {// Setup the controller for the view.ExpandableNotificationRowComponent component =mExpandableNotificationRowComponentBuilder.expandableNotificationRow(row).notificationEntry(entry).onExpandClickListener(mPresenter).listContainer(mListContainer).build();ExpandableNotificationRowController rowController =component.getExpandableNotificationRowController();rowController.init(entry);entry.setRowController(rowController);bindRow(entry, row);updateRow(entry, row);inflateContentViews(entry, row, callback);});}}...
}
这里很好理解,调用RowInflaterTask
的inflate
接口,不出意外,最终会转至回调中inflateContentViews
package com.android.systemui.statusbar.notification.row;
...
public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener {...private RowInflationFinishedListener mListener;...public void inflate(Context context, ViewGroup parent, NotificationEntry entry,RowInflationFinishedListener listener) {...mListener = listener;AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);...inflater.inflate(R.layout.status_bar_notification_row, parent, this);}...@Overridepublic void onInflateFinished(View view, int resid, ViewGroup parent) {if (!mCancelled) {try {mEntry.onInflationTaskFinished();mListener.onInflationFinished((ExpandableNotificationRow) view);} catch (Throwable t) {...}}}...
}
果然,这里不出意外,返回NotificationRowBinderImpl
的inflateContentViews
package com.android.systemui.statusbar.notification.collection.inflation;
...
public class NotificationRowBinderImpl implements NotificationRowBinder {...private final RowContentBindStage mRowContentBindStage;...private void inflateContentViews(NotificationEntry entry,ExpandableNotificationRow row,@Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {...mRowContentBindStage.requestRebind(entry, en -> {...if (inflationCallback != null) {inflationCallback.onAsyncInflationFinished(en);}});}...
}
RowContentBindStage
继承自BindRequester
且未实现requestRebind
package com.android.systemui.statusbar.notification.row;
...
public abstract class BindRequester {private @Nullable BindRequestListener mBindRequestListener;public final CancellationSignal requestRebind(@NonNull NotificationEntry entry,@Nullable BindCallback callback) {CancellationSignal signal = new CancellationSignal();if (mBindRequestListener != null) {mBindRequestListener.onBindRequest(entry, signal, callback);}return signal;}final void setBindRequestListener(BindRequestListener listener) {mBindRequestListener = listener;}
}
这里的mBindRequestListener
是在NotifBindPipeline
中进行设置
package com.android.systemui.statusbar.notification.row;
...
public final class NotifBindPipeline {...private BindStage mStage;...public void setStage(BindStage stage) {mLogger.logStageSet(stage.getClass().getName());mStage = stage;mStage.setBindRequestListener(this::onBindRequested);}...private void onBindRequested(@NonNull NotificationEntry entry,@NonNull CancellationSignal signal,@Nullable BindCallback callback) {...requestPipelineRun(entry);}...
}
上面的mStage
就是RowContentBindStage
实例
- 后面专门记录下
RowContentBindStage
、BindRequester
、BindStage
以及NotifBindPipeline
之间的联系
接下来看requestPipelineRun
的实现逻辑
package com.android.systemui.statusbar.notification.row;
...
public final class NotifBindPipeline {...NotifBindPipeline(CommonNotifCollection collection,NotifBindPipelineLogger logger,@Main Looper mainLooper) {...mMainHandler = new NotifBindPipelineHandler(mainLooper);}...private void requestPipelineRun(NotificationEntry entry) {...if (!mMainHandler.hasMessages(START_PIPELINE_MSG, entry)) {Message msg = Message.obtain(mMainHandler, START_PIPELINE_MSG, entry);mMainHandler.sendMessage(msg);}}...private static final int START_PIPELINE_MSG = 1;private class NotifBindPipelineHandler extends Handler {NotifBindPipelineHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case START_PIPELINE_MSG:NotificationEntry entry = (NotificationEntry) msg.obj;startPipeline(entry);break;default:throw new IllegalArgumentException("Unknown message type: " + msg.what);}}}
}
调度调用到startPipeline
package com.android.systemui.statusbar.notification.row;
...
public final class NotifBindPipeline {...private void startPipeline(NotificationEntry entry) {...mStage.executeStage(entry, row, (en) -> onPipelineComplete(en));}...
}
上面了解到,这里的mStage
是RowContentBindStage
实例
package com.android.systemui.statusbar.notification.row;
...
public class RowContentBindStage extends BindStage<RowContentBindParams> {private final NotificationRowContentBinder mBinder;...@Overrideprotected void executeStage(@NonNull NotificationEntry entry,@NonNull ExpandableNotificationRow row,@NonNull StageCallback callback) {...mBinder.bindContent(entry, row, contentToBind, bindParams, forceInflate, inflationCallback);}...
}
这里的NotificationRowContentBinder
接口的实现类为NotificationContentInflater
package com.android.systemui.statusbar.notification.row;
...
public class NotificationContentInflater implements NotificationRowContentBinder {...@Overridepublic void bindContent(NotificationEntry entry,ExpandableNotificationRow row,@InflationFlag int contentToBind,BindParams bindParams,boolean forceInflate,@Nullable InflationCallback callback) {if (row.isRemoved()) {// We don't want to reinflate anything for removed notifications. Otherwise views might// be readded to the stack, leading to leaks. This may happen with low-priority groups// where the removal of already removed children can lead to a reinflation.return;}StatusBarNotification sbn = entry.getSbn();// To check if the notification has inline image and preload inline image if necessary.row.getImageResolver().preloadImages(sbn.getNotification());if (forceInflate) {mRemoteViewCache.clearCache(entry);}// Cancel any pending frees on any view we're trying to bind since we should be bound after.cancelContentViewFrees(row, contentToBind);AsyncInflationTask task = new AsyncInflationTask(mBgExecutor,mInflateSynchronously,contentToBind,mRemoteViewCache,entry,mConversationProcessor,row,bindParams.isLowPriority,bindParams.usesIncreasedHeight,bindParams.usesIncreasedHeadsUpHeight,callback,mRemoteInputManager.getRemoteViewsOnClickHandler(),mIsMediaInQS,mSmartReplyStateInflater);if (mInflateSynchronously) {task.onPostExecute(task.doInBackground());} else {task.executeOnExecutor(mBgExecutor);}}...
}
接下来是执行异步任务
package com.android.systemui.statusbar.notification.row;
...
public class NotificationContentInflater implements NotificationRowContentBinder {...private static CancellationSignal apply(Executor bgExecutor,boolean inflateSynchronously,InflationProgress result,@InflationFlag int reInflateFlags,NotifRemoteViewCache remoteViewCache,NotificationEntry entry,ExpandableNotificationRow row,RemoteViews.InteractionHandler remoteViewClickHandler,@Nullable InflationCallback callback) {...int flag = FLAG_CONTENT_VIEW_CONTRACTED;if ((reInflateFlags & flag) != 0) {...applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,privateLayout, privateLayout.getContractedChild(),privateLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),runningInflations, applyCallback);}flag = FLAG_CONTENT_VIEW_EXPANDED;if ((reInflateFlags & flag) != 0) {if (result.newExpandedView != null) {...applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,remoteViewCache, entry, row, isNewView, remoteViewClickHandler,callback, privateLayout, privateLayout.getExpandedChild(),privateLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,applyCallback);}}flag = FLAG_CONTENT_VIEW_HEADS_UP;if ((reInflateFlags & flag) != 0) {if (result.newHeadsUpView != null) {...applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,remoteViewCache, entry, row, isNewView, remoteViewClickHandler,callback, privateLayout, privateLayout.getHeadsUpChild(),privateLayout.getVisibleWrapper(VISIBLE_TYPE_HEADSUP), runningInflations,applyCallback);}}flag = FLAG_CONTENT_VIEW_PUBLIC;if ((reInflateFlags & flag) != 0) {...applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,publicLayout, publicLayout.getContractedChild(),publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),runningInflations, applyCallback);}...}...public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>implements InflationCallback, InflationTask {...@Overrideprotected void onPostExecute(InflationProgress result) {if (mError == null) {mCancellationSignal = apply(mBgExecutor,mInflateSynchronously,result,mReInflateFlags,mRemoteViewCache,mEntry,mRow,mRemoteViewClickHandler,this);} else {handleError(mError);}}...}...
}
可以看出,异步任务都指向了applyRemoteView
接口