android notification

[复制链接]
发表于 2025-12-10 16:04:14 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
前言

在做应用时,时常须要在关照栏表现一条关照,那么详细流程是怎样的呢,怀着如许的探究目标,来举行一步步源码分析。
源码梳理
  1. package com.android.server;
  2. ...
  3. public final class SystemServer implements Dumpable {
  4.         ...
  5.     private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
  6.                 ...
  7.         if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
  8.             dpms = null;
  9.         } else {
  10.                         ...
  11.             t.traceBegin("StartNotificationManager");
  12.             mSystemServiceManager.startService(NotificationManagerService.class);
  13.             SystemNotificationChannels.removeDeprecated(context);
  14.             SystemNotificationChannels.createAll(context);
  15.             notification = INotificationManager.Stub.asInterface(
  16.                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
  17.             t.traceEnd();
  18.                         ...
  19.                 }
  20.                 ...
  21.         }
  22.         ...
  23. }
复制代码
  1. package com.android.server.notification;
  2. ...
  3. public class NotificationManagerService extends SystemService {
  4.         ...
  5.     @Override
  6.     public void onStart() {
  7.                 ...
  8.         publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
  9.                 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
  10.         publishLocalService(NotificationManagerInternal.class, mInternalService);
  11.         }
  12.         ...
  13.     @VisibleForTesting
  14.     final IBinder mService = new INotificationManager.Stub() {
  15.                 ...
  16.         }
  17.         ...
  18. }
复制代码
  1. package com.android.server;
  2. ...
  3. @SystemApi(client = Client.SYSTEM_SERVER)
  4. public abstract class SystemService {
  5.         ...
  6.     protected final void publishBinderService(String name, IBinder service,
  7.                                               boolean allowIsolated, int dumpPriority) {
  8.         ServiceManager.addService(name, service, allowIsolated, dumpPriority);
  9.     }
  10.     ...
  11. }
复制代码
应用开辟都比力认识,表现一条关照是调用NotificationManager的notify接口
  1. package android.app;
  2. ...
  3. @SystemService(Context.NOTIFICATION_SERVICE)
  4. public class NotificationManager {
  5.         ...
  6.     /**
  7.      * @hide
  8.      */
  9.     @UnsupportedAppUsage
  10.     static public INotificationManager getService() {
  11.         if (sService != null) {
  12.             return sService;
  13.         }
  14.         IBinder b = ServiceManager.getService("notification");
  15.         sService = INotificationManager.Stub.asInterface(b);
  16.         return sService;
  17.     }
  18.         ...
  19.     public void notify(int id, Notification notification) {
  20.         notify(null, id, notification);
  21.     }
  22.     public void notify(String tag, int id, Notification notification) {
  23.         notifyAsUser(tag, id, notification, mContext.getUser());
  24.     }
  25.     /**
  26.      * @hide
  27.      */
  28.     @UnsupportedAppUsage
  29.     public void notifyAsUser(String tag, int id, Notification notification, UserHandle user) {
  30.         INotificationManager service = getService();
  31.         String pkg = mContext.getPackageName();
  32.         try {
  33.             if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
  34.             service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
  35.                     fixNotification(notification), user.getIdentifier());
  36.         } catch (RemoteException e) {
  37.             throw e.rethrowFromSystemServer();
  38.         }
  39.     }
  40.         ...
  41. }
复制代码
这里比力清朗,直接跳转至NotificationManagerService的mService#enqueueNotificationWithTag
  1. package com.android.server.notification;
  2. ...
  3. public class NotificationManagerService extends SystemService {
  4.         ...
  5.     @VisibleForTesting
  6.     final IBinder mService = new INotificationManager.Stub() {
  7.                 ...
  8.         @Override
  9.         public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
  10.                                                Notification notification, int userId) throws RemoteException {
  11.             enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
  12.                     Binder.getCallingPid(), tag, id, notification, userId);
  13.         }
  14.         ...
  15.         }
  16.         ...
  17.     void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
  18.                                      final int callingPid, final String tag, final int id, final Notification notification,
  19.                                      int incomingUserId) {
  20.         enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
  21.                 incomingUserId, false);
  22.     }
  23.     void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
  24.                                      final int callingPid, final String tag, final int id, final Notification notification,
  25.                                      int incomingUserId, boolean postSilently) {
  26.                 ...
  27.         final StatusBarNotification n = new StatusBarNotification(
  28.                 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
  29.                 user, null, System.currentTimeMillis());
  30.                 ...
  31.         final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
  32.         r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
  33.         r.setPostSilently(postSilently);
  34.         r.setFlagBubbleRemoved(false);
  35.         r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));
  36.                 ...
  37.         mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
  38.         }
  39.         ...
  40. }
复制代码
终极,通过Handler调治EnqueueNotificationRunnable
  1. package com.android.server.notification;
  2. ...
  3. public class NotificationManagerService extends SystemService {
  4.         ...
  5.     protected class EnqueueNotificationRunnable implements Runnable {
  6.         private final NotificationRecord r;
  7.         private final int userId;
  8.         private final boolean isAppForeground;
  9.         EnqueueNotificationRunnable(int userId, NotificationRecord r, boolean foreground) {
  10.             this.userId = userId;
  11.             this.r = r;
  12.             this.isAppForeground = foreground;
  13.         }
  14.         @Override
  15.         public void run() {
  16.             synchronized (mNotificationLock) {
  17.                 ...
  18.                 // tell the assistant service about the notification
  19.                 if (mAssistants.isEnabled()) {
  20.                     mAssistants.onNotificationEnqueuedLocked(r);
  21.                     mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
  22.                             DELAY_FOR_ASSISTANT_TIME);
  23.                 } else {
  24.                     mHandler.post(new PostNotificationRunnable(r.getKey()));
  25.                 }
  26.             }
  27.         }
  28.         }
  29.         ...
  30. }
复制代码
接下来是PostNotificationRunnable
  1. package com.android.server.notification;
  2. ...
  3. public class NotificationManagerService extends SystemService {
  4.         ...
  5.     private NotificationListeners mListeners;
  6.         ...
  7.     protected class PostNotificationRunnable implements Runnable {
  8.         private final String key;
  9.         PostNotificationRunnable(String key) {
  10.             this.key = key;
  11.         }
  12.         @Override
  13.         public void run() {
  14.             synchronized (mNotificationLock) {
  15.                 try {
  16.                     NotificationRecord r = null;
  17.                     int N = mEnqueuedNotifications.size();
  18.                     for (int i = 0; i < N; i++) {
  19.                         final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
  20.                         if (Objects.equals(key, enqueued.getKey())) {
  21.                             r = enqueued;
  22.                             break;
  23.                         }
  24.                     }
  25.                     ...
  26.                     final StatusBarNotification n = r.getSbn();
  27.                     final Notification notification = n.getNotification();
  28.                     ...
  29.                     if (notification.getSmallIcon() != null) {
  30.                         StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
  31.                         mListeners.notifyPostedLocked(r, old);
  32.                         ...
  33.                     } else {
  34.                         ...
  35.                     }
  36.                     ...
  37.                 } finally {
  38.                     ...
  39.                 }
  40.             }
  41.         }
  42.     }
  43.         ...
  44. }
复制代码
实行到mListeners.notifyPostedLocked(r, old);,继承跳转
  1. package com.android.server.notification;
  2. ...
  3. public class NotificationManagerService extends SystemService {
  4.         ...
  5.     public class NotificationListeners extends ManagedServices {
  6.                 ...
  7.         @GuardedBy("mNotificationLock")
  8.         public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) {
  9.             notifyPostedLocked(r, old, true);
  10.         }
  11.         @GuardedBy("mNotificationLock")
  12.         private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
  13.                                         boolean notifyAllListeners) {
  14.             try {
  15.                 ...
  16.                 for (final ManagedServiceInfo info : getServices()) {
  17.                     ...
  18.                     final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
  19.                     ...
  20.                     final StatusBarNotification sbnToPost = trimCache.ForListener(info);
  21.                     mHandler.post(() -> notifyPosted(info, sbnToPost, update));
  22.                 }
  23.             } catch (Exception e) {
  24.                 Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e);
  25.             }
  26.         }
  27.                 ...
  28.         private void notifyPosted(final ManagedServiceInfo info,
  29.                                   final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
  30.             final INotificationListener listener = (INotificationListener) info.service;
  31.             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
  32.             try {
  33.                 listener.onNotificationPosted(sbnHolder, rankingUpdate);
  34.             } catch (RemoteException ex) {
  35.                 Slog.e(TAG, "unable to notify listener (posted): " + info, ex);
  36.             }
  37.         }
  38.                 ...
  39.         }
  40.         ...
  41. }
复制代码
走到这里,接下来就是通过aidl调用onNotificationPosted接口
  1. package android.service.notification;
  2. ...
  3. public abstract class NotificationListenerService extends Service {
  4.         ...
  5.     private Handler mHandler;
  6.         ...
  7.     @Override
  8.     protected void attachBaseContext(Context base) {
  9.         super.attachBaseContext(base);
  10.         mHandler = new MyHandler(getMainLooper());
  11.     }
  12.         ...
  13.     protected class NotificationListenerWrapper extends INotificationListener.Stub {
  14.                 @Override
  15.         public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
  16.                                          NotificationRankingUpdate update) {
  17.             StatusBarNotification sbn;
  18.             ...
  19.             synchronized (mLock) {
  20.                 applyUpdateLocked(update);
  21.                 if (sbn != null) {
  22.                     SomeArgs args = SomeArgs.obtain();
  23.                     args.arg1 = sbn;
  24.                     args.arg2 = mRankingMap;
  25.                     mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
  26.                             args).sendToTarget();
  27.                 } else {
  28.                     ...
  29.                 }
  30.             }
  31.         }
  32.         ...
  33.         }
  34.         ...
  35.     private final class MyHandler extends Handler {
  36.         public static final int MSG_ON_NOTIFICATION_POSTED = 1;
  37.         ...
  38.         @Override
  39.         public void handleMessage(Message msg) {
  40.             if (!isConnected) {
  41.                 return;
  42.             }
  43.             switch (msg.what) {
  44.                 case MSG_ON_NOTIFICATION_POSTED: {
  45.                     SomeArgs args = (SomeArgs) msg.obj;
  46.                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
  47.                     RankingMap rankingMap = (RankingMap) args.arg2;
  48.                     args.recycle();
  49.                     onNotificationPosted(sbn, rankingMap);
  50.                 }
  51.                 break;
  52.                 ...
  53.                         }
  54.                 }
  55.         }
  56. }
复制代码
到这里,接下来就是调用onNotificationPosted接口,那么就要转至SystemUI的NotificationListener中去

  • 有时机单独纪录下为啥是NotificationListener
  1. package com.android.systemui.statusbar;
  2. ...
  3. public class NotificationListener extends NotificationListenerWithPlugins {
  4.         ...
  5.     @Override
  6.     public void onNotificationPosted(final StatusBarNotification sbn,
  7.             final RankingMap rankingMap) {
  8.         if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
  9.         if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
  10.             mMainHandler.post(() -> {
  11.                 processForRemoteInput(sbn.getNotification(), mContext);
  12.                 for (NotificationHandler handler : mNotificationHandlers) {
  13.                     handler.onNotificationPosted(sbn, rankingMap);
  14.                 }
  15.             });
  16.         }
  17.     }
  18.         ...
  19. }
复制代码
再转至这里的NotificationHandler中去
  1. package com.android.systemui.statusbar.notification;
  2. ...
  3. public class NotificationEntryManager implements
  4.         CommonNotifCollection,
  5.         Dumpable,
  6.         VisualStabilityManager.Callback {
  7.         ...
  8.     private final NotificationHandler mNotifListener = new NotificationHandler() {
  9.         @Override
  10.         public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
  11.             final boolean isUpdateToInflatedNotif = mActiveNotifications.containsKey(sbn.getKey());
  12.             if (isUpdateToInflatedNotif) {
  13.                 updateNotification(sbn, rankingMap);
  14.             } else {
  15.                 addNotification(sbn, rankingMap);
  16.             }
  17.         }
  18.         ...
  19.         }
  20.         ...
  21. }
复制代码
显然,接下来进入addNotification环节
  1. package com.android.systemui.statusbar.notification;
  2. ...
  3. public class NotificationEntryManager implements
  4.         CommonNotifCollection,
  5.         Dumpable,
  6.         VisualStabilityManager.Callback {
  7.         ...
  8.     private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
  9.         ...
  10.     private void addNotificationInternal(
  11.             StatusBarNotification notification,
  12.             RankingMap rankingMap) throws InflationException {
  13.         ...
  14.         NotificationEntry entry = mPendingNotifications.get(key);
  15.         ...
  16.         // Construct the expanded view.
  17.         if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
  18.             mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
  19.         }
  20.         ...
  21.     }
  22.     public void addNotification(StatusBarNotification notification, RankingMap ranking) {
  23.         try {
  24.             addNotificationInternal(notification, ranking);
  25.         } catch (InflationException e) {
  26.             handleInflationException(notification, e);
  27.         }
  28.     }
  29.         ...
  30. }
复制代码
可以看到,这里开始调用NotificationRowBinder的inflateViews创建关照栏控件,而NotificationRowBinder接口的实现类是NotificationRowBinderImpl
  1. package com.android.systemui.statusbar.notification.collection.inflation;
  2. ...
  3. public class NotificationRowBinderImpl implements NotificationRowBinder {
  4.         ...
  5.     private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
  6.         ...
  7.     @Override
  8.     public void inflateViews(
  9.             NotificationEntry entry,
  10.             NotificationRowContentBinder.InflationCallback callback)
  11.             throws InflationException {
  12.                 ...
  13.         if (entry.rowExists()) {
  14.             ...
  15.         } else {
  16.             mIconManager.createIcons(entry);
  17.             mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
  18.                     row -> {
  19.                         // Setup the controller for the view.
  20.                         ExpandableNotificationRowComponent component =
  21.                                 mExpandableNotificationRowComponentBuilder
  22.                                         .expandableNotificationRow(row)
  23.                                         .notificationEntry(entry)
  24.                                         .onExpandClickListener(mPresenter)
  25.                                         .listContainer(mListContainer)
  26.                                         .build();
  27.                         ExpandableNotificationRowController rowController =
  28.                                 component.getExpandableNotificationRowController();
  29.                         rowController.init(entry);
  30.                         entry.setRowController(rowController);
  31.                         bindRow(entry, row);
  32.                         updateRow(entry, row);
  33.                         inflateContentViews(entry, row, callback);
  34.                     });
  35.         }
  36.         }
  37.         ...
  38. }
复制代码
这里很好明白,调用RowInflaterTask的inflate接口,不出不测,终极会转至回调中inflateContentViews
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener {
  4.         ...
  5.     private RowInflationFinishedListener mListener;
  6.         ...
  7.     public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
  8.             RowInflationFinishedListener listener) {
  9.         ...
  10.         mListener = listener;
  11.         AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
  12.         ...
  13.         inflater.inflate(R.layout.status_bar_notification_row, parent, this);
  14.     }
  15.         ...
  16.     @Override
  17.     public void onInflateFinished(View view, int resid, ViewGroup parent) {
  18.         if (!mCancelled) {
  19.             try {
  20.                 mEntry.onInflationTaskFinished();
  21.                 mListener.onInflationFinished((ExpandableNotificationRow) view);
  22.             } catch (Throwable t) {
  23.                 ...
  24.             }
  25.         }
  26.     }
  27.         ...
  28. }
复制代码
果然,这里不出不测,返回NotificationRowBinderImpl的inflateContentViews
  1. package com.android.systemui.statusbar.notification.collection.inflation;
  2. ...
  3. public class NotificationRowBinderImpl implements NotificationRowBinder {
  4.         ...
  5.     private final RowContentBindStage mRowContentBindStage;
  6.         ...
  7.     private void inflateContentViews(
  8.             NotificationEntry entry,
  9.             ExpandableNotificationRow row,
  10.             @Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
  11.         ...
  12.         mRowContentBindStage.requestRebind(entry, en -> {
  13.             ...
  14.             if (inflationCallback != null) {
  15.                 inflationCallback.onAsyncInflationFinished(en);
  16.             }
  17.         });
  18.     }
  19.         ...
  20. }
复制代码
RowContentBindStage继承自BindRequester且未实现requestRebind
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public abstract class BindRequester {
  4.     private @Nullable BindRequestListener mBindRequestListener;
  5.     public final CancellationSignal requestRebind(
  6.             @NonNull NotificationEntry entry,
  7.             @Nullable BindCallback callback) {
  8.         CancellationSignal signal = new CancellationSignal();
  9.         if (mBindRequestListener != null) {
  10.             mBindRequestListener.onBindRequest(entry, signal, callback);
  11.         }
  12.         return signal;
  13.     }
  14.     final void setBindRequestListener(BindRequestListener listener) {
  15.         mBindRequestListener = listener;
  16.     }
  17. }
复制代码
这里的mBindRequestListener是在NotifBindPipeline中举行设置
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public final class NotifBindPipeline {
  4.         ...
  5.     private BindStage mStage;
  6.     ...
  7.     public void setStage(
  8.             BindStage stage) {
  9.         mLogger.logStageSet(stage.getClass().getName());
  10.         mStage = stage;
  11.         mStage.setBindRequestListener(this::onBindRequested);
  12.     }
  13.     ...
  14.     private void onBindRequested(
  15.             @NonNull NotificationEntry entry,
  16.             @NonNull CancellationSignal signal,
  17.             @Nullable BindCallback callback) {
  18.         ...
  19.         requestPipelineRun(entry);
  20.     }
  21.     ...
  22. }
复制代码
上面的mStage就是RowContentBindStage实例

  • 背面专门纪录下RowContentBindStage、BindRequester、BindStage以及NotifBindPipeline之间的接洽
接下来看requestPipelineRun的实现逻辑
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public final class NotifBindPipeline {
  4.         ...
  5.     NotifBindPipeline(
  6.             CommonNotifCollection collection,
  7.             NotifBindPipelineLogger logger,
  8.             @Main Looper mainLooper) {
  9.         ...
  10.         mMainHandler = new NotifBindPipelineHandler(mainLooper);
  11.     }
  12.         ...
  13.     private void requestPipelineRun(NotificationEntry entry) {
  14.         ...
  15.         if (!mMainHandler.hasMessages(START_PIPELINE_MSG, entry)) {
  16.             Message msg = Message.obtain(mMainHandler, START_PIPELINE_MSG, entry);
  17.             mMainHandler.sendMessage(msg);
  18.         }
  19.     }
  20.     ...
  21.     private static final int START_PIPELINE_MSG = 1;
  22.     private class NotifBindPipelineHandler extends Handler {
  23.         NotifBindPipelineHandler(Looper looper) {
  24.             super(looper);
  25.         }
  26.         @Override
  27.         public void handleMessage(Message msg) {
  28.             switch (msg.what) {
  29.                 case START_PIPELINE_MSG:
  30.                     NotificationEntry entry = (NotificationEntry) msg.obj;
  31.                     startPipeline(entry);
  32.                     break;
  33.                 default:
  34.                     throw new IllegalArgumentException("Unknown message type: " + msg.what);
  35.             }
  36.         }
  37.     }
  38. }
复制代码
调治调用到startPipeline
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public final class NotifBindPipeline {
  4.         ...
  5.     private void startPipeline(NotificationEntry entry) {
  6.         ...
  7.         mStage.executeStage(entry, row, (en) -> onPipelineComplete(en));
  8.     }
  9.     ...
  10. }
复制代码
上面相识到,这里的mStage是RowContentBindStage实例
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public class RowContentBindStage extends BindStage<RowContentBindParams> {
  4.     private final NotificationRowContentBinder mBinder;
  5.         ...
  6.     @Override
  7.     protected void executeStage(
  8.             @NonNull NotificationEntry entry,
  9.             @NonNull ExpandableNotificationRow row,
  10.             @NonNull StageCallback callback) {
  11.         ...
  12.         mBinder.bindContent(entry, row, contentToBind, bindParams, forceInflate, inflationCallback);
  13.     }
  14.         ...
  15. }
复制代码
这里的NotificationRowContentBinder接口的实现类为NotificationContentInflater
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public class NotificationContentInflater implements NotificationRowContentBinder {
  4.         ...
  5.     @Override
  6.     public void bindContent(
  7.             NotificationEntry entry,
  8.             ExpandableNotificationRow row,
  9.             @InflationFlag int contentToBind,
  10.             BindParams bindParams,
  11.             boolean forceInflate,
  12.             @Nullable InflationCallback callback) {
  13.         if (row.isRemoved()) {
  14.             // We don't want to reinflate anything for removed notifications. Otherwise views might
  15.             // be readded to the stack, leading to leaks. This may happen with low-priority groups
  16.             // where the removal of already removed children can lead to a reinflation.
  17.             return;
  18.         }
  19.         StatusBarNotification sbn = entry.getSbn();
  20.         // To check if the notification has inline image and preload inline image if necessary.
  21.         row.getImageResolver().preloadImages(sbn.getNotification());
  22.         if (forceInflate) {
  23.             mRemoteViewCache.clearCache(entry);
  24.         }
  25.         // Cancel any pending frees on any view we're trying to bind since we should be bound after.
  26.         cancelContentViewFrees(row, contentToBind);
  27.         AsyncInflationTask task = new AsyncInflationTask(
  28.                 mBgExecutor,
  29.                 mInflateSynchronously,
  30.                 contentToBind,
  31.                 mRemoteViewCache,
  32.                 entry,
  33.                 mConversationProcessor,
  34.                 row,
  35.                 bindParams.isLowPriority,
  36.                 bindParams.usesIncreasedHeight,
  37.                 bindParams.usesIncreasedHeadsUpHeight,
  38.                 callback,
  39.                 mRemoteInputManager.getRemoteViewsOnClickHandler(),
  40.                 mIsMediaInQS,
  41.                 mSmartReplyStateInflater);
  42.         if (mInflateSynchronously) {
  43.             task.onPostExecute(task.doInBackground());
  44.         } else {
  45.             task.executeOnExecutor(mBgExecutor);
  46.         }
  47.     }
  48.         ...
  49. }
复制代码
接下来是实行异步使命
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public class NotificationContentInflater implements NotificationRowContentBinder {
  4.         ...
  5.     private static CancellationSignal apply(
  6.             Executor bgExecutor,
  7.             boolean inflateSynchronously,
  8.             InflationProgress result,
  9.             @InflationFlag int reInflateFlags,
  10.             NotifRemoteViewCache remoteViewCache,
  11.             NotificationEntry entry,
  12.             ExpandableNotificationRow row,
  13.             RemoteViews.InteractionHandler remoteViewClickHandler,
  14.             @Nullable InflationCallback callback) {
  15.         ...
  16.         int flag = FLAG_CONTENT_VIEW_CONTRACTED;
  17.         if ((reInflateFlags & flag) != 0) {
  18.             ...
  19.             applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
  20.                     remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
  21.                     privateLayout,  privateLayout.getContractedChild(),
  22.                     privateLayout.getVisibleWrapper(
  23.                             NotificationContentView.VISIBLE_TYPE_CONTRACTED),
  24.                     runningInflations, applyCallback);
  25.         }
  26.         flag = FLAG_CONTENT_VIEW_EXPANDED;
  27.         if ((reInflateFlags & flag) != 0) {
  28.             if (result.newExpandedView != null) {
  29.                 ...
  30.                 applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
  31.                         remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
  32.                         callback, privateLayout, privateLayout.getExpandedChild(),
  33.                         privateLayout.getVisibleWrapper(
  34.                                 NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
  35.                         applyCallback);
  36.             }
  37.         }
  38.         flag = FLAG_CONTENT_VIEW_HEADS_UP;
  39.         if ((reInflateFlags & flag) != 0) {
  40.             if (result.newHeadsUpView != null) {
  41.                 ...
  42.                 applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
  43.                         remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
  44.                         callback, privateLayout, privateLayout.getHeadsUpChild(),
  45.                         privateLayout.getVisibleWrapper(
  46.                                 VISIBLE_TYPE_HEADSUP), runningInflations,
  47.                         applyCallback);
  48.             }
  49.         }
  50.         flag = FLAG_CONTENT_VIEW_PUBLIC;
  51.         if ((reInflateFlags & flag) != 0) {
  52.             ...
  53.             applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
  54.                     remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
  55.                     publicLayout, publicLayout.getContractedChild(),
  56.                     publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
  57.                     runningInflations, applyCallback);
  58.         }
  59.         ...
  60.     }
  61.         ...
  62.     public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
  63.             implements InflationCallback, InflationTask {
  64.                 ...
  65.         @Override
  66.         protected void onPostExecute(InflationProgress result) {
  67.             if (mError == null) {
  68.                 mCancellationSignal = apply(
  69.                         mBgExecutor,
  70.                         mInflateSynchronously,
  71.                         result,
  72.                         mReInflateFlags,
  73.                         mRemoteViewCache,
  74.                         mEntry,
  75.                         mRow,
  76.                         mRemoteViewClickHandler,
  77.                         this);
  78.             } else {
  79.                 handleError(mError);
  80.             }
  81.         }
  82.                 ...
  83.         }
  84.         ...
  85. }
复制代码
可以看出,异步使命都指向了applyRemoteView接口
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public class NotificationContentInflater implements NotificationRowContentBinder {
  4.         ...
  5.     @VisibleForTesting
  6.     static void applyRemoteView(
  7.             Executor bgExecutor,
  8.             boolean inflateSynchronously,
  9.             final InflationProgress result,
  10.             final @InflationFlag int reInflateFlags,
  11.             @InflationFlag int inflationId,
  12.             final NotifRemoteViewCache remoteViewCache,
  13.             final NotificationEntry entry,
  14.             final ExpandableNotificationRow row,
  15.             boolean isNewView,
  16.             RemoteViews.InteractionHandler remoteViewClickHandler,
  17.             @Nullable final InflationCallback callback,
  18.             NotificationContentView parentLayout,
  19.             View existingView,
  20.             NotificationViewWrapper existingWrapper,
  21.             final HashMap<Integer, CancellationSignal> runningInflations,
  22.             ApplyCallback applyCallback) {
  23.         RemoteViews newContentView = applyCallback.getRemoteView();
  24.         if (inflateSynchronously) {
  25.             try {
  26.                 if (isNewView) {
  27.                     View v = newContentView.apply(
  28.                             result.packageContext,
  29.                             parentLayout,
  30.                             remoteViewClickHandler);
  31.                     v.setIsRootNamespace(true);
  32.                     applyCallback.setResultView(v);
  33.                 } else {
  34.                     newContentView.reapply(
  35.                             result.packageContext,
  36.                             existingView,
  37.                             remoteViewClickHandler);
  38.                     existingWrapper.onReinflated();
  39.                 }
  40.             } catch (Exception e) {
  41.                 handleInflationError(runningInflations, e, row.getEntry(), callback);
  42.                 // Add a running inflation to make sure we don't trigger callbacks.
  43.                 // Safe to do because only happens in tests.
  44.                 runningInflations.put(inflationId, new CancellationSignal());
  45.             }
  46.             return;
  47.         }
  48.         RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() {
  49.             @Override
  50.             public void onViewInflated(View v) {
  51.                 if (v instanceof ImageMessageConsumer) {
  52.                     ((ImageMessageConsumer) v).setImageResolver(row.getImageResolver());
  53.                 }
  54.             }
  55.             @Override
  56.             public void onViewApplied(View v) {
  57.                 if (isNewView) {
  58.                     v.setIsRootNamespace(true);
  59.                     applyCallback.setResultView(v);
  60.                 } else if (existingWrapper != null) {
  61.                     existingWrapper.onReinflated();
  62.                 }
  63.                 runningInflations.remove(inflationId);
  64.                 finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations,
  65.                         callback, entry, row);
  66.             }
  67.             @Override
  68.             public void onError(Exception e) {
  69.                 // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could
  70.                 // actually also be a system issue, so let's try on the UI thread again to be safe.
  71.                 try {
  72.                     View newView = existingView;
  73.                     if (isNewView) {
  74.                         newView = newContentView.apply(
  75.                                 result.packageContext,
  76.                                 parentLayout,
  77.                                 remoteViewClickHandler);
  78.                     } else {
  79.                         newContentView.reapply(
  80.                                 result.packageContext,
  81.                                 existingView,
  82.                                 remoteViewClickHandler);
  83.                     }
  84.                     Log.wtf(TAG, "Async Inflation failed but normal inflation finished normally.",
  85.                             e);
  86.                     onViewApplied(newView);
  87.                 } catch (Exception anotherException) {
  88.                     runningInflations.remove(inflationId);
  89.                     handleInflationError(runningInflations, e, row.getEntry(),
  90.                             callback);
  91.                 }
  92.             }
  93.         };
  94.         CancellationSignal cancellationSignal;
  95.         if (isNewView) {
  96.             cancellationSignal = newContentView.applyAsync(
  97.                     result.packageContext,
  98.                     parentLayout,
  99.                     bgExecutor,
  100.                     listener,
  101.                     remoteViewClickHandler);
  102.         } else {
  103.             cancellationSignal = newContentView.reapplyAsync(
  104.                     result.packageContext,
  105.                     existingView,
  106.                     bgExecutor,
  107.                     listener,
  108.                     remoteViewClickHandler);
  109.         }
  110.         runningInflations.put(inflationId, cancellationSignal);
  111.     }
  112.         ...
  113. }
复制代码
这里是要转调RemoteViews的applyAsync接口,而且传入了OnViewAppliedListener回调监听;我们知道RemoteViews是可以在差别线程或进程中创建和更新UI的组件,而且,在调用关照接口时,传入的自界说的结构参数也是RemoteViews
  1. package android.app;
  2. ...
  3. public class Notification implements Parcelable {
  4.         ...
  5.     public static class Builder {
  6.                 ...
  7.         @Deprecated
  8.         public Builder setContent(RemoteViews views) {
  9.             return setCustomContentView(views);
  10.         }
  11.         @NonNull
  12.         public Builder setCustomContentView(RemoteViews contentView) {
  13.             mN.contentView = contentView;
  14.             return this;
  15.         }
  16.                 ...
  17.         }
  18.         ...
  19. }
复制代码
那么就再来看下RemoteViews的applyAsync接口
  1. package android.widget;
  2. ...
  3. public class RemoteViews implements Parcelable, Filter {
  4.         ...
  5.     public CancellationSignal applyAsync(Context context, ViewGroup parent,
  6.                                          Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
  7.         return applyAsync(context, parent, executor, listener, handler, null /* size */);
  8.     }
  9.     public CancellationSignal applyAsync(Context context, ViewGroup parent,
  10.                                          Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
  11.                                          SizeF size) {
  12.         return applyAsync(context, parent, executor, listener, handler, size,
  13.                 null /* themeColors */);
  14.     }
  15.     public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
  16.                                          OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
  17.                                          ColorResources colorResources) {
  18.         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
  19.                 handler, colorResources, null /* result */,
  20.                 true /* topLevel */).startTaskOnExecutor(executor);
  21.     }
  22.         ...
  23.     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
  24.             implements CancellationSignal.OnCancelListener {
  25.                 ...
  26.         final OnViewAppliedListener mListener;
  27.                 ...
  28.         @Override
  29.         protected void onPostExecute(ViewTree viewTree) {
  30.             ...
  31.             if (mListener != null) {
  32.                 if (mError != null) {
  33.                     mListener.onError(mError);
  34.                 } else {
  35.                     mListener.onViewApplied(viewTree.mRoot);
  36.                 }
  37.             } else if (mError != null) {
  38.                 ...
  39.             }
  40.         }
  41.                 ...
  42.         }
  43.         ...
  44. }
复制代码
好了,这里开始走OnViewAppliedListener回调的onViewApplied接口
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public class NotificationContentInflater implements NotificationRowContentBinder {
  4.         ...
  5.     @VisibleForTesting
  6.     static void applyRemoteView(
  7.             Executor bgExecutor,
  8.             boolean inflateSynchronously,
  9.             final InflationProgress result,
  10.             final @InflationFlag int reInflateFlags,
  11.             @InflationFlag int inflationId,
  12.             final NotifRemoteViewCache remoteViewCache,
  13.             final NotificationEntry entry,
  14.             final ExpandableNotificationRow row,
  15.             boolean isNewView,
  16.             RemoteViews.InteractionHandler remoteViewClickHandler,
  17.             @Nullable final InflationCallback callback,
  18.             NotificationContentView parentLayout,
  19.             View existingView,
  20.             NotificationViewWrapper existingWrapper,
  21.             final HashMap<Integer, CancellationSignal> runningInflations,
  22.             ApplyCallback applyCallback) {
  23.         RemoteViews newContentView = applyCallback.getRemoteView();
  24.         ...
  25.         RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() {
  26.             ...
  27.             @Override
  28.             public void onViewApplied(View v) {
  29.                 if (isNewView) {
  30.                     v.setIsRootNamespace(true);
  31.                     applyCallback.setResultView(v);
  32.                 } else if (existingWrapper != null) {
  33.                     existingWrapper.onReinflated();
  34.                 }
  35.                 runningInflations.remove(inflationId);
  36.                 finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations,
  37.                         callback, entry, row);
  38.             }
  39.             ...
  40.         };
  41.         ...
  42.     }
  43.         ...
  44.     private static boolean finishIfDone(InflationProgress result,
  45.             @InflationFlag int reInflateFlags, NotifRemoteViewCache remoteViewCache,
  46.             HashMap<Integer, CancellationSignal> runningInflations,
  47.             @Nullable InflationCallback endListener, NotificationEntry entry,
  48.             ExpandableNotificationRow row) {
  49.         ...
  50.         if (runningInflations.isEmpty()) {
  51.             ...
  52.             if (endListener != null) {
  53.                 endListener.onAsyncInflationFinished(entry);
  54.             }
  55.             return true;
  56.         }
  57.         return false;
  58.     }
  59.         ...
  60. }
复制代码
在finishIfDone中,末了回调InflationCallback的onAsyncInflationFinished,而这个回调就是AsyncInflationTask
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public class NotificationContentInflater implements NotificationRowContentBinder {
  4.         ...
  5.     public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
  6.             implements InflationCallback, InflationTask {
  7.                 ...
  8.         private final InflationCallback mCallback;
  9.                 ...
  10.         @Override
  11.         public void onAsyncInflationFinished(NotificationEntry entry) {
  12.             mEntry.onInflationTaskFinished();
  13.             mRow.onNotificationUpdated();
  14.             if (mCallback != null) {
  15.                 mCallback.onAsyncInflationFinished(mEntry);
  16.             }
  17.             // Notify the resolver that the inflation task has finished,
  18.             // try to purge unnecessary cached entries.
  19.             mRow.getImageResolver().purgeCache();
  20.         }
  21.                 ...
  22.         }
  23.         ...
  24. }
复制代码
回溯一下之前的回调
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public class RowContentBindStage extends BindStage<RowContentBindParams> {
  4.     private final NotificationRowContentBinder mBinder;
  5.         ...
  6.     @Override
  7.     protected void executeStage(
  8.             @NonNull NotificationEntry entry,
  9.             @NonNull ExpandableNotificationRow row,
  10.             @NonNull StageCallback callback) {
  11.         ...
  12.         InflationCallback inflationCallback = new InflationCallback() {
  13.             @Override
  14.             public void handleInflationException(NotificationEntry entry, Exception e) {
  15.                 mNotifInflationErrorManager.setInflationError(entry, e);
  16.             }
  17.             @Override
  18.             public void onAsyncInflationFinished(NotificationEntry entry) {
  19.                 mNotifInflationErrorManager.clearInflationError(entry);
  20.                 getStageParams(entry).clearDirtyContentViews();
  21.                 callback.onStageFinished(entry);
  22.             }
  23.         };
  24.         mBinder.cancelBind(entry, row);
  25.         mBinder.bindContent(entry, row, contentToBind, bindParams, forceInflate, inflationCallback);
  26.     }
  27.         ...
  28. }
复制代码
继承回溯callback.onStageFinished
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public final class NotifBindPipeline {
  4.         ...
  5.     private final List<BindCallback> mScratchCallbacksList = new ArrayList<>();
  6.         ...
  7.     private void startPipeline(NotificationEntry entry) {
  8.         ...
  9.         mStage.executeStage(entry, row, (en) -> onPipelineComplete(en));
  10.     }
  11.     private void onPipelineComplete(NotificationEntry entry) {
  12.         final BindEntry bindEntry = getBindEntry(entry);
  13.         final Set<BindCallback> callbacks = bindEntry.callbacks;
  14.         mLogger.logFinishedPipeline(entry.getKey(), callbacks.size());
  15.         bindEntry.invalidated = false;
  16.         // Move all callbacks to separate list as callbacks may themselves add/remove callbacks.
  17.         // TODO: Throw an exception for this re-entrant behavior once we deprecate
  18.         // NotificationGroupAlertTransferHelper
  19.         mScratchCallbacksList.addAll(callbacks);
  20.         callbacks.clear();
  21.         for (int i = 0; i < mScratchCallbacksList.size(); i++) {
  22.             mScratchCallbacksList.get(i).onBindFinished(entry);
  23.         }
  24.         mScratchCallbacksList.clear();
  25.     }
  26.     ...
  27. }
复制代码
接下来回溯BindCallback的onBindFinished
  1. package com.android.systemui.statusbar.notification.row;
  2. ...
  3. public final class NotifBindPipeline {
  4.         ...
  5.     private void onBindRequested(
  6.             @NonNull NotificationEntry entry,
  7.             @NonNull CancellationSignal signal,
  8.             @Nullable BindCallback callback) {
  9.         final BindEntry bindEntry = getBindEntry(entry);
  10.         if (bindEntry == null) {
  11.             // Invalidating views for a notification that is not active.
  12.             return;
  13.         }
  14.         bindEntry.invalidated = true;
  15.         // Put in new callback.
  16.         if (callback != null) {
  17.             final Set<BindCallback> callbacks = bindEntry.callbacks;
  18.             callbacks.add(callback);
  19.             signal.setOnCancelListener(() -> callbacks.remove(callback));
  20.         }
  21.         requestPipelineRun(entry);
  22.     }
  23.     ...
  24. }
复制代码
  1. package com.android.systemui.statusbar.notification.collection.inflation;
  2. ...
  3. public class NotificationRowBinderImpl implements NotificationRowBinder {
  4.         ...
  5.     private final RowContentBindStage mRowContentBindStage;
  6.         ...
  7.     private void inflateContentViews(
  8.             NotificationEntry entry,
  9.             ExpandableNotificationRow row,
  10.             @Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
  11.         ...
  12.         mRowContentBindStage.requestRebind(entry, en -> {
  13.             ...
  14.             if (inflationCallback != null) {
  15.                 inflationCallback.onAsyncInflationFinished(en);
  16.             }
  17.         });
  18.     }
  19.         ...
  20. }
复制代码
也就是要继承回溯inflationCallback.onAsyncInflationFinished(en)
  1. package com.android.systemui.statusbar.notification;
  2. ...
  3. public class NotificationEntryManager implements
  4.         CommonNotifCollection,
  5.         Dumpable,
  6.         VisualStabilityManager.Callback {
  7.         ...
  8.     private NotificationPresenter mPresenter;
  9.         ...
  10.     private final InflationCallback mInflationCallback = new InflationCallback() {
  11.         ...
  12.         @Override
  13.         public void onAsyncInflationFinished(NotificationEntry entry) {
  14.             ...
  15.             if (!entry.isRowRemoved()) {
  16.                 ...
  17.                 if (isNew) {
  18.                     for (NotificationEntryListener listener : mNotificationEntryListeners) {
  19.                         listener.onEntryInflated(entry);
  20.                     }
  21.                     addActiveNotification(entry);
  22.                     updateNotifications("onAsyncInflationFinished");
  23.                     for (NotificationEntryListener listener : mNotificationEntryListeners) {
  24.                         listener.onNotificationAdded(entry);
  25.                     }
  26.                 } else {
  27.                     for (NotificationEntryListener listener : mNotificationEntryListeners) {
  28.                         listener.onEntryReinflated(entry);
  29.                     }
  30.                 }
  31.             }
  32.         }
  33.     };
  34.         ...
  35.     private void addNotificationInternal(
  36.             StatusBarNotification notification,
  37.             RankingMap rankingMap) throws InflationException {
  38.         ...
  39.         // Construct the expanded view.
  40.         if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
  41.             mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
  42.         }
  43.         ...
  44.     }
  45.         ...
  46.     public void updateNotifications(String reason) {
  47.         reapplyFilterAndSort(reason);
  48.         if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
  49.             mPresenter.updateNotificationViews(reason);
  50.         }
  51.     }
  52.         ...
  53. }
复制代码
这里的NotificationPresenter接口实现类为StatusBarNotificationPresenter
  1. package com.android.systemui.statusbar.phone;
  2. ...
  3. public class StatusBarNotificationPresenter implements NotificationPresenter,
  4.         ConfigurationController.ConfigurationListener,
  5.         NotificationRowBinderImpl.BindRowCallback,
  6.         CommandQueue.Callbacks {
  7.         ...
  8.     private final NotificationViewHierarchyManager mViewHierarchyManager;
  9.         ...
  10.     private final NotificationPanelViewController mNotificationPanel;
  11.         ...
  12.     @Override
  13.     public void updateNotificationViews(final String reason) {
  14.         ...
  15.         mViewHierarchyManager.updateNotificationViews();
  16.         mNotificationPanel.updateNotificationViews(reason);
  17.     }
  18.         ...
  19. }
复制代码
这里显然是开始调用更新关照栏接口了
  1. package com.android.systemui.statusbar;
  2. ...
  3. public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
  4.         ...
  5.     private NotificationListContainer mListContainer;
  6.         ...
  7.     public void updateNotificationViews() {
  8.                 ...
  9.         ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
  10.         ...
  11.         for (int i = 0; i < toShow.size(); i++) {
  12.             View v = toShow.get(i);
  13.             if (v.getParent() == null) {
  14.                 mVisualStabilityManager.notifyViewAddition(v);
  15.                 mListContainer.addContainerView(v);
  16.             } else if (!mListContainer.containsView(v)) {
  17.                 // the view is added somewhere else. Let's make sure
  18.                 // the ordering works properly below, by excluding these
  19.                 toShow.remove(v);
  20.                 i--;
  21.             }
  22.         }
  23.         ...
  24.         }
  25.         ...
  26. }
复制代码
不难明白,这里开始添加关照栏控件了
  1. package com.android.systemui.statusbar.notification.stack;
  2. ...
  3. public class NotificationStackScrollLayoutController {
  4.         ...
  5.     private NotificationStackScrollLayout mView;
  6.     ...
  7.     private class NotificationListContainerImpl implements NotificationListContainer {
  8.                 ...
  9.         @Override
  10.         public void addContainerView(View v) {
  11.             mView.addContainerView(v);
  12.         }
  13.                 ...
  14.         }
  15.         ...
  16. }
复制代码
  1. package com.android.systemui.statusbar.notification.stack;
  2. ...
  3. public class NotificationStackScrollLayout extends ViewGroup implements Dumpable {
  4.         ...
  5.     public void addContainerView(View v) {
  6.         Assert.isMainThread();
  7.         addView(v);
  8.         if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
  9.             mController.updateShowEmptyShadeView();
  10.             updateFooter();
  11.         }
  12.         updateSpeedBumpIndex();
  13.     }
  14.     ...
  15. }
复制代码
自此,关照栏添加一条关照的流程算是简单梳理完成
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表