Android Q notification创建发送流程-SystemUI篇

Android10 notification创建发送流程-framework篇分析了通知是如何从framework发送到NotificationListenerServiceonNotificationPosted方法的,这个方法其实并没有具体实现,需要子类实现具体逻辑,这篇文章接着上一篇分析SystemUI部分通知的处理流程。

NotificationListenerWithPlugins继承了NotificationListenerService类
onNotificationPosted的具体实现在NotificationListenerWithPlugins子类NotificationListener

NotificationListener

NotificationListener这个类对NotificationManagerService的onNotificationPosted方法做了具体实现
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java

public class NotificationListener extends NotificationListenerWithPlugins {
     ......
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
            //相当于handler.post,"main_handler",post到主线程执行
            Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
                processForRemoteInput(sbn.getNotification(), mContext);
                String key = sbn.getKey();
                boolean isUpdate =
                        mEntryManager.getNotificationData().get(key) != null;        
                    ......
                //判断到来的通知是需要更新还是添加
                if (isUpdate) {
                    mEntryManager.updateNotification(sbn, rankingMap);
                } else {
                    mEntryManager.addNotification(sbn, rankingMap);
                }
            });
        }
    }
       ......
}

主要来看下isUpdate
这里初始化NotificationEntryManager对象时用到了Dagger框架的懒加载Lazy和依赖注入@Inject,具体的没去研究过不清楚,我们只需要知道就是创建了NotificationEntryManager对象就行了,这里以key为键获取NotificationData的值,key就是之前分析的StatusBarNotification的key方法,对一条通知唯一标识

private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class);
boolean isUpdate = mEntryManager.getNotificationData().get(key) != null;

我们第一次创建通知的时候get到的getNotificationData一定是null,因为还没有放进去,所以isUpdate = false,走添加通知的流程

addNotification

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java

    @Override
    public void addNotification(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) {
        try {
            addNotificationInternal(notification, ranking);
        } catch (InflationException e) {
            handleInflationException(notification, e);
        }
    }
    private void addNotificationInternal(StatusBarNotification notification,
            NotificationListenerService.RankingMap rankingMap) throws InflationException {
        //获取唯一标识key,key打印出来是这样的:0|com.example.app3|110|null|10161
        String key = notification.getKey();
        mNotificationData.updateRanking(rankingMap);
        //创建通知的相关排名类
        NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
        rankingMap.getRanking(key, ranking);
        //创建NotificationEntry
        NotificationEntry entry = new NotificationEntry(notification, ranking);

        Dependency.get(LeakDetector.class).trackInstance(entry);
        // 构造通知View
        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
                REASON_CANCEL));

        abortExistingInflation(key);

        mPendingNotifications.put(key, entry);
        for (NotificationEntryListener listener : mNotificationEntryListeners) {
            listener.onPendingEntryAdded(entry);
        }
    }

requireBinder其实是返回NotificationRowBinder实例,上面requireBinder().inflateViews调到了NotificationRowBinder的inflateViews方法,NotificationRowBinder是一个接口,找它的实现类NotificationRowBinderImpl

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java

    /**
     * Inflates the views for the given entry (possibly asynchronously).
     */
    @Override
    public void inflateViews(
            NotificationEntry entry,
            Runnable onDismissRunnable)
            throws InflationException {
        ViewGroup parent = mListContainer.getViewParentForNotification(entry);
        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                entry.notification.getUser().getIdentifier());

        final StatusBarNotification sbn = entry.notification;
        if (entry.rowExists()) {
            entry.updateIcons(mContext, sbn);
            entry.reset();
            updateNotification(entry, pmUser, sbn, entry.getRow());
        } else {
            entry.createIcons(mContext, sbn);
            new RowInflaterTask().inflate(mContext, parent, entry,
                    row -> {
                        bindRow(entry, pmUser, sbn, row, onDismissRunnable);
                        updateNotification(entry, pmUser, sbn, row);
                    });
        }
    }

onDismissRunnable是NotificationEntryManager传过来的一个匿名函数,看名字是移除通知的操作

        requireBinder().inflateViews(entry, 
                () -> performRemoveNotification(notification,
                REASON_CANCEL))
        public void performRemoveNotification(StatusBarNotification n, int reason) {
        final NotificationVisibility nv = obtainVisibility(n.getKey());
        removeNotificationInternal(
                n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */,
                reason);
    }

接着回到NotificationRowBinderImpl的inflateViews方法,
mListContainer是通过NotificationRowBinderImpl的setUpWithPresenter传递过来的,类型是NotificationListContainer,它的实现类NotificationStackScrollLayout

    /**
     * Inflates the views for the given entry (possibly asynchronously).
     */
    @Override
    public void inflateViews(
            NotificationEntry entry,
            Runnable onDismissRunnable)
            throws InflationException {
            //getViewParentForNotification返回this即NotificationStackScrollLayout
        ViewGroup parent = mListContainer.getViewParentForNotification(entry);
        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                entry.notification.getUser().getIdentifier());

        final StatusBarNotification sbn = entry.notification;
        if (entry.rowExists()) {
            entry.updateIcons(mContext, sbn);
            entry.reset();
            updateNotification(entry, pmUser, sbn, entry.getRow());
        } else {
            //看方法名,创建通知图标
            entry.createIcons(mContext, sbn);
            new RowInflaterTask().inflate(mContext, parent, entry,
                    row -> {
                        bindRow(entry, pmUser, sbn, row, onDismissRunnable);
                        updateNotification(entry, pmUser, sbn, row);
                    });
        }
    }

重点:通知View的构造

 new RowInflaterTask().inflate(mContext, parent, entry,
                    row -> {
                        bindRow(entry, pmUser, sbn, row, onDismissRunnable);
                        updateNotification(entry, pmUser, sbn, row);
                    });

inflate

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java

    public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
            RowInflationFinishedListener listener) {
        if (TRACE_ORIGIN) {
            mInflateOrigin = new Throwable("inflate requested here");
        }
        mListener = listener;
        //异步Inflater
        AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
        mEntry = entry;
        entry.setInflationTask(this);
        inflater.inflate(R.layout.status_bar_notification_row, parent, this);
    }

加载了一个R.layout.status_bar_notification_row的布局文件,父容器是传过来的parent,即NotificationStackScrollLayout
看一下status_bar_notification_row

/frameworks/base/packages/SystemUI/res/layout/status_bar_notification_row.xml

<com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:focusable="true"
    android:clickable="true"
    >

    <!-- Menu displayed behind notification added here programmatically -->

    <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
        android:id="@+id/backgroundNormal"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
        android:id="@+id/backgroundDimmed"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.android.systemui.statusbar.notification.row.NotificationContentView
        android:id="@+id/expanded"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />

    <com.android.systemui.statusbar.notification.row.NotificationContentView
        android:id="@+id/expandedPublic"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/veto"
        android:layout_width="48dp"
        android:layout_height="0dp"
        android:gravity="end"
        android:layout_marginEnd="-80dp"
        android:background="@null"
        android:paddingEnd="8dp"
        android:paddingStart="8dp"
        />

    <ViewStub
        android:layout="@layout/notification_children_container"
        android:id="@+id/child_container_stub"
        android:inflatedId="@+id/notification_children_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

    <ViewStub
        android:layout="@layout/notification_guts"
        android:id="@+id/notification_guts_stub"
        android:inflatedId="@+id/notification_guts"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

    <com.android.systemui.statusbar.notification.FakeShadowView
        android:id="@+id/fake_shadow"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</com.android.systemui.statusbar.notification.row.ExpandableNotificationRow>

采用异步加载的方式AsyncLayoutInflater,布局文件status_bar_notification_row加载完之后回调onInflateFinished,从视图树看这整个布局文件就是一条通知ExpandableNotificationRow
在这里插入图片描述

onInflateFinished

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java

    @Override
    public void onInflateFinished(View view, int resid, ViewGroup parent) {
         //加载任务没被取消
        if (!mCancelled) {
            try {
                mEntry.onInflationTaskFinished();
                mListener.onInflationFinished((ExpandableNotificationRow) view);
            } catch (Throwable t) {
                if (mInflateOrigin != null) {
                    Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin);
                    t.addSuppressed(mInflateOrigin);
                }
                throw t;
            }
        }
    }

mEntry.onInflationTaskFinished

    public void onInflationTaskFinished() {
        mRunningTask = null;
    }

mRunningTask是之前inflate方法传过去的entry.setInflationTask(this);

mListener.onInflationFinished

NotificationRowBinderImpl传递过来的mListener是一个lambda表达式,调用mListener.onInflationFinished其实就是执行了此lambda表达式,row就是onInflationFinished传入的参数ExpandableNotificationRow

new RowInflaterTask().inflate(mContext, parent, entry,
                    row -> {
                        bindRow(entry, pmUser, sbn, row, onDismissRunnable);
                        updateNotification(entry, pmUser, sbn, row);
                    });

bindRow

此方法会填充ExpandableNotificationRow的数据,并且再将ExpandableNotificationRow数据绑定到StatusBarNotificationPresenter
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java

    private void bindRow(NotificationEntry entry, PackageManager pmUser,
            StatusBarNotification sbn, ExpandableNotificationRow row,
            Runnable onDismissRunnable) {
        row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
        row.setGroupManager(mGroupManager);
        row.setHeadsUpManager(mHeadsUpManager);
        row.setOnExpandClickListener(mPresenter);
        row.setInflationCallback(mInflationCallback);
        if (mAllowLongPress) {
            row.setLongPressListener(mGutsManager::openGuts);
        }
        //将ExpandableNotificationRow绑定到NotificationStackScrollLayout
        mListContainer.bindRow(row);
        getRemoteInputManager().bindRow(row);
        final String pkg = sbn.getPackageName();
        String appname = pkg;
        ......
        row.setAppName(appname);
        row.setOnDismissRunnable(onDismissRunnable);
        row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        if (ENABLE_REMOTE_INPUT) {
            row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
        }
        row.setAppOpsOnClickListener(mOnAppOpsClickListener);
        //StatusBarNotificationPresenter实现了mBindRowCallback
        mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
    }

继续回到之前lambda表达式里面

updateNotification

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java

    private void updateNotification(
            NotificationEntry entry,
            PackageManager pmUser,
            StatusBarNotification sbn,
            ExpandableNotificationRow row) {
        row.setIsLowPriority(entry.ambient);
        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.setRow(row);
        row.setOnActivatedListener(mPresenter);
        row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
        row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
        row.setEntry(entry);
        ......
        row.setNeedsRedaction(
                Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
        //重点
        row.inflateViews();

        // bind the click event to the content area
        checkNotNull(mNotificationClicker).register(row, sbn);
    }

row.inflateViews

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java

    public void inflateViews() {
        mNotificationInflater.inflateNotificationViews();
    }

inflateNotificationViews

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java

    public void inflateNotificationViews() {
        inflateNotificationViews(mInflationFlags);
    }
    private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
        //如果通知已被删除
        if (mRow.isRemoved()) {
            return;
        }
        // Only inflate the ones that are set.
        reInflateFlags &= mInflationFlags;
        StatusBarNotification sbn = mRow.getEntry().notification;

        // To check if the notification has inline image and preload inline image if necessary.
        mRow.getImageResolver().preloadImages(sbn.getNotification());
        AsyncInflationTask task = new AsyncInflationTask(
                sbn,
                mInflateSynchronously,
                reInflateFlags,
                mCachedContentViews,
                mRow,
                mIsLowPriority,
                mIsChildInGroup,
                mUsesIncreasedHeight,
                mUsesIncreasedHeadsUpHeight,
                mRedactAmbient,
                mCallback,
                mRemoteViewClickHandler);
        if (mInflateSynchronously) {
            task.onPostExecute(task.doInBackground());
        } else {
            task.execute();
        }
    }

这里通知视图的加载都是通过异步的
来看这个AsyncInflationTask,首先看doInBackground

 public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
            implements InflationCallback, InflationTask {
            ......
            @Override
        protected InflationProgress doInBackground(Void... params) {
            try {
                final Notification.Builder recoveredBuilder
                        = Notification.Builder.recoverBuilder(mContext,
                        mSbn.getNotification());

                Context packageContext = mSbn.getPackageContext(mContext);
                Notification notification = mSbn.getNotification();
                //如果是多媒体类型的通知
                if (notification.isMediaNotification()) {
                    MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
                            packageContext);
                    processor.processNotification(notification, recoveredBuilder);
                }
                //构造通知View
                InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
                        recoveredBuilder, mIsLowPriority,
                        mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
                        mRedactAmbient, packageContext);
                return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
                        mRow.getContext(), mRow.getHeadsUpManager(),
                        mRow.getExistingSmartRepliesAndActions());
            } catch (Exception e) {
                mError = e;
                return null;
            }
        }
            ......
      }

先看下InflationProgress,NotificationContentInflater的静态内部类,用于构造通知View,RemoteViews用于跨进程显示视图
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java

    static class InflationProgress {
        private RemoteViews newContentView;
        private RemoteViews newHeadsUpView;
        private RemoteViews newExpandedView;
        private RemoteViews newAmbientView;
        private RemoteViews newPublicView;

        @VisibleForTesting
        Context packageContext;

        private View inflatedContentView;
        private View inflatedHeadsUpView;
        private View inflatedExpandedView;
        private View inflatedAmbientView;
        private View inflatedPublicView;
        private CharSequence headsUpStatusBarText;
        private CharSequence headsUpStatusBarTextPublic;

        private InflatedSmartReplies expandedInflatedSmartReplies;
        private InflatedSmartReplies headsUpInflatedSmartReplies;
    }

createRemoteViews

此方法是构造显示出来的通知View的核心方法,会返回通知布局文件,如果没有自定义通知布局将返回系统默认的布局

    private static InflationProgress createRemoteViews(@InflationFlag 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_CONTENT_VIEW_CONTRACTED) != 0) {
            result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
        }

        if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
            result.newExpandedView = createExpandedView(builder, isLowPriority);
        }

        if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
            result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
        }

        if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
            result.newPublicView = builder.makePublicContentView();
        }

        if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
            result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
                    : builder.makeAmbientNotification();
        }
        result.packageContext = packageContext;
        result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
        result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
                true /* showingPublic */);
        return result;
    }

上面这些if判断就是根据不同flag即不同通知类型加载不同通知布局,我们这里以最简单的默认布局(createContentView)来看,可以看到直接通过Notification.Builder来创建通知View

    private static RemoteViews createContentView(Notification.Builder builder,
            boolean isLowPriority, boolean useLarge) {
        if (isLowPriority) {
            return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
        }
        return builder.createContentView(useLarge);
    }

builder.createContentView

/frameworks/base/core/java/android/app/Notification.java

        //构造最终的通知UI布局
        public RemoteViews createContentView() {
            return createContentView(false /* increasedheight */ );
        }
       public RemoteViews createContentView(boolean increasedHeight) {
            /*mN.contentView其实就是我们需要自定义通知View时调用
            setContent方法设置RemoteView,如果没有设置就加载系统默认布局*/
            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(), null /* result */);
        }
         private int getBaseLayoutResource() {
            return R.layout.notification_template_material_base;
        }

默认加载布局就是R.layout.notification_template_material_base,applyStandardTemplate这个方法就是给此布局设置属性,寬高颜色字体等

private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
                TemplateBindResult result) {
            RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
            resetStandardTemplate(contentView);
            final Bundle ex = mN.extras;
            updateBackgroundColor(contentView, p);
            bindNotificationHeader(contentView, p);
            bindLargeIconAndReply(contentView, p, result);
            boolean showProgress = handleProgressBar(contentView, ex, p);
            if (p.title != null) {
                contentView.setViewVisibility(R.id.title, View.VISIBLE);
                contentView.setTextViewText(R.id.title, processTextSpans(p.title));
                setTextViewColorPrimary(contentView, R.id.title, p);
                contentView.setViewLayoutWidth(R.id.title, showProgress
                        ? ViewGroup.LayoutParams.WRAP_CONTENT
                        : ViewGroup.LayoutParams.MATCH_PARENT);
            }
            if (p.text != null) {
                int textId = showProgress ? com.android.internal.R.id.text_line_1
                        : com.android.internal.R.id.text;
                contentView.setTextViewText(textId, processTextSpans(p.text));
                setTextViewColorSecondary(contentView, textId, p);
                contentView.setViewVisibility(textId, View.VISIBLE);
            }
            setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
            return contentView;
        }

NotificationContentInflater的createContentView就已经将通知View创建了出来,继续回到doInBackground

inflateSmartReplyViews

这个方法是我们创建通知时如果调用了makeHeaderExpanded或者createHeadsUpContentView设置通知头或者弹出式通知就会去具体构造这两种通知,具体不进一步去看了

    private static InflationProgress inflateSmartReplyViews(InflationProgress result,
            @InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
            HeadsUpManager headsUpManager, SmartRepliesAndActions previousSmartRepliesAndActions) {
        SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class);
        SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class);
        if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
            result.expandedInflatedSmartReplies =
                    InflatedSmartReplies.inflate(
                            context, entry, smartReplyConstants, smartReplyController,
                            headsUpManager, previousSmartRepliesAndActions);
        }
        if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) {
            result.headsUpInflatedSmartReplies =
                    InflatedSmartReplies.inflate(
                            context, entry, smartReplyConstants, smartReplyController,
                            headsUpManager, previousSmartRepliesAndActions);
        }
        return result;
    }

onPostExecute

doInBackground执行完之后接着到onPostExecute

protected void onPostExecute(InflationProgress result) {         
            if (mError == null) {
                mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
                        mCachedContentViews, mRow, mRedactAmbient, mRemoteViewClickHandler, this);
            } else {
                handleError(mError);
            }
        }

apply

根据不同的通知类型做不同的事,只选一个最普通的类型FLAG_CONTENT_VIEW_CONTRACTED

public static CancellationSignal apply(
            boolean inflateSynchronously,
            InflationProgress result,
            @InflationFlag int reInflateFlags,
            ArrayMap<Integer, RemoteViews> cachedContentViews,
            ExpandableNotificationRow row,
            boolean redactAmbient,
            RemoteViews.OnClickHandler remoteViewClickHandler,
            @Nullable InflationCallback callback) {
            //加载这个R.id.expanded
        NotificationContentView privateLayout = row.getPrivateLayout();
        //加载这个R.id.expandedPublic
        NotificationContentView publicLayout = row.getPublicLayout();
        final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();

        int flag = FLAG_CONTENT_VIEW_CONTRACTED;
        if ((reInflateFlags & flag) != 0) {
           //是否是新增加的View
            boolean isNewView =
                    !canReapplyRemoteView(result.newContentView,
                            cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
               //回调
            ApplyCallback applyCallback = new ApplyCallback() {
                @Override
                public void setResultView(View v) {
                    result.inflatedContentView = v;
                }

                @Override
                public RemoteViews getRemoteView() {
                    return result.newContentView;
                }
            };
            applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
                    row, redactAmbient, isNewView, remoteViewClickHandler, callback, privateLayout,
                    privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
                            NotificationContentView.VISIBLE_TYPE_CONTRACTED),
                    runningInflations, applyCallback);
        }
        ......
        ......
        //通知View加载完成之后
        finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row,
                redactAmbient);
        CancellationSignal cancellationSignal = new CancellationSignal();
        cancellationSignal.setOnCancelListener(
                () -> runningInflations.values().forEach(CancellationSignal::cancel));
        return cancellationSignal;
    }

applyRemoteView

static void applyRemoteView(
            boolean inflateSynchronously,
            final InflationProgress result,
            final @InflationFlag int reInflateFlags,
            @InflationFlag int inflationId,
            final ArrayMap<Integer, RemoteViews> cachedContentViews,
            final ExpandableNotificationRow row,
            final boolean redactAmbient,
            boolean isNewView,
            RemoteViews.OnClickHandler remoteViewClickHandler,
            @Nullable final InflationCallback callback,
            NotificationContentView parentLayout,
            View existingView,
            NotificationViewWrapper existingWrapper,
            final HashMap<Integer, CancellationSignal> runningInflations,
            ApplyCallback applyCallback) {
            ......
            //新增加通知的情况
            if (isNewView) {
                    View v = newContentView.apply(
                            result.packageContext,
                            parentLayout,
                            remoteViewClickHandler);
                    v.setIsRootNamespace(true);
                    applyCallback.setResultView(v);
                } else {
                    newContentView.reapply(
                            result.packageContext,
                            existingView,
                            remoteViewClickHandler);
                    existingWrapper.onReinflated();
                }
            ......
       }

finishIfDone

private static boolean finishIfDone(InflationProgress result,
            @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
            HashMap<Integer, CancellationSignal> runningInflations,
            @Nullable InflationCallback endListener, ExpandableNotificationRow row,
            boolean redactAmbient) {
            ......
        NotificationEntry entry = row.getEntry();
        NotificationContentView privateLayout = row.getPrivateLayout();
        NotificationContentView publicLayout = row.getPublicLayout();
        if (runningInflations.isEmpty()) {
            if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
                if (result.inflatedContentView != null) {
                  /*result.inflatedContentView就是RemoteViews
                  将result.inflatedContentView添加到
                  privateLayout(R.id.expanded)*/
                    privateLayout.setContractedChild(result.inflatedContentView);
                    cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
                } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) {
                  cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
                }
            }
            ......
            if (endListener != null) {
                endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
            }
}

setContractedChild

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java

    public void setContractedChild(@Nullable View child) {
        if (mContractedChild != null) {
            mContractedChild.animate().cancel();
            removeView(mContractedChild);
        }
        if (child == null) {
            mContractedChild = null;
            mContractedWrapper = null;
            if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
                mTransformationStartVisibleType = UNDEFINED;
            }
            return;
        }
        //添加RemoteViews
        addView(child);
        mContractedChild = child;
        mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
                mContainingNotification);
    }

RemoteView加载到NotificationContentView之后接着回调onAsyncInflationFinished

onAsyncInflationFinished

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java

    public void onAsyncInflationFinished(NotificationEntry entry,
            @InflationFlag int inflatedFlags) {
        mPendingNotifications.remove(entry.key);
        if (!entry.isRowRemoved()) {
            boolean isNew = mNotificationData.get(entry.key) == null;
            //新增通知
            if (isNew) {
                ......
                updateNotifications();
                .....
            } else {
               .....
            }
        }
    }

updateNotifications

    public void updateNotifications() {
        //对通知进行过滤和排序
        mNotificationData.filterAndSort();
        if (mPresenter != null) {
            mPresenter.updateNotificationViews();
        }
    }

updateNotificationViews

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java

    public void updateNotificationViews() {
        if (mScrimController == null) return;
        if (isCollapsing()) {         mShadeController.addPostCollapseAction(this::updateNotificationViews);
            return;
        }
        mViewHierarchyManager.updateNotificationViews();

        mNotificationPanel.updateNotificationViews();
    }

mViewHierarchyManager.updateNotificationViews

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java

public void updateNotificationViews() {
        //拿到所有的NotificationEntry,封装了整个通知的信息
        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
                .getActiveNotifications();
         //创建ExpandableNotificationRow,用来承载每条通知RemoteView
        ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
        final int N = activeNotifications.size();
        //遍历全部通知
        for (int i = 0; i < N; i++) {
        ......
        //经过过滤之后全部添加到toShow
        toShow.add(ent.getRow());
        ......
        }
       ......
       //遍历需要显示的通知,添加到NotificationStackScrollLayout
       for (int i = 0; i < toShow.size(); i++) {
            View v = toShow.get(i);
            //如果是新增通知则需要添加,addContainerView
            if (v.getParent() == null) {
                mVisualStabilityManager.notifyViewAddition(v);
                mListContainer.addContainerView(v);
                //如果是此通知已经添加过了,移除toShow
            } else if (!mListContainer.containsView(v)) {
                     toShow.remove(v);
                i--;
            }
        }
       ......
       //后面会对通知排序等操作
       
}

addContainerView

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java

    public void addContainerView(View v) {
        Assert.isMainThread();
        addView(v);
    }

到此通知已经全部的创建,加载以及添加完成,这篇文章仅仅大概梳理了通知布局的加载,数据填充即添加到显示UI,很多细节代码需要遇到具体问题在具体分析,整个流程大概就是从framework拿到StatusBarNotification,封装成NotificationEntry,之后去根据不同通知类型加载不同布局,如果没有自定义布局则加载默认布局,创建RemoteView,添加到ExpandableNotificationRow的中的id为R.id.expanded的NotificationContentView,之后再将ExpandableNotificationRow添加到NotificationStackScrollLayout,整个通知布局如下图:
在这里插入图片描述

发布了28 篇原创文章 · 获赞 40 · 访问量 4817

猜你喜欢

转载自blog.csdn.net/qq_34211365/article/details/103398511