美文网首页
SystemUI之状态栏notification icon加载流

SystemUI之状态栏notification icon加载流

作者: 按捉老虎 | 来源:发表于2018-10-26 11:28 被阅读0次

    引言

    今天我们主要讲的是SystemUI状态栏里面另一个常见的icons——notification icons,该icons主要用于显示app或者framework发送的各种notification icon,表示当前有新的通知来了,需要下拉通知栏进行查看,以达到提示用户的目的。

    正文

    本文主要从两个方面讲述下notification icon功能,主要分为初始化流程和通知icon显示流程
    话不多说,我们开始吧。

    初始化流程

    首先我们看下状态栏的布局文件 status_bar.xml

    <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
                 PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
            <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                android:id="@+id/notification_icon_area"
                android:layout_width="0dip"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:orientation="horizontal" />
    
            <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:orientation="horizontal"
                >
    

    我们今天讲的notification icons就是这个 android:id="@+id/notification_icon_area" 了。
    同样它最外层是一个AlphaOptimizedFrameLayout控件,前文已经说过类似的了,
    SystemUI之状态栏status icon加载流程
    该控件实现了hasOverlappingRendering()方法,该方法用来标记当前view是否存在过度绘制。

    接下来我们看下SystemUI是怎么加载这个AlphaOptimizedFrameLayout

    CollapsedStatusBarFragment.java

    public void initNotificationIconArea(NotificationIconAreaController
                notificationIconAreaController) {
            ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
            mNotificationIconAreaInner =
                    notificationIconAreaController.getNotificationInnerAreaView();
            if (mNotificationIconAreaInner.getParent() != null) {
                ((ViewGroup) mNotificationIconAreaInner.getParent())
                        .removeView(mNotificationIconAreaInner);
            }
            notificationIconArea.addView(mNotificationIconAreaInner);
            // Default to showing until we know otherwise.
            showNotificationIconArea(false);
        }
    

    NotificationIconAreaController.java

    /**
         * Initializes the views that will represent the notification area.
         */
        protected void initializeNotificationAreaViews(Context context) {
            reloadDimens(context);
    
            LayoutInflater layoutInflater = LayoutInflater.from(context);
            mNotificationIconArea = inflateIconArea(layoutInflater);
            mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
                    R.id.notificationIcons);
    
            mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
        }
    
        protected View inflateIconArea(LayoutInflater inflater) {
            return inflater.inflate(R.layout.notification_icon_area, null);
        }
    

    如上,我们就找到了初始化的地方,主要是通过inflate这个R.layout.notification_icon_area文件,通过addView的方式添加到了AlphaOptimizedFrameLayout,下面就是看下R.layout.notification_icon_area这个文件了

    <com.android.keyguard.AlphaOptimizedLinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/notification_icon_area_inner"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <com.android.systemui.statusbar.phone.NotificationIconContainer
            android:id="@+id/notificationIcons"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentStart="true"
            android:gravity="center_vertical"
            android:orientation="horizontal"/>
    </com.android.keyguard.AlphaOptimizedLinearLayout>
    
    /**
     * A container for notification icons. It handles overflowing icons properly and positions them
     * correctly on the screen.
     */
    public class NotificationIconContainer extends AlphaOptimizedFrameLayout {}
    

    从备注就能够看出来,这个就是所有notification icons的父控件,所有icons最后都是添加到这里面来的,
    好了,到这里我们的第一部分初始化流程就讲完了。

    通知icon显示流程

    首先我们需要看下notification生成的地方

    StatusBar.java

    public void onNotificationPosted(final StatusBarNotification sbn,
                    final RankingMap rankingMap) {
    
                              if (isUpdate) {
                                    updateNotification(sbn, rankingMap);
                                } else {
                                    addNotification(sbn, rankingMap);
                                }
    }
    
    public void addNotification(StatusBarNotification notification, RankingMap ranking)
                throws InflationException {
            String key = notification.getKey();
            if (DEBUG) Log.d(TAG, "addNotification key=" + key);
    
            mNotificationData.updateRanking(ranking);
            Entry shadeEntry = createNotificationViews(notification);
    
    protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
                throws InflationException {
            if (DEBUG) {
                Log.d(TAG, "createNotificationViews(notification=" + sbn);
            }
            NotificationData.Entry entry = new NotificationData.Entry(sbn);
            Dependency.get(LeakDetector.class).trackInstance(entry);
            entry.createIcons(mContext, sbn);
            // Construct the expanded view.
            inflateViews(entry, mStackScroller);
            return entry;
        }
    }
    

    层层调用之后,最后会通过entry.createIcons(mContext, sbn)生成notification icon,然后存放在NotificationData里面,感兴趣的可以看下entry.createIcons(mContext, sbn), 该函数里面主要生成了一个StatusBarIconView对象,这个就是最终显示在状态栏的icon。

    notification生成的过程大致就是通过inflateViews(entry, mStackScroller)--->AsyncInflationTask处理加载然后生成ExpandableNotificationRow等的信息,最后通过StatusBar.java

    @Override
        public void onAsyncInflationFinished(Entry entry) {
            mPendingNotifications.remove(entry.key);
            // If there was an async task started after the removal, we don't want to add it back to
            // the list, otherwise we might get leaks.
            boolean isNew = mNotificationData.get(entry.key) == null;
            if (isNew && !entry.row.isRemoved()) {
                addEntry(entry);
            } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
                mVisualStabilityManager.onLowPriorityUpdated(entry);
                updateNotificationShade();//  此处完成添加
            }
            entry.row.setLowPriorityStateUpdated(false);
        }
    
    private void updateNotificationShade() {
            ................................................
            for (int i=0; i<toShow.size(); i++) {
                View v = toShow.get(i);
                if (v.getParent() == null) {
                    mVisualStabilityManager.notifyViewAddition(v);
                    mStackScroller.addView(v);
                }
            }
            ................................................
            // Let's also update the icons
            mNotificationIconAreaController.updateNotificationIcons(mNotificationData);//添加notification icons
    }
    
    

    updateNotificationShade这个函数回调完成view的添加,这个函数先是把inflate出来的通知,添加到NotificationScrollLayout里面,然后再添加notification icon,接下来我们就看下updateNotificationIcons里面的逻辑了。

    NotificationIconAreaController.java

    private NotificationIconContainer mNotificationIcons;
    /**
         * Updates the notifications with the given list of notifications to display.
         */
        public void updateNotificationIcons(NotificationData notificationData) {
    
            updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
                    false /* showAmbient */);//  添加status bar notification icon
            updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
                    NotificationShelf.SHOW_AMBIENT_ICONS);//  添加 notification self icon
    
            applyNotificationIconsTint();
        }
    

    主要的添加动作就在updateIconsForLayout这个函数中了

    private void updateIconsForLayout(NotificationData notificationData,
                Function<NotificationData.Entry, StatusBarIconView> function,
                NotificationIconContainer hostLayout, boolean showAmbient) {
    
            ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                    mNotificationScrollLayout.getChildCount());
            // Filter out ambient notifications and notification children.
            for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
                View view = mNotificationScrollLayout.getChildAt(i);
                if (view instanceof ExpandableNotificationRow) {
                    NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
                    if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
                        toShow.add(function.apply(ent));
                    }
                }
            }
            ......................................
            ......................................
            ......................................
            final FrameLayout.LayoutParams params = generateIconLayoutParams();
            for (int i = 0; i < toShow.size(); i++) {
                View v = toShow.get(i);
                // The view might still be transiently added if it was just removed and added again
                hostLayout.removeTransientView(v);
                if (v.getParent() == null) {
                    hostLayout.addView(v, i, params);
                }
            }
    
    }
    

    首先从mNotificationScrollLayout取出NotificationData,然后把NotificationData存放的StatusBarIconView取出添加到toShow里面,最后遍历添加到NotificationIconContainer中,这样就完成了往NotificationIconContainer添加icon的过程。

    到这里,notification icon加载流程已经讲完,后面有时间还会讲下signal icon的加载流程,敬请关注。

    如有什么问题欢迎指正。

    本文章已经独家授权ApeClub公众号使用。

    相关文章

      网友评论

          本文标题:SystemUI之状态栏notification icon加载流

          本文链接:https://www.haomeiwen.com/subject/otsqtqtx.html