Android 9.0系统源码_SystemUI(四)通知图标控制器

前言

上一篇我们具体分析了状态栏上状态图标,例如 wifi、蓝牙等图标的控制流程,本篇文章我们继续来分析下状态栏上通知图标的控制流程。主要分析当一个新通知来临时,新通知的图标是如何一步步显示到状态栏上的。

一、通知图标控制器

1、通过上一篇系统状态栏图标控制可知,状态栏图标是由一个叫StatusBarIconController接口控制显示的,而通知图标区域也有一个控制器,叫NotificationIconAreaController(它不是一个接口)。

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

public class NotificationIconAreaController implements DarkReceiver {
 	...
    public NotificationIconAreaController(Context context, StatusBar statusBar) {
        mStatusBar = statusBar;
        mNotificationColorUtil = NotificationColorUtil.getInstance(context);//通知栏颜色工具
        mContext = context;
        mEntryManager = Dependency.get(NotificationEntryManager.class);

        initializeNotificationAreaViews(context);//初始化通知栏区域视图
    }
    ...
}

2、在NotificationIconAreaController的构造函数中会调用如下方法来创建通知图标的容器

public class NotificationIconAreaController implements DarkReceiver {

    protected View mNotificationIconArea;
    private NotificationIconContainer mNotificationIcons;
    private NotificationStackScrollLayout mNotificationScrollLayout;
    
    /**
     * 初始化化通知图标区域视图
     */
    protected void initializeNotificationAreaViews(Context context) {
        reloadDimens(context);
        LayoutInflater layoutInflater = LayoutInflater.from(context);
        // 实例化通知图标区域视图
        mNotificationIconArea = inflateIconArea(layoutInflater);
        // mNotificationIcons才是真正存放通知图标的父容器
        mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(R.id.notificationIcons);
        mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
    }
    ...
    protected View inflateIconArea(LayoutInflater inflater) {
        // 实例化通知图标区域视图
        return inflater.inflate(R.layout.notification_icon_area, null);
    }
    ...
}

3、在NotificationIconAreaController的inflateIconArea方法中会加载了R.layout.notification_icon_are.xml布局,来看下这个布局:

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

<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"
    android:clipChildren="false">
    <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"
        android:clipChildren="false"/>
</com.android.keyguard.AlphaOptimizedLinearLayout>

id为notificationIcons的控件就是状态栏通知图标容器,对应于上面代码的mNotificationIcons变量。

二、初始化通知图标区域

1、既然是NotificationIconAreaController自己创建了通知图标容器,那么通知图标是如何被添加到状态栏视图中的呢?这个过程主要是在StatusBar的makeStatusBarView方法中实现的,关键代码如下所示:

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

    protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
        ...
        // 创建状态栏视图
        FragmentHostManager.get(mStatusBarWindow)
                .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                    CollapsedStatusBarFragment statusBarFragment = (CollapsedStatusBarFragment) fragment;
                    // 初始化了通知图标区域
                    statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                    ...
                })
                .getFragmentManager()
                .beginTransaction()
                // CollapsedStatusBarFragment实现了状态栏的添加
                .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(), CollapsedStatusBarFragment.TAG)
                .commit();     
      ...         
    }

2、CollapsedStatusBarFragment的initNotificationIconArea方法如下所示:

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


    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
        //CollapsedStatusBarFragment加载布局文件
        return inflater.inflate(R.layout.status_bar, container, false);
    }
        @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mStatusBar = (PhoneStatusBarView) view;
	}
	// 初始化通知图标区域
    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);
        // 这里其实显示了通知图标区域和中心图标区域
        showNotificationIconArea(false);
    }

上面代码的主要目的就是将通知图标控制器所构建的视图mNotificationIconAreaInner添加到布局status_bar.xml中id等于notification_icon_area的容器中,notification_icon_area容器在status_bar.xml布局中的位置如下所示:

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

<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.statusbar.phone.PhoneStatusBarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
    android:layout_width="match_parent"
    android:layout_height="@dimen/status_bar_height"
    android:id="@+id/status_bar"
    android:background="@drawable/system_bar_background"
    android:orientation="vertical"
    android:focusable="false"
    android:descendantFocusability="afterDescendants"
    android:accessibilityPaneTitle="@string/status_bar"
    >
	...
    <LinearLayout android:id="@+id/status_bar_contents"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingEnd="@dimen/status_bar_padding_end"
        android:orientation="horizontal"
        >
		...
        <FrameLayout
            android:layout_height="match_parent"
            android:layout_width="0dp"
            android:layout_weight="1">
          	...  
            <LinearLayout
                android:id="@+id/status_bar_left_side"
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:clipChildren="false"
            >
            	...
                <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                    android:id="@+id/notification_icon_area"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:orientation="horizontal"
                    android:clipChildren="false"/>
                    
            </LinearLayout>
        </FrameLayout>
    </LinearLayout>

</com.android.systemui.statusbar.phone.PhoneStatusBarView>

三、监听通知的服务端

1、当一条新通知发送后,它会存储到通知服务端,也就是NotificationManagerService,那SystemUI是如何知道新通知来临的?这就需要SystemUI向NotificationManagerService注册一个"服务"(一个Binder)。
这个"服务"就相当于客户端SystemUI在服务端NotificationManagerService注册的一个回调。当有通知来临的时候,就会通过这个"服务"通知SystemUI。这个注册是在StatusBar#start()中完成的:

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

public class StatusBar extends SystemUI implements DemoMode,
        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
        OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
        ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
       
        ...
    protected NotificationListener mNotificationListener;
    protected NotificationEntryManager mEntryManager;
    	...
    	
    public void start() {
        ...
        // 向通知服务端注册一个"服务",用于接收通知信息的回调
        mNotificationListener =  Dependency.get(NotificationListener.class);
        mNotificationListener.setUpWithPresenter(this, mEntryManager);
        ...        
    }
    	...    
}

2、继续来看NotificationListener的setUpWithPresenter方法,该方法会调用父类的NotificationListenerWithPlugins的registerAsSystemService方法:

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

  public class NotificationListener extends NotificationListenerWithPlugins {
  		...    
    public void setUpWithPresenter(NotificationPresenter presenter, NotificationEntryManager entryManager) {
        mPresenter = presenter;
        mEntryManager = entryManager;
        try {
            registerAsSystemService(mContext, new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), UserHandle.USER_ALL);
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to register notification listener", e);
        }
    }
    //连接成功
    @Override
    public void onListenerConnected() {
		...
    }
    //收到新的通知
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
		...
    }
    //移除通知
    @Override
    public void onNotificationRemoved(StatusBarNotification sbn,
            final RankingMap rankingMap) {
		...
    }
    //更新通知
    @Override
    public void onNotificationRankingUpdate(final RankingMap rankingMap) {
		...
    }
  }

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

public class NotificationListenerWithPlugins extends NotificationListenerService implements
        PluginListener<NotificationListenerController> {
        ...
    @Override
    public void registerAsSystemService(Context context, ComponentName componentName,
            int currentUser) throws RemoteException {
        super.registerAsSystemService(context, componentName, currentUser);
        //获取PluginManager的具体实现类PluginManagerImpl,并将自身添加到PluginManagerImpl实例对象中
        Dependency.get(PluginManager.class).addPluginListener(this, NotificationListenerController.class);
    }
        ...
}

四、服务端接收到新的通知消息

1、当一条新的通知来临的时候,会触发NotificationListener的onNotificationPosted方法,该方法会调用状态栏条目管理者的addNotification方法,关键代码如下:

  public class NotificationListener extends NotificationListenerWithPlugins {
    		...
      protected NotificationEntryManager mEntryManager;//状态栏条目管理者

    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
            // 在主线程中进行更新
            mPresenter.getHandler().post(() -> {
                processForRemoteInput(sbn.getNotification(), mContext);
                String key = sbn.getKey();
                mEntryManager.removeKeyKeptForRemoteInput(key);
                //我们第一次创建通知的时候getNotificationData返回的一定是null,因为还没有放进去,所以isUpdate = false,走添加通知的流程
                boolean isUpdate = mEntryManager.getNotificationData().get(key) != null;
                ...
                //判断到来的通知是需要更新还是添加                
                if (isUpdate) {
                    // 更新通知操作
                    mEntryManager.updateNotification(sbn, rankingMap);
                } else {
                    // 添加新通知操作
                    mEntryManager.addNotification(sbn, rankingMap);
                }
            });
        }
    }
	...
}

2、NotificationEntryManager的addNotification方法又进一步调用addNotificationInternal方法

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

public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
        ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
        VisualStabilityManager.Callback {
        ...
    @Override
    public void addNotification(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) {
			...
         addNotificationInternal(notification, ranking);
			...
    }
        ...
    private void addNotificationInternal(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) throws InflationException {
        //获取唯一标识key,key打印出来是这样的:0|com.example.app3|110|null|10161
        String key = notification.getKey();
        mNotificationData.updateRanking(ranking);
        //创建Entry实例对象
        NotificationData.Entry shadeEntry = createNotificationViews(notification);
			...
    }
}

3、addNotificationInternal方法进一步调用createNotificationViews方法,该方法会继续调用inflateViews来构建视图对象

public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
        ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
        VisualStabilityManager.Callback {
     ...
  protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
            throws InflationException {
        NotificationData.Entry entry = new NotificationData.Entry(sbn);
        Dependency.get(LeakDetector.class).trackInstance(entry);
        entry.createIcons(mContext, sbn);
        //构建视图
        inflateViews(entry, mListContainer.getViewParentForNotification(entry));
        return entry;
    }
    ...
}    

4、inflateViews方法会创建RowInflaterTask对象并调用inflate方法:

public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
        ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
        VisualStabilityManager.Callback {
        ...
    private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                entry.notification.getUser().getIdentifier());

        final StatusBarNotification sbn = entry.notification;
        if (entry.row != null) {
            entry.reset();
            updateNotification(entry, pmUser, sbn, entry.row);
        } else {
        	//创建构建视图的任务对象
            new RowInflaterTask().inflate(mContext, parent, entry,
                    // 加载完成的回调,这里的加载指的仅仅是一个空视图
                    row -> {
                       // 绑定监听事件和回调
                        bindRow(entry, pmUser, sbn, row);
                        // 在视图上更新通知信息                        
                        updateNotification(entry, pmUser, sbn, row);
                    });
        }
    }
}

5、RowInflaterTask的inflate方法会创建异步构建视图任务对象AsyncLayoutInflater并调用他的inflate方法:

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

public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener {
    /**
     * 构建一个新的通知栏视图
     */
    public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
            RowInflationFinishedListener listener) {
 		...
        //使用异步方式加载视图的Inflater
        AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
 		...
        inflater.inflate(R.layout.status_bar_notification_row, parent, this);
    }

}

6、AsyncLayoutInflater对象调用inflate将status_bar_notification_row.xml布局文件转化为视图对象,该布局文件的具体内容如下所示:

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

<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.statusbar.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"
    >
    <com.android.systemui.statusbar.NotificationBackgroundView
        android:id="@+id/backgroundNormal"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <com.android.systemui.statusbar.NotificationBackgroundView
        android:id="@+id/backgroundDimmed"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <com.android.systemui.statusbar.NotificationContentView 
       android:id="@+id/expanded"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
    <com.android.systemui.statusbar.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.ExpandableNotificationRow>

7、从视图树可以看到这整个布局文件status_bar_notification_row.xml就是一条通知ExpandableNotificationRow:
ExpandableNotificationRow布局文件

8、AsyncLayoutInflater是采用异步方式来加载布局文件的,待布局文件加载完之后会调用之前在RowInflaterTask中设置的onInflateFinished回调方法:

frameworks/support/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java

public final class AsyncLayoutInflater {
    ...
    private Callback mHandlerCallback = new Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            InflateRequest request = (InflateRequest) msg.obj;
            if (request.view == null) {
                request.view = mInflater.inflate(request.resid, request.parent, false);
            }
            //布局加载完毕,调用之前在RowInflaterTask中设置的回调方法
            request.callback.onInflateFinished(request.view, request.resid, request.parent);
            mInflateThread.releaseRequest(request);
            return true;
        }
    };
    ...
}        

9、RowInflaterTask的onInflateFinished方法会继续回调之前在NotificationEntryManager的inflateViews方法中使用lamb表达式设置的回调方法:

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

public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
        ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
        VisualStabilityManager.Callback {
        ...
    private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                entry.notification.getUser().getIdentifier());
			...
            new RowInflaterTask().inflate(mContext, parent, entry,
                    //回调方法
                    row -> {
                    	//填充数据,绑定监听事件和回调
                        bindRow(entry, pmUser, sbn, row);
                        // 在视图上更新通知信息                        
                        updateNotification(entry, pmUser, sbn, row);
                    });
			...
    }
     ...
}

10、bindRow方法会填充ExpandableNotificationRow的数据,并且再将ExpandableNotificationRow数据绑定到StatusBar:

public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
        ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
        VisualStabilityManager.Callback {
        ...
    //填充数据并绑定到StatusBarNotificationPresenter中
    private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
            StatusBarNotification sbn, ExpandableNotificationRow row) {
        row.setExpansionLogger(this, entry.notification.getKey());
        row.setGroupManager(mGroupManager);
        row.setHeadsUpManager(mHeadsUpManager);
        row.setOnExpandClickListener(mPresenter);
        row.setInflationCallback(this);//这个回调很重要
        row.setLongPressListener(getNotificationLongClicker());
        mListContainer.bindRow(row);
        mRemoteInputManager.bindRow(row);
        final String pkg = sbn.getPackageName();
        String appname = pkg;
        try {
            final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
                    PackageManager.MATCH_UNINSTALLED_PACKAGES
                            | PackageManager.MATCH_DISABLED_COMPONENTS);
            if (info != null) {
                appname = String.valueOf(pmUser.getApplicationLabel(info));
            }
        } catch (PackageManager.NameNotFoundException e) {
            // Do nothing
        }
        row.setAppName(appname);
        row.setOnDismissRunnable(() ->
                performRemoveNotification(row.getStatusBarNotification()));
        row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        if (ENABLE_REMOTE_INPUT) {
            row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
        }
        row.setAppOpsOnClickListener(mOnAppOpsClickListener);
		//StatusBar实现了mCallback
        mCallback.onBindRow(entry, pmUser, sbn, row);
    }
    ...
}

11、updateNotification方法来更新ExpandableNotificationRow的视图:

public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
        ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
        VisualStabilityManager.Callback {
        ...
    protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
            StatusBarNotification sbn, ExpandableNotificationRow row) {
        row.setNeedsRedaction(mLockscreenUserManager.needsRedaction(entry));
        boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
        boolean isUpdate = mNotificationData.get(entry.key) != null;
        boolean wasLowPriority = row.isLowPriority();
        row.setIsLowPriority(isLowPriority);
        row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
        // bind the click event to the content area
        mNotificationClicker.register(row, sbn);

        // Extract target SDK version.
        try {
            ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
            entry.targetSdk = info.targetSdkVersion;
        } catch (PackageManager.NameNotFoundException ex) {
            Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
        }
        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.autoRedacted = entry.notification.getNotification().publicVersion == null;

        entry.row = row;
        entry.row.setOnActivatedListener(mPresenter);

        boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
                mNotificationData.getImportance(sbn.getKey()));
        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
                && !mPresenter.isPresenterFullyCollapsed();
        row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
        row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
        row.updateNotification(entry);//更新通知视图
    }
       ...
}

12 ExpandableNotificationRow的updateNotification方法如下所示:

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

public class ExpandableNotificationRow extends ActivatableNotificationView
        implements PluginListener<NotificationMenuRowPlugin> {
    ...
    private final NotificationInflater mNotificationInflater;   
  	...
    public void updateNotification(NotificationData.Entry entry) {
        mEntry = entry;
        mStatusBarNotification = entry.notification;
        //调用NotificationInflater的inflateNotificationViews方法
        mNotificationInflater.inflateNotificationViews();

        cacheIsSystemNotification();
    }
	...
}

13、继续来看下NotificationInflater的inflateNotificationViews方法:

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

public class NotificationInflater {
	...
    public void inflateNotificationViews() {
        inflateNotificationViews(FLAG_REINFLATE_ALL);
    }
    void inflateNotificationViews(int reInflateFlags) {
        //如果通知已经被删除,直接返回
        if (mRow.isRemoved()) {
            // We don't want to reinflate anything for removed notifications. Otherwise views might
            // be readded to the stack, leading to leaks. This may happen with low-priority groups
            // where the removal of already removed children can lead to a reinflation.
            return;
        }
        StatusBarNotification sbn = mRow.getEntry().notification;
        AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow,
                mIsLowPriority,
                mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
                mCallback, mRemoteViewClickHandler);
        if (mCallback != null && mCallback.doInflateSynchronous()) {
            task.onPostExecute(task.doInBackground());
        } else {
            task.execute();
        }
    }
	...    
}

14、通过上面的代码可以知道,通知视图是通过异步进行加载的,来看这个AsyncInflationTask,首先看下doInBackground方法:

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

public class NotificationInflater {
	...
    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
                return createRemoteViews(mReInflateFlags,
                        recoveredBuilder, mIsLowPriority, mIsChildInGroup,
                        mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
                        packageContext);
            } catch (Exception e) {
                mError = e;
                return null;
            }
        }
		...
	//InflationProgress是NotificationContentInflater的静态内部类,用于构造通知View	
	static class InflationProgress {
		//RemoteViews类型的视图主要用于跨进程显示视图
        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;
    }  
	 ...        
 }
} 

15、doInBackground方法会继续调用createRemoteViews方法,此方法是构造状态栏通知View的核心方法,会返回通知布局文:

public class NotificationInflater {
	...
    private static InflationProgress createRemoteViews(int reInflateFlags,
            Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
            boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
            Context packageContext) {
        InflationProgress result = new InflationProgress();
        isLowPriority = isLowPriority && !isChildInGroup;
        if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {//默认布局 
            result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
        }

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

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

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

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

16、通过上面的代码可以看出,系统会根据不同的flag(不同通知类型)来加载不同的通知布局,我们这里以最简单的默认布局(createContentView)来分析,可以看到直接通过Notification.Builder来创建通知View:

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

17、继续来看Notification#Builder的createContentView方法:

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

public class Notification implements Parcelable
{
      ...
      //构造最终的通知UI布局
      public RemoteViews createContentView() {
            return createContentView(false);
        }

      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 */);
        }
      //获取布局文件,默认加载布局就是R.layout.notification_template_material_base
      private int getBaseLayoutResource() {
            return R.layout.notification_template_material_base;
        }
        ...
 }

18、applyStandardTemplate方法会给布局设置寬高颜色字体等属性:

public class Notification implements Parcelable
{
	...
   private RemoteViews applyStandardTemplate(int resId, TemplateBindResult result) {
            return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this), result);
        }

        private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
                TemplateBindResult result) {
            return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
                    .fillTextsFrom(this), result);
        }

        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);
            bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
            bindLargeIconAndReply(contentView, p, result);
            boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
            if (p.title != null) {
                contentView.setViewVisibility(R.id.title, View.VISIBLE);
                contentView.setTextViewText(R.id.title, processTextSpans(p.title));
                if (!p.ambient) {
                    setTextViewColorPrimary(contentView, R.id.title);
                }
                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));
                if (!p.ambient) {
                    setTextViewColorSecondary(contentView, textId);
                }
                contentView.setViewVisibility(textId, View.VISIBLE);
            }

            setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());

            return contentView;
        }
	...
}        

19、经过15-18步的分析,我们已经知道了通知栏视图的创建流程,现在让我们重新回到第14步,在AsyncInflationTask的doInBackground方法执行完毕,接着就会执行onPostExecute方法:

public class NotificationInflater {
	...
 public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
            implements InflationCallback, InflationTask {
		...
        @Override
        protected InflationProgress doInBackground(Void... params) {
			...
        }

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

20、AsyncInflationTask的onPostExecute方法会进一步调用NotificationInflater的apply方法,该方法会根据不同的通知类型做不同的事,这里选一个最普通的类型FLAG_REINFLATE_CONTENT_VIEW来看一下这个方法:

public class NotificationInflater {
	...
    public static CancellationSignal apply(InflationProgress result, int reInflateFlags,
            ExpandableNotificationRow row, boolean redactAmbient,
            RemoteViews.OnClickHandler remoteViewClickHandler,
            @Nullable InflationCallback callback) {
        NotificationData.Entry entry = row.getEntry();
        NotificationContentView privateLayout = row.getPrivateLayout();//获取id为R.id.expanded的视图View
        NotificationContentView publicLayout = row.getPublicLayout();//获取id为R.id.expandedPublic的视图View
        final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();

        int flag = FLAG_REINFLATE_CONTENT_VIEW;
        if ((reInflateFlags & flag) != 0) {//通知栏默认布局
            //是否是新增加的View
            boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView);
            //创建回调对象
            ApplyCallback applyCallback = new ApplyCallback() {
                @Override
                public void setResultView(View v) {
                    result.inflatedContentView = v;
                }

                @Override
                public RemoteViews getRemoteView() {
                    return result.newContentView;
                }
            };
            applyRemoteView(result, reInflateFlags, flag, row, redactAmbient,
                    isNewView, remoteViewClickHandler, callback, entry, privateLayout,
                    privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
                            NotificationContentView.VISIBLE_TYPE_CONTRACTED),
                    runningInflations, applyCallback);
        }  
		...
    }
	...
}

21、继续看applyRemoteView方法:

public class NotificationInflater {
	...
    static void applyRemoteView(final InflationProgress result,
            final int reInflateFlags, int inflationId,
            final ExpandableNotificationRow row,
            final boolean redactAmbient, boolean isNewView,
            RemoteViews.OnClickHandler remoteViewClickHandler,
            @Nullable final InflationCallback callback, NotificationData.Entry entry,
            NotificationContentView parentLayout, View existingView,
            NotificationViewWrapper existingWrapper,
            final HashMap<Integer, CancellationSignal> runningInflations,
            ApplyCallback applyCallback) {
        RemoteViews newContentView = applyCallback.getRemoteView();
        if (callback != null && callback.doInflateSynchronous()) {
            try {
            	//新增通知的情况
                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();
                }
            } 
            ...
        }
     ...
    }
}

22、重新回到第20步的apply方法,继续向下看,会继续调用一个关键方法finishIfDone,

public class NotificationInflater {
	...
    public static CancellationSignal apply(InflationProgress result, int reInflateFlags,
            ExpandableNotificationRow row, boolean redactAmbient,
            RemoteViews.OnClickHandler remoteViewClickHandler,
            @Nullable InflationCallback callback) {
		...
		finishIfDone(result, reInflateFlags, runningInflations, callback, row,
                redactAmbient);
        CancellationSignal cancellationSignal = new CancellationSignal();
        cancellationSignal.setOnCancelListener(
                () -> runningInflations.values().forEach(CancellationSignal::cancel));
        return cancellationSignal;
    }
	...
}

23、finishIfDone方法中比较关键的代码点在于,会通过调用privateLayout的setContractedChild方法将remouteViews添加到privateLayout视图中:

public class NotificationInflater {
	...
    private static boolean finishIfDone(InflationProgress result, int reInflateFlags,
            HashMap<Integer, CancellationSignal> runningInflations,
            @Nullable InflationCallback endListener, ExpandableNotificationRow row,
            boolean redactAmbient) {
        Assert.isMainThread();
        NotificationData.Entry entry = row.getEntry();
        NotificationContentView privateLayout = row.getPrivateLayout();
        NotificationContentView publicLayout = row.getPublicLayout();
        if (runningInflations.isEmpty()) {
            if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
                if (result.inflatedContentView != null) {
	                //result.inflatedContentView就是RemoteViews
	                //这里将result.inflatedContentView添加到privateLayout(R.id.expanded)
                    privateLayout.setContractedChild(result.inflatedContentView);
                }
                entry.cachedContentView = result.newContentView;
            }
			...
            if (endListener != null) {
            	//RemoteView加载到NotificationContentView之后会回调endListener的onAsyncInflationFinished方法
                endListener.onAsyncInflationFinished(row.getEntry());
            }
            return true;
        }
        return false;
    }
    ...
}

23、NotificationContentView的setContractedChild方法代码如下:

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

public class NotificationContentView extends FrameLayout {
	...
    public void setContractedChild(View child) {
        if (mContractedChild != null) {
            mContractedChild.animate().cancel();
            removeView(mContractedChild);
        }
        addView(child);
        mContractedChild = child;
        mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
                mContainingNotification);
    }
 	...
}

上面23个步骤具体讲述了系统通知管理服务端,从接收到通知消息到创建对应通知消息视图View,并将RemoteView加载到NotificationContentView的过程。

五、显示新的通知图标

1、接下来我们继续来看一下NotificationContentView创建完毕之后,是如何更新到系统状态栏视图上的。
继续看上面提到过的NotificationInflater的finishIfDone方法,该方法的最后会调用endListener的onAsyncInflationFinished方法:

public class NotificationInflater {
	...
    private static boolean finishIfDone(InflationProgress result, int reInflateFlags,
            HashMap<Integer, CancellationSignal> runningInflations,
            @Nullable InflationCallback endListener, ExpandableNotificationRow row,
            boolean redactAmbient) {
			...
            if (endListener != null) {
            	//RemoteView加载到NotificationContentView之后会回调endListener的onAsyncInflationFinished方法
                endListener.onAsyncInflationFinished(row.getEntry());
            }
            return true;
        }
        return false;
    }
    ...
}
	//回调接口
    public interface InflationCallback {
        void handleInflationException(StatusBarNotification notification, Exception e);
        void onAsyncInflationFinished(NotificationData.Entry entry);
        default boolean doInflateSynchronous() {
            return false;
        }
    }

2、endListener的回调很关键,该回调最早是在NotificationEntryManager的bindRow方法中设置的,也因此这里会层层回调,最终回调回NotificationEntryManager的onAsyncInflationFinished方法,该方法首先会判断是否是 新的通知,如果是则调用addEntry方法,该方法继续调用addNotificationViews方法,addNotificationViews方法在添加新的通知栏视图数据之后,会继续调用updateNotifications方法,updateNotifications方法先是对通知栏视图数据进行过滤和排序,然后再用mPresenter层的updateNotificationViews方法来刷新通知栏视图:

public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
        ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
        VisualStabilityManager.Callback {
        ...
    //填充数据并绑定到StatusBarNotificationPresenter中
    private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
            StatusBarNotification sbn, ExpandableNotificationRow row) {
        row.setExpansionLogger(this, entry.notification.getKey());
        row.setGroupManager(mGroupManager);
        row.setHeadsUpManager(mHeadsUpManager);
        row.setOnExpandClickListener(mPresenter);
        row.setInflationCallback(this);//这里会设置回调方法,这个回调很关键
   	 	...
    }
    ...
    //最终的回调方法
    @Override
    public void onAsyncInflationFinished(NotificationData.Entry entry) {
        mPendingNotifications.remove(entry.key);
        boolean isNew = mNotificationData.get(entry.key) == null;

        if (isNew && !entry.row.isRemoved()) {
	        //1如果是新的通知,调用addEntry方法
            addEntry(entry);
        } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
            mVisualStabilityManager.onLowPriorityUpdated(entry);
            mPresenter.updateNotificationViews();
        }
        entry.row.setLowPriorityStateUpdated(false);
    }
   
   private void addEntry(NotificationData.Entry shadeEntry) {
      	...
        //2继续调用addNotificationViews方法来增加通知栏视图
        addNotificationViews(shadeEntry);
      	...
    }
    
    protected void addNotificationViews(NotificationData.Entry entry) {
        if (entry == null) {
            return;
        }
        //添加通知视图数据对象
        mNotificationData.add(entry);
        tagForeground(entry.notification);
        //3继续调用updateNotifications方法
        updateNotifications();
    }
    
    public void updateNotifications() {
    	//对通知进行过滤和排序
        mNotificationData.filterAndSort();
        //4继续调用P层的updateNotificationViews方法刷新通知栏视图
        mPresenter.updateNotificationViews();
    }
}

3、NotificationEntryManager的mPresenter的实现者是在StatusBar,来看下状态栏对象的updateNotificationViews方法,在该方法中,首先是将通知视图添加到通知栏视图中,然后再更新通知栏视图和和视图图标:

public class StatusBar extends SystemUI implements DemoMode,
        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
        OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
        ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
        ...
    @Override
    public void updateNotificationViews() {
        ...    
        // 将通知视图添加到通知栏视图中
        mViewHierarchyManager.updateNotificationViews();

        updateSpeedBumpIndex();
        updateFooter();
        updateEmptyShadeView();

        updateQsExpansionEnabled();  
        // 这里不仅仅更新了通知面版的通知视图,也更新了状态栏的通知图标
        mNotificationIconAreaController.updateNotificationIcons();
    }
        ...    
}

4、将通知视图添加到通知栏视图中的具体方法在NotificationViewHierarchyManager的updateNotificationViews方法中:

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

public class NotificationViewHierarchyManager {
	...
    public void updateNotificationViews() {
        //拿到所有的通知条目,条目中封装了整个通知的信息
        ArrayList<NotificationData.Entry> 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++) {
            NotificationData.Entry ent = activeNotifications.get(i);
				...
                //经过过滤之后全部添加到toShow
                toShow.add(ent.row);
				...
            }

        }
		...
		//移除通知子条目
        removeNotificationChildren();
        //遍历需要显示的通知,添加到NotificationStackScrollLayout
        for (int i = 0; i < toShow.size(); i++) {
            View v = toShow.get(i);
            //如果是新增通知则需要添加,addContainerView
            if (v.getParent() == null) {
                mVisualStabilityManager.notifyViewAddition(v);
                //将通知条目视图添加到通知栏视图NotificationStackScrollLayout中
                mListContainer.addContainerView(v);
            }
        }
       //后面会对通知视图进行排序等操作
    }
	...
}

5、NotificationStackScrollLayout的addContainerView方法如下所示:

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

public class NotificationStackScrollLayout extends ViewGroup
        implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
        ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
        NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
        NotificationListContainer {
   	...
    @Override
    public void addContainerView(View v) {
        addView(v);
    }
   	...
}

6、分析完通知视图添加到通知栏视图的过程,接下来我们继续来看下是如何更新通知栏和状态栏视图的,该过程主要是在NotificationIconAreaController的updateNotificationIcons方法中实现的:

public class NotificationIconAreaController implements DarkReceiver {
	...
    public void updateNotificationIcons() {
		//更新状态栏图标
        updateStatusBarIcons();
        //向状态栏添加通知图标
        updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
                false /* hideRepliedMessages */);

        applyNotificationIconsTint();
    }
    public void updateStatusBarIcons() {
        updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
                false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */);
    }    
	...    
}

7、可以发现updateIconsForLayout方法:

public class NotificationIconAreaController implements DarkReceiver {
	...
  private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
            NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed,
            boolean hideRepliedMessages) {
        // toShow保存即将显示的图标
        ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                mNotificationScrollLayout.getChildCount());

        // 过滤通知,并保存需要显示的通知图标
        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, showAmbient, hideDismissed,hideRepliedMessages)) {
                	//添加图标
                    toShow.add(function.apply(ent));
                }
            }
        }

        ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons = new ArrayMap<>();
        ArrayList<View> toRemove = new ArrayList<>();
        for (int i = 0; i < hostLayout.getChildCount(); i++) {
            View child = hostLayout.getChildAt(i);
            if (!(child instanceof StatusBarIconView)) {
                continue;
            }
            if (!toShow.contains(child)) {
                boolean iconWasReplaced = false;
                StatusBarIconView removedIcon = (StatusBarIconView) child;
                String removedGroupKey = removedIcon.getNotification().getGroupKey();
                for (int j = 0; j < toShow.size(); j++) {
                    StatusBarIconView candidate = toShow.get(j);
                    if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon()))
                            && candidate.getNotification().getGroupKey().equals(removedGroupKey)) {
                        if (!iconWasReplaced) {
                            iconWasReplaced = true;
                        } else {
                            iconWasReplaced = false;
                            break;
                        }
                    }
                }
                if (iconWasReplaced) {
                    ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(removedGroupKey);
                    if (statusBarIcons == null) {
                        statusBarIcons = new ArrayList<>();
                        replacingIcons.put(removedGroupKey, statusBarIcons);
                    }
                    statusBarIcons.add(removedIcon.getStatusBarIcon());
                }
                toRemove.add(removedIcon);
            }
        }
        // removing all duplicates
        ArrayList<String> duplicates = new ArrayList<>();
        for (String key : replacingIcons.keySet()) {
            ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(key);
            if (statusBarIcons.size() != 1) {
                duplicates.add(key);
            }
        }
        replacingIcons.removeAll(duplicates);
        hostLayout.setReplacingIcons(replacingIcons);

        final int toRemoveCount = toRemove.size();
        for (int i = 0; i < toRemoveCount; i++) {
            hostLayout.removeView(toRemove.get(i));
        }

        final FrameLayout.LayoutParams params = generateIconLayoutParams();
        for (int i = 0; i < toShow.size(); i++) {
            StatusBarIconView 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) {
                if (hideDismissed) {
                    v.setOnDismissListener(mUpdateStatusBarIcons);
                }
                hostLayout.addView(v, i, params);
            }
        }

        hostLayout.setChangingViewPositions(true);
        // Re-sort notification icons
        final int childCount = hostLayout.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View actual = hostLayout.getChildAt(i);
            StatusBarIconView expected = toShow.get(i);
            if (actual == expected) {
                continue;
            }
            hostLayout.removeView(expected);
            hostLayout.addView(expected, i);
        }
        hostLayout.setChangingViewPositions(false);
        hostLayout.setReplacingIcons(null);
    }
	...
}

到此通知已经全部的创建,加载以及添加完成。

五、总结

本篇文章大概梳理了通知布局的加载,数据填充以及添加和显示,整个流程大概就是从framework拿到StatusBarNotification,封装成NotificationEntry,之后再根据不同通知类型加载不同布局,如果没有自定义布局则加载默认布局,创建RemoteView,添加到ExpandableNotificationRow的中的id为R.id.expanded的NotificationContentView,之后再将ExpandableNotificationRow添加到NotificationStackScrollLayout,整个通知布局如下图:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值