android12 状态栏图标的加载流程

状态栏图标的加载流程,本质上是数据与视图的分离与同步过程StatusBarIconController 作为数据中心,接收来自系统各处的状态变化,并将变化推送给所有已注册的 IconManager,由后者负责实际的视图更新。

核心角色与职责

  • StatusBarIconController中央控制器 。它是控制状态图标显示的核心接口,负责接收所有状态栏图标的更新请求,并管理所有IconManager

  • StatusBarIconControllerImpl具体实现类 。维护一个 IconManager 列表 (mIconGroups),接收系统回调并分发状态。其构造函数会根据 config_statusBarIcons 数组预定义好所有图标槽位(Slots)。

csharp 复制代码
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java)
  public StatusBarIconControllerImpl(
              Context context,
              CommandQueue commandQueue,
              DemoModeController demoModeController) {
          //调用StatusBarIconList构造函数
          super(context.getResources().getStringArray(
                  com.android.internal.R.array.config_statusBarIcons));
          ...
 }
 
 //frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
//StatusBarIconList构造函数
public StatusBarIconList(String[] slots) {
    final int N = slots.length;
    for (int i=0; i < N; i++) {
        mSlots.add(new Slot(slots[i], null));
    }
}
  • IconManager视图管理者 。负责实际创建、添加和更新 StatusBarIconView。主要有两种:

    • DarkIconManager:用于主状态栏 (CollapsedStatusBarFragment)。
    • TintedIconManager:用于锁屏状态栏或快捷设置面板。
  • PhoneStatusBarPolicy策略决策者 。负责"决策"哪些图标该显示,根据系统状态(如蓝牙开关)调用 StatusBarIconControllersetIconVisibility 等方法。

  • StatusBarIconView实际视图 。状态栏中每个图标的具体 View 实例。

  • PhoneStatusBarView整个状态栏的根布局容器 ,而 StatusBarIconView 则是状态栏里每一个具体图标的视图单元 。 它承载了状态栏上所有的可见元素。所有你看到的图标、文字,最终都是被添加到这个容器里的某个子区域中。它是整个状态栏"下拉"手势的起点。当你触摸并向下滑动屏幕顶部时,PhoneStatusBarView 会捕获这个触摸事件,并协同 StatusBar 服务,触发通知面板和快捷设置面板的展开动画。

加载与显示流程

  1. 系统启动与注册SystemUI 启动时,StatusBar 服务初始化,通过 Dependency 机制创建 StatusBarIconControllerImpl 单例。随后,CollapsedStatusBarFragment 等视图会创建对应的 IconManager(如 DarkIconManager),并通过 addIconGroup() 将其注册到 StatusBarIconControllerImpl 中。
  2. 图标初始化 :注册后,StatusBarIconControllerImpl 会立即遍历所有预定义的图标槽位,通知 IconManager 创建对应的 StatusBarIconView 并添加到布局中。PhoneStatusBarPolicy 也会在构造时完成对所有系统状态图标的初始化和状态监听。
  3. 图标数据更新 :当系统状态变化(如连接Wi-Fi)时,PhoneStatusBarPolicy 监听到事件,调用 setIcon() 等方法。StatusBarIconControllerImpl 更新内部数据,并遍历 mIconGroups,调用每个 IconManager 的相应方法。
  4. 图标视图更新IconManager 收到通知后,负责更新对应的 StatusBarIconView,比如更新图标资源或可见性。
ini 复制代码
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
private final StatusBarIconController mStatusBarIconController;
public StatusBar(
        Context context,
        ...
        PhoneStatusBarPolicy phoneStatusBarPolicy,
        ...
        NotificationIconAreaController notificationIconAreaController,
        ...
        StatusBarIconController statusBarIconController,
        ...
        Optional<StartingSurface> startingSurfaceOptional) {
    super(context);
    mNotificationsController = notificationsController;
    ...
    mIconPolicy = phoneStatusBarPolicy;
    ...
    mNotificationIconAreaController = notificationIconAreaController;
    ...
    mStatusBarIconController = statusBarIconController;
    ...
    DateTimeView.setReceiverHandler(timeTickHandler);
}

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java)
public void start() {
    ...
    //1.创建状态栏和下拉栏窗口和视图
    createAndAddWindows(result);
    ...
    //2.图标策略初始化
    mIconPolicy.init();
    ...

}

第1步:createAndAddWindows()

这一步是视图层 的初始化, PhoneStatusBarViewStatusBarIconView 直接相关。

  • 它会调用 makeStatusBarView(),最终完成以下工作:

    1. 加载 status_bar.xml 布局,实例化出 PhoneStatusBarView
    2. 通过 CollapsedStatusBarFragmentPhoneStatusBarView 添加到窗口中。
    3. 创建 IconManager(如 DarkIconManager),并通过 StatusBarIconController.addIconGroup() 将其注册为观察者。
    4. 注意:此时 IconManager 已注册,但还没有收到任何图标数据,所以状态栏此时是空的或只有占位符。
scss 复制代码
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
    //创建状态栏视图
    makeStatusBarView(result);
    ...
}

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    final Context context = mContext;
    ...
    //监听 `CollapsedStatusBarFragment` 的创建完成事件
   FragmentHostManager.get(mPhoneStatusBarWindow)
        //添加标签监听器,当带有 `TAG` 的 Fragment 被创建或重新创建时触发回调
        .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
            CollapsedStatusBarFragment statusBarFragment =
                    (CollapsedStatusBarFragment) fragment;

            PhoneStatusBarView oldStatusBarView = mStatusBarView;
            //从 Fragment 中获取新创建的 `PhoneStatusBarView` 视图
            mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
            //将 `StatusBar` 实例注入到视图,用于回调状态栏事件
            mStatusBarView.setBar(this);
            //注入面板控制器,用于面板展开状态查询
            mStatusBarView.setPanel(mNotificationPanelViewController);
            //注入遮罩控制器,用于控制遮罩层
            mStatusBarView.setScrimController(mScrimController);
            //设置展开状态变化监听器列表
            mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
            for (ExpansionChangedListener listener : mExpansionChangedListeners) {
            //向监听器发送当前展开状态(初始值)
                sendInitialExpansionAmount(listener);
            }

           mNotificationPanel.notifyBarPanelExpansionChanged().
           //检查是否有固定的 HeadsUp 通知(即正在显示的 HeadsUp)
            if (mHeadsUpManager.hasPinnedHeadsUp()) {
               //通知面板展开状态已变化,更新视图 mNotificationPanelViewController.notifyBarPanelExpansionChanged();
            }
            mStatusBarView.setBouncerShowing(mBouncerShowing);
            ...
          
           //设置状态栏视图到面板窗口控制器
           mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
            checkBarModes();
        }).getFragmentManager()
        .beginTransaction()
        .replace(R.id.status_bar_container,
                //创建折叠状态栏fragment
                new CollapsedStatusBarFragment(
                        mOngoingCallController,//正在进行的通话控制器
                        mAnimationScheduler,//动画调度器
                        mStatusBarLocationPublisher,
                        mNotificationIconAreaController,//通知图标区域控制器
                        mFeatureFlags,
                        mStatusBarIconController,//状态栏图标控制器
                        mKeyguardStateController,//锁屏状态控制器
                        mNetworkController,//网络控制器(信号/WiFi)
                        mStatusBarStateController,//状态栏状态控制器
                        this,
                        mCommandQueue
                ),
                CollapsedStatusBarFragment.TAG)
        .commit();

    ...
   
      }

    ...

}

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java)
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
  //是状态栏(非展开状态,即 `Collapsed` 状态)的根视图。在这个容器里,摆放着
  //状态栏左侧的通知图标、右侧的系统状态图标等所有可见元素
  //处理下拉手势,协调面板状态,响应深色/浅色主题
    mStatusBar = (PhoneStatusBarView) view;
    ...
    //初始化 DarkIconManager,管理状态栏右侧的图标
    mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags);
    ...
    //状态栏图标控制器
    mStatusBarIconController.addIconGroup(mDarkIconManager);
    //系统图标区域(右侧,包含信号、WiFi、电量等)
    mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
    ...
    //初始隐藏系统图标区域
    //这些组件会在系统状态更新时(如锁屏状态变化、网络连接)才显示。
    //这避免了在 Fragment 初始化时出现闪烁。
    showSystemIconArea(false);
    ...
    //初始化通知图标区域(左侧,显示应用通知图标)
    initNotificationIconArea();
    mAnimationScheduler.addCallback(this);
}

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
public void addIconGroup(IconManager group) {
    mIconGroups.add(group);
    List<Slot> allSlots = getSlots();
    for (int i = 0; i < allSlots.size(); i++) {
        Slot slot = allSlots.get(i);
        ...

        for (StatusBarIconHolder holder : holders) {
            ...
            //调用StatusBarIconController的DarkIconManager的onIconAdded方法
            group.onIconAdded(viewIndex, slot.getName(), hidden, holder);
        }
    }
}

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
protected void onIconAdded(int index, String slot, boolean blocked,
        StatusBarIconHolder holder) {
    //步骤 1: 创建并添加图标视图
    StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
    //步骤 2: 注册主题监听
    mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);
}


protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
        StatusBarIconHolder holder) {
    //是否包含被阻止显示的图标
    if (mBlockList.contains(slot)) {
        blocked = true;
    }
    switch (holder.getType()) {
       //处理普通图标,如电池、蓝牙、闹钟等。
        case TYPE_ICON:
            return addIcon(index, slot, blocked, holder.getIcon());
       //处理Wi-Fi 信号图标,需要显示信号强度(如 0~4 格)
        case TYPE_WIFI:
            return addSignalIcon(index, slot, holder.getWifiState());
        //处理移动网络信号图标,同样需要显示信号强度,还可能包含运营商类型(如 4G、5G)
        case TYPE_MOBILE:
            return addMobileIcon(index, slot, holder.getMobileState());
    }

    return null;
}


//创建普通图标,如电池、蓝牙、闹钟等。
protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
        StatusBarIcon icon) {
    //1. 创建图标视图实例
    StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
    //2. 绑定图标数据
    view.set(icon);
    //3. 添加到父容器
    mGroup.addView(view, index, onCreateLayoutParams());
    return view;
}

第2步:mIconPolicy.init()

这一步是数据层 的初始化,PhoneStatusBarPolicy.init()

  • 它完成的工作包括:

    1. 注册各种系统广播接收器和服务回调,开始监听系统状态。
    2. 主动查询所有系统服务的当前状态 (如 Wi-Fi 是否开启、电量多少等),并调用 StatusBarIconController.setIcon() 将初始数据推送过去。
    3. 一旦 setIcon() 被调用,StatusBarIconController 就会通知所有已注册的 IconManager,触发 onIconAddedaddHolderaddIcon 调用链,最终创建出 StatusBarIconView 并添加到 PhoneStatusBarView 中。
scss 复制代码
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
public void init() {
     //广播接收器注册,监听的事件包括:耳机插拔、SIM卡状态变化、TTY模式变化、
     //托管配置文件变化等。
    IntentFilter filter = new IntentFilter();
    filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
    filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
    ...
    mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
    //监听铃声模式的变化
    Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen);
    //更新音量/静音/振动图标
    mRingerModeTracker.getRingerMode().observeForever(observer);
    mRingerModeTracker.getRingerModeInternal().observeForever(observer);

    ...
    //更新蓝牙图标
    updateBluetooth();

    //闹钟图标等一系列图标初始化
    //调用StatusBarIconControllerImpl.setIcon()
    mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
    mIconController.setIconVisibility(mSlotAlarmClock, false);

    ...
    //各种系统服务注册回调监听器
    mRotationLockController.addCallback(this);
    //蓝牙图标
    mBluetooth.addCallback(this);
    ...
    mCommandQueue.addCallback(this);
}

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
    //1. 根据 slot 查找索引
    int index = getSlotIndex(slot);
    // 2. 尝试获取已存在的图标数据
    StatusBarIconHolder holder = getIcon(index, 0);
    if (holder == null) {
        StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
                Icon.createWithResource(
                        mContext, resourceId), 0, 0, contentDescription);
        holder = StatusBarIconHolder.fromIcon(icon);
        //图标不存在,执行"新增"逻辑 
        setIcon(index, holder);
    } else {
        holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
        holder.getIcon().contentDescription = contentDescription;
        //图标已存在,执行"更新"逻辑
        handleSet(index, holder);
    }
}


public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
    // 1. 判断是新增还是更新
    boolean isNew = getIcon(index, holder.getTag()) == null;
    super.setIcon(index, holder);

    if (isNew) {
        //新增:通知创建视图
        addSystemIcon(index, holder);
    } else {
        //更新:通知更新视图
        handleSet(index, holder);
    }
}


private void addSystemIcon(int index, StatusBarIconHolder holder) {
    //1. 获取图标的槽位名称
    String slot = getSlotName(index);
    // 2. 计算图标在视图中的位置索引
    int viewIndex = getViewIndex(index, holder.getTag());
    // 3. 检查该槽位是否被配置为隐藏
    boolean hidden = mIconHideList.contains(slot);
    // 4. 遍历并通知所有 IconManager
    //这个回调在mStatusBarIconController.addIconGroup(mDarkIconManager)中已分析
    //调用StatusBarIconController的DarkIconManager的onIconAdded方法更新图标
    mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder));
}


private void handleSet(int index, StatusBarIconHolder holder) {
    // 1. 计算图标在视图中的位置索引
    int viewIndex = getViewIndex(index, holder.getTag());
    // 2. 遍历并通知所有 IconManager 执行更新
    mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
}


//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
    switch (holder.getType()) {
        //更新普通图标(如电池、蓝牙)的图片资源和描述
        case TYPE_ICON:
            onSetIcon(viewIndex, holder.getIcon());
            return;
        //更新 Wi-Fi 信号强度格数
        case TYPE_WIFI:
            onSetSignalIcon(viewIndex, holder.getWifiState());
            return;
        //更新移动信号强度格数和网络类型(如 4G/5G)
        case TYPE_MOBILE:
            onSetMobileIcon(viewIndex, holder.getMobileState());
        default:
            break;
    }
}

从 Android U(14)开始,Wi-Fi 和移动数据图标的逻辑已迁移到全新的"数据管道"架构,而其他大部分系统图标仍沿用上述传统机制。

相关推荐
hai_android2 小时前
Kotlin 协程作用域源码剖析
android·kotlin
Yeyu2 小时前
Android 画中画(PiP)小窗播放:怎么做、以及它到底是怎么工作的
android
千里马学框架2 小时前
安卓系统性能优化高级实战开发专题--开机,app冷启动优化
android·智能手机·性能优化·framework·性能·开机优化·app冷启动优化
律宏阔3 小时前
Android 手机通过 adblib 使用 ADB Wi-Fi 控制 Android 9 开发板
android
律宏阔3 小时前
Android App 里实现开机动画替换
android
消失的旧时光-19434 小时前
Android 系统层扫盲 05:Android 开机后发生了什么?从 Bootloader 到 Launcher
android·zygote·fork·aosp·cow
hunterandroid5 小时前
Android 线上卡顿治理:从布局层级到主线程负载的全链路排查
android
恋猫de小郭5 小时前
Dart Skills CLI 1.0 :AI 时代的 Dart 交付支持
android·前端·flutter
2501_916007475 小时前
使用Apple Dashboards显示和自定iOS应用性能指标与数据可视化指南
android·ios·小程序·https·uni-app·iphone·webview