Android(安卓)Notification从notify到添加view的处理流程
Android版本:8.1
创建Notification是很容易的,android8.0以后开始加入通知渠道NotificationChannel,然后在构造NotificationCompat.Builder的时候,指定要发送的渠道,最后调用NotificationManager.notify(id,notification)发送通知。
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(); final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam); ... try { service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id, copy, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } ------------------- static public INotificationManager getService() { if (sService != null) { return sService; } IBinder b = ServiceManager.getService("notification"); sService = INotificationManager.Stub.asInterface(b); return sService; }
获取了INotificationManager 远程接口对象,把Notification 给加入到队列中,
INotificationManager的远程对象是NotificationManagerService里的mservice,NotificationManagerService是系统服务。在开机启动的时候,Systemserver里和其他系统服务一起启动,最后注册到ServiceManager里。
publishBinderService(Context.NOTIFICATION_SERVICE, mService); //把service注册到Servicemanager里----private final IBinder mService = new INotificationManager.Stub(){...}
接着前面的,找到了enqueueNotificationWithTag
@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) { ....处理notification包含的信息,通知渠道,优先级。。 mHandler.post(new EnqueueNotificationRunnable(userId, r));}
把notification转换为NotificationRecord,并post给EnqueueNotificationRunnable,
EnqueueNotificationRunnable的run方法里。 @Override public void run() { synchronized (mNotificationLock) { mEnqueuedNotifications.add(r); scheduleTimeoutLocked(r); ... mHandler.post(new PostNotificationRunnable(r.getKey())); } ------------ @Override public void run() { synchronized (mNotificationLock) { try { NotificationRecord r = null; int N = mEnqueuedNotifications.size(); NotificationRecord old = mNotificationsByKey.get(key); final StatusBarNotification n = r.sbn; final Notification notification = n.getNotification(); ... if (notification.getSmallIcon() != null) { StatusBarNotification oldSbn = (old != null) ? old.sbn : null; mListeners.notifyPostedLocked(n, oldSbn); if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) { mHandler.post(new Runnable() { @Override public void run() { mGroupHelper.onNotificationPosted( n, hasAutoGroupSummaryLocked(n)); } }); } } ... buzzBeepBlinkLocked(r); } ------------ void buzzBeepBlinkLocked(NotificationRecord record) {处理notification的 声音 震动和灯光闪烁 } ------------ public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {发送给状态栏 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); } }
INotificationListener是另一个远程接口对象
protected class NotificationListenerWrapper extends INotificationListener.Stub------------@Overridepublic void onNotificationPosted(IStatusBarNotificationHolder sbnHolder, NotificationRankingUpdate update) { SomeArgs args = SomeArgs.obtain(); args.arg1 = sbn; args.arg2 = mRankingMap; mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED, args).sendToTarget(); }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);------------public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { onNotificationPosted(sbn);}public void onNotificationPosted(StatusBarNotification sbn) { // optional}
很意外,发现方法是空的,那调这么多有什么用? 研究了一下发现,onNotificationPosted的这个方法属于的是
public abstract class NotificationListenerService extends Service {
NotificationListenerService 是个抽象方法,那很自然调用的时候会调用它的子类,
然后
public class NotificationListenerWithPlugins extends NotificationListenerService
但是找了一圈NotificationListenerWithPlugins ,没有onNotificationPosted,那只有继续找它的子类了,
后来发现,在Statusbar里有个匿名内部类实现了NotificationListenerService 的方法。
private final NotificationListenerWithPlugins mNotificationListener = new NotificationListenerWithPlugins() {...@Override public void onNotificationPosted(final StatusBarNotification sbn, final RankingMap rankingMap) { mHandler.post(new Runnable() { @Override public void run() {...if (isUpdate) { updateNotification(sbn, rankingMap); } else { addNotification(sbn, rankingMap); }...} } ...}------------ public void addNotification(StatusBarNotification notification, RankingMap ranking) throws InflationException { Entry shadeEntry = createNotificationViews(notification); boolean isHeadsUped = shouldPeek(shadeEntry);. ... }------------protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) throws InflationException { NotificationData.Entry entry = new NotificationData.Entry(sbn); Dependency.get(LeakDetector.class).trackInstance(entry); entry.createIcons(mContext, sbn); // Construct the expanded view. inflateViews(entry, mStackScroller);}------------protected void inflateViews(Entry entry, ViewGroup parent) { new RowInflaterTask().inflate(mContext, parent, entry, row -> { bindRow(entry, pmUser, sbn, row); updateNotification(entry, pmUser, sbn, row); });}
最后bindRow就是去构造通知栏的通知View,然后updateNotification就是去显示到状态栏。
private void updateNotification(Entry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row) {...row.updateNotification(entry);}------------public void updateNotification(NotificationData.Entry entry) { mEntry = entry; mStatusBarNotification = entry.notification; mNotificationInflater.inflateNotificationViews();}------------public void inflateNotificationViews() { inflateNotificationViews(FLAG_REINFLATE_ALL);}------------void inflateNotificationViews(int reInflateFlags) {... StatusBarNotification sbn = mRow.getEntry().notification; new AsyncInflationTask(sbn, reInflateFlags, mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler).execute();}
使用了异步任务AsyncTask去完成布局
AsyncInflationTask@Overrideprotected InflationProgress doInBackground(Void... params) { return createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, packageContext);}------------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; return result; }
到了这里,都是创建各种布局
比如createContentView
public RemoteViews createContentView() { return createContentView(false /* increasedheight */ );}------------public RemoteViews createContentView(boolean increasedHeight) { if (mN.contentView != null && useExistingRemoteView()) { return mN.contentView; } else if (mStyle != null) { final RemoteViews styleView = mStyle.makeContentView(increasedHeight); if (styleView != null) { return styleView; } } return applyStandardTemplate(getBaseLayoutResource());}
这里会去判断我们是否有在notification里添加style, 如果有不同的style,比如音乐播放器那种notification,就是自定义style,如果没有,那就用默认的layout。
private int getBaseLayoutResource() { return R.layout.notification_template_material_base;}------------<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/status_bar_latest_event_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:tag="base" > <include layout="@layout/notification_template_header" /> <LinearLayout android:id="@+id/notification_main_column" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" android:layout_marginStart="@dimen/notification_content_margin_start" android:layout_marginEnd="@dimen/notification_content_margin_end" android:layout_marginTop="@dimen/notification_content_margin_top" android:layout_marginBottom="@dimen/notification_content_margin_bottom" android:orientation="vertical" > <include layout="@layout/notification_template_part_line1" /> <include layout="@layout/notification_template_text" /> <include android:layout_width="match_parent" android:layout_height="@dimen/notification_progress_bar_height" android:layout_marginTop="@dimen/notification_progress_margin_top" layout="@layout/notification_template_progress" /> </LinearLayout> <include layout="@layout/notification_template_right_icon" /></FrameLayout>
原来这就是我们用的notification的布局内容。
private RemoteViews applyStandardTemplate(int resId) { return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));}private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) { return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress) .fillTextsFrom(this));}private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {updateBackgroundColor(contentView); bindNotificationHeader(contentView, p.ambient); bindLargeIcon(contentView);}
^_^ 后面全是布局绘画.
基本上这就是Notification从app到Framework再到SystemUI的流程,从notify到布局。
更多相关文章
- Android::整理基础之—— startActivityForResult相关
- Android(安卓)资料整理
- Android关于获取时间的记录(小结)
- Android(安卓)studio里Toast,menu的用法
- Android(安卓)WebView加载网页,实现前进、后退、刷新、超链接
- Android(安卓)之 ListView使用SimpleAdapter展示列表
- Android(安卓)GestureDetector详解
- Android(安卓)时间显示问题
- android第一天:搭建基础环境