android12 SystemUI下拉通知栏的加载流程

1. SystemUI 服务启动与窗口创建

SystemUI 是一个系统应用,由 SystemServer 进程负责启动,入口是 SystemUIApplication

启动后,核心服务 StatusBarstart() 方法会被调用。在这里,它会执行 createAndAddWindows() 方法,通过 WindowManager 为状态栏和通知栏创建独立的系统窗口。

scss 复制代码
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
    //创建状态栏视图
    makeStatusBarView(result);
    //添加下拉通知面板窗口
    mNotificationShadeWindowController.attach();
    ...
}

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    final Context context = mContext;
    ...
    //创建下拉面板视图,创建状态栏窗口视图
    inflateStatusBarWindow();
    ...
//为下拉面板根视图设置触摸监听器,处理触摸事件分发
mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());

    //初始化通知列表控制器
    mStackScrollerController =
            mNotificationPanelViewController.getNotificationStackScrollLayoutController();
    //获取 `NotificationStackScrollLayout` 视图实例
    mStackScroller = mStackScrollerController.getView();
    //获取通知列表容器接口,用于操作通知列表
    NotificationListContainer notifListContainer =
            mStackScrollerController.getNotificationListContainer();
    mNotificationLogger.setUpWithContainer(notifListContainer);

    ...

}

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
private void inflateStatusBarWindow() {
   //创建下拉面板视图,SuperStatusBarViewFactory
    mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView();
    //获取构建器,将下拉面板视图作为依赖注入到组件中
    StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
            .statusBarWindowView(mNotificationShadeWindowView).build();
    //获取下拉面板窗口控制器
    mNotificationShadeWindowViewController = statusBarComponent
            .getNotificationShadeWindowViewController();
    
    ...
    //获取通知面板控制器
    mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
    ...
}

2. 加载通知面板根布局:super_notification_shade.xml

通过 SuperStatusBarViewFactorygetNotificationShadeWindowView() 方法加载并解析 R.layout.super_notification_shade。加载完成后,NotificationShadeWindowControllerattach() 方法会将对应的 UI 添加到屏幕上。

ini 复制代码
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
public NotificationShadeWindowView getNotificationShadeWindowView() {
    if (mNotificationShadeWindowView != null) {
        return mNotificationShadeWindowView;
    }

    mNotificationShadeWindowView = (NotificationShadeWindowView)
            mInjectionInflationController.injectable(
            LayoutInflater.from(mContext)).inflate(R.layout.super_notification_shade,
            /* root= */ null);
    ...

    return mNotificationShadeWindowView;
}

//frameworks/base/packages/SystemUI/res/layout/super_notification_shade.xml
<com.android.systemui.statusbar.phone.NotificationShadeWindowView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    ...

    <include layout="@layout/status_bar_expanded"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="invisible" />

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

3. 下拉面板的视图层次:status_bar_expanded.xml

status_bar_expanded.xml 构建了下拉面板的完整视图层次:

  • 根容器是 NotificationPanelView
  • 内部包含了 NotificationsQuickSettingsContainer,它同时容纳了快捷开关面板(QS)和通知列表。
  • 通知列表的滚动容器是 NotificationStackScrollLayout
ini 复制代码
//frameworks/base/packages/SystemUI/res/layout/status_bar_expanded.xml
<com.android.systemui.statusbar.phone.NotificationPanelView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/notification_panel"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent">
    ...
    <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="@integer/notification_panel_layout_gravity"
        android:id="@+id/notification_container_parent"
        android:clipToPadding="false"
        android:clipChildren="false">

        ...
        #快捷开关面板(QS)的容器
        <FrameLayout
            android:id="@+id/qs_frame"
            android:layout="@layout/qs_panel"
            android:layout_width="@dimen/qs_panel_width"
            android:layout_height="0dp"
            android:clipToPadding="false"
            android:clipChildren="false"
            systemui:viewType="com.android.systemui.plugins.qs.QS"
            systemui:layout_constraintStart_toStartOf="parent"
            systemui:layout_constraintEnd_toEndOf="parent"
            systemui:layout_constraintTop_toTopOf="parent"
            systemui:layout_constraintBottom_toBottomOf="parent"
        />

        ...

      #通知列表的滚动容器 
       <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
            android:id="@+id/notification_stack_scroller"
            android:layout_marginTop="@dimen/notification_panel_margin_top"
            android:layout_width="@dimen/notification_panel_width"
            android:layout_height="match_parent"
            android:layout_marginBottom="@dimen/close_handle_underlap"
            android:importantForAccessibility="no"
            systemui:layout_constraintStart_toStartOf="parent"
            systemui:layout_constraintEnd_toEndOf="parent"
        />

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

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

4. 触发面板显示:makeExpandedVisible()

在触摸事件处理阶段,当手势确认要展开面板时,PhoneStatusBarViewonPanelPeeked() 方法会被调用。该方法进而调用 StatusBarmakeExpandedVisible() 方法。这个方法的核心作用,就是将整个下拉面板的窗口(NotificationShadeWindowView)设置为可见状态。PhoneStatusBarView是状态栏status_bar.xml的根布局容器。

csharp 复制代码
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
public boolean onTouchEvent(MotionEvent event) {
    //mBar是StatusBar
    boolean barConsumedEvent = mBar.interceptTouchEvent(event);
    ...
    //如果 StatusBar 消费了事件,返回 true;否则交给父类处理(默认的 View 触摸处理)
    return barConsumedEvent || super.onTouchEvent(event);
}
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
public boolean onTouchEvent(MotionEvent event) {
    //检查面板是否启用
    if (!panelEnabled()) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
        }
        return false; //面板被禁用,不处理事件
    }

    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        final PanelViewController panel = mPanel;
        if (panel == null) {
            // 面板不存在,消费掉这个手势
            return true;
        }
        // 检查面板是否启用
        boolean enabled = panel.isEnabled();
        
        if (!enabled) {
            // 面板被禁用,消费掉这个手势
            return true;
        }
    }
    //将事件转发给面板的视图
    //mPanel = PanelViewController是抽象类,实现类NotificationPanelViewController
    //`mPanel.getView()`返回的是`NotificationPanelView`
    return mPanel == null || mPanel.getView().dispatchTouchEvent(event);
}

mPanel.getView() 返回的是 NotificationPanelView。当调用它的 dispatchTouchEvent(event) 时:

  1. Android 框架会检查 View 是否设置了 OnTouchListener
  2. 如果设置了,会先调用 OnTouchListener.onTouch()
  3. 由于 TouchHandler 被注册为 OnTouchListener,所以 TouchHandler.onTouch() 被调用
  4. TouchHandler.onTouch() 内部,会进一步调用 NotificationPanelViewController 的具体处理逻辑。

TouchHandler 是什么

PanelViewController.TouchHandler 是一个接口,它继承自 View.OnTouchListener,并扩展了 onInterceptTouchEvent 方法。在 NotificationPanelViewController 中,通过 createTouchHandler() 方法创建具体的实现。并通过 mView.setOnTouchListener(touchHandler) 将其注册到 NotificationPanelView 上。

typescript 复制代码
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
public boolean onInterceptTouchEvent(MotionEvent event) {
    //PanelViewController.TouchHandler mTouchHandler
    return mTouchHandler.onInterceptTouchEvent(event);
}


//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
public PanelViewController(PanelView view,
        FalsingManager falsingManager, DozeLog dozeLog,
        KeyguardStateController keyguardStateController,
        ...) {
    ...
    mView.setOnTouchListener(createTouchHandler());
}

protected TouchHandler createTouchHandler() {
    return new TouchHandler();
}


public class TouchHandler implements View.OnTouchListener {
    public boolean onInterceptTouchEvent(MotionEvent event) {
        ...
        return (mView.getVisibility() != View.VISIBLE);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        ...
        return !mGestureWaitForTouchSlop || mTracking;
    }
}

5. 填充通知内容:NotificationContentInflater

面板可见后,就需要向 NotificationStackScrollLayout 中填充具体的通知内容了。这个构建过程由 NotificationContentInflater 类负责。

  • 收到通知 :当应用发出通知,NotificationManager 服务会通知 SystemUI。
  • 开始构建 :SystemUI 收到后,会调用 NotificationContentInflaterinflateNotificationViews() 方法。
  • 创建视图:该方法会根据通知的样式(如标准、大图等),加载不同的布局文件,为每条通知创建独立的视图对象。
  • 添加到列表 :最后,这些构建好的通知视图会被添加到 NotificationStackScrollLayout 中,呈现在你眼前。
相关推荐
小七在进步1 小时前
C++入门(3)
android·java·c++
凡泰AI2 小时前
如何为政务 APP 搭建开放平台,实现多部门、第三方供应商标准化入驻与统一管理
android·大数据·小程序·uni-app·app·政务
ImTryCatchException3 小时前
Android 画中画(PiP)小窗播放:怎么做、以及它到底是怎么工作的
android·pip
flower_drop13 小时前
基于 WebUSB 与 CDP:在浏览器端实现 Android 设备通信与无证书抓包实践
android·chrome·测试工具·adb·bug·edge浏览器
撩得Android一次心动15 小时前
Jetpack Compose 知识点整理1【个人用】
android·学习·kotlin·android jetpack·compose
ForteScarlet16 小时前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
方白羽17 小时前
Android Studio 自动国际化插件:SmartI18n
android·android studio·intellij idea
码农coding20 小时前
android12 状态栏图标的加载流程
android
hai_android21 小时前
Kotlin 协程作用域源码剖析
android·kotlin