SystemUI StatusBar 手机信号相关图标的显示和更新流程分析

SystemUI StatusBar 手机信号相关图标的显示和更新流程分析

以下源码基于AC8015版 android 9.0

StatusBar的图标控制器

SystemUIStatusBar的图标控制器实现类为StatusBarIconControllerImpl,其继承StatusBarIconController接口,用于跟踪所有图标的状态,并将对应的状态发送给注册的图标管理器(IconManagers);

StatusBarIconControllerImpl的创建和获取

StatusBarIconControllerStatusBrstart方法一步步调用到makeStatusBarView之后从Dependency中获取,而StatusBrstart方法则是随着SystemUI的启动而启动,具体是在SystemBars中通过反射实例化,并调用其start方法。有关start方法的源码如下:
/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 {
        ....
        private NetworkController mNetworkController;
        protected StatusBarIconController mIconController;
        private PhoneStatusBarPolicy mIconPolicy;
        private StatusBarSignalPolicy mSignalPolicy;
        ....
        @Override
        public void start() {
            ....
            // 网络控制器,之后移动信号图标显示和更新流程相关
            mNetworkController = Dependency.get(NetworkController.class);
            ....
            // 内部会构造mIconController
            createAndAddWindows();
            ....
            // 最后,调用图标策略以安装/更新所有图标。
            mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
            mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
            ....
        }    
        ....
    public void createAndAddWindows() {
        addStatusBarWindow();
    }
    private void addStatusBarWindow() {
        makeStatusBarView();
        ....
    }
    protected void makeStatusBarView() {
        final Context context = mContext;
        ....
        // StatusBarIconControllerImpl获取
        mIconController = Dependency.get(StatusBarIconController.class);
        ....
        // 快速设置相关内容加载
        View container = mStatusBarWindow.findViewById(R.id.qs_frame);
            if (container != null) {
                FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
                ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
                        Dependency.get(ExtensionController.class)
                        ....
                        .build());
                // 传入mIconController创造一个快速设置Host
                final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
                        mIconController);
                ....
                fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
                    QS qs = (QS) f;
                    if (qs instanceof QSFragment) {
                        ((QSFragment) qs).setHost(qsh);
                        mQSPanel = ((QSFragment) qs).getQsPanel();
                        mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
                        mKeyguardStatusBar.setQSPanel(mQSPanel);
                    }
                });
            }
    }
    ....
}

Dependency通过懒加载提前初始化SystemUI整个生命周期都需要存在,同时适用于SystemUI大部分地方的类,其在SystemUIApplicationSystemBars启动前)启动时进行初始化,本质也是通过反射实例化,并调用其start方法。其中与StatusBarIconControllerImpl相关源码如下:
/SystemUI/src/com/android/systemui/Dependency.java

public class Dependency extends SystemUI {
    private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();
    @Override
    public void start() {
        ....
        mProviders.put(StatusBarIconController.class, () ->
                new StatusBarIconControllerImpl(mContext));
                
        mProviders.put(NetworkController.class, () ->
                new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),
                        getDependency(DeviceProvisionedController.class)));
    }
    
    ....
    public static <T> T get(Class<T> cls) {
        if(sDependency == null) {
            Log.d("Dependency","sDependency is null");
            return null;
        }
        return sDependency.getDependency(cls);
    }
}

StatusBarIconControllerImpl的构造方法

/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl

public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
        ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
        private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
        private Context mContext;
        ....
        public StatusBarIconControllerImpl(Context context) {
            // 这里根据系统配置的config_statusBarIcons添加对应的Icon槽,用于之后Icon的加载
            super(context.getResources().getStringArray(
                    com.android.internal.R.array.config_statusBarIcons));
            Dependency.get(ConfigurationController.class).addCallback(this);

            mContext = context;
            ....
            // 设置CommandQueue监听图标相关回调
            SysUiServiceProvider.getComponent(context, CommandQueue.class)
                    .addCallbacks(this);
            Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);
        }
            
}

StatusBarIconControllerImpl继承了StatusBarIconController接口,当我们在StatusBar中获取到它的实例后,还会将它传给PhoneStatusBarPolicyStatusBarSignalPolicy对象。PhoneStatusBarPolicy控制启动时装载哪些图标(蓝牙,定位等),而StatusBarSignalPolicy*控制网络信号图标(移动网络,WiFi,以太网)的变化。

到这里就完成了状态栏图标控制器StatusBarIconControllerImpl的构造。之后要看下具体的Icon显示和更新是如下传入到这里控制的。这里只分析移动信号图标的显示和更新流程,其他图标可以该流程参考。

StatusBar的手机信号处理类

Icon的信号处理控制逻辑基于NetworkControllerImpl,其继承自BroadcastReceiver,类内部监听网络状态变化,SIM卡状态变化等广播,执行相应的操作。

NetworkControllerImpl的创建

NetworkControllerImpl该类在Dependencystart方法中构造,StatusBarstart方法中获取并赋值给其内部的mNetworkController变量。过程与StatusBarIconControllerImpl类似,这里就略过了。

NetworkControllerImpl构造方法

/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java

public class NetworkControllerImpl extends BroadcastReceiver
        implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider,
        ConfigurationChangedReceiver, Dumpable {
    ....
    // WiFi信号控制器
    /*final */WifiSignalController mWifiSignalController = null;

    // 以太网信号控制器
    @VisibleForTesting
    final EthernetSignalController mEthernetSignalController;

    // 移动网络信号控制器列表(每个SIM卡对应一个)
    @VisibleForTesting
    final SparseArray<MobileSignalController> mMobileSignalControllers = new SparseArray<>();
    
    // 移动网络,WiFi网络,网络连接状态管理器
    private final TelephonyManager mPhone;
    private final WifiManager mWifiManager;
    private final ConnectivityManager mConnectivityManager;
    // 每一张SIM卡都对应一个Subscription,用谁家的SIM卡就相当于订阅(Subscription)谁家的业务。
    // 而每一个Subscription对应一个MobileSignalController
    private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
    // 处理程序,用于接收所有广播。
    private final Handler mReceiverHandler;
    // 处理程序,所有回调都在其上进行。
    private final CallbackHandler mCallbackHandler;
    
    public NetworkControllerImpl(Context context, Looper bgLooper,
            DeviceProvisionedController deviceProvisionedController) {
        // 1.进入构造
        this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
                (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
                SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
                new CallbackHandler(),
                new AccessPointControllerImpl(context),
                new DataUsageController(context),
                new SubscriptionDefaults(),
                deviceProvisionedController);
        // 3. 设置广播监听
        mReceiverHandler.post(mRegisterListeners);
    }
    // 2.构造控制器,管理器和设置连接状态监听
    @VisibleForTesting
    NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
            TelephonyManager telephonyManager, WifiManager wifiManager,
            SubscriptionManager subManager, Config config, Looper bgLooper,
            CallbackHandler callbackHandler,
            AccessPointControllerImpl accessPointController,
            DataUsageController dataUsageController,
            SubscriptionDefaults defaultsHandler,
            DeviceProvisionedController deviceProvisionedController) {
                mContext = context;
        // 配置各种变量
        ....
        // 内部处理所有广播监听
        mReceiverHandler = new Handler(bgLooper);
        // 内部处理所有回调监听,通过addCallback向其内部的监听列表添加监听对象。
        mCallbackHandler = callbackHandler;
        ....
        mConnectivityManager = connectivityManager;
        // telephony
        mPhone = telephonyManager;

        // wifi
        mWifiManager = wifiManager;     
        ....
        if (mWifiManager != null) {
            mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
                    mCallbackHandler, this, mWifiManager);
        }
        mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);
        ....
        // 用于设置连接状态的监听
        ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
            private Network mLastNetwork;
            private NetworkCapabilities mLastNetworkCapabilities;

            @Override
            public void onCapabilitiesChanged(
                Network network, NetworkCapabilities networkCapabilities) {
                boolean lastValidated = (mLastNetworkCapabilities != null) &&
                    mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
                boolean validated =
                    networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);

                // 这个回调会被调用很多次(例如当信号强度发生变化时),
                // 这里可以避免在连接状态保持不变的情况下更新图标。
                if (network.equals(mLastNetwork) &&
                    networkCapabilities.equalsTransportTypes(mLastNetworkCapabilities) &&
                    validated == lastValidated) {
                    return;
                }
                mLastNetwork = network;
                mLastNetworkCapabilities = networkCapabilities;
                updateConnectivity();
            }
        };
        mConnectivityManager.registerDefaultNetworkCallback(callback, mReceiverHandler);
    }
    
    ....
    private final Runnable mRegisterListeners = new Runnable() {
        @Override
        public void run() {
            registerListeners();
        }
    };
    // 3.设置广播监听
    private void registerListeners() {
        ....
        IntentFilter filter = new IntentFilter();
        // wifi相关
        // wifi信号强度广播
        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
        // wifi状态变化广播
        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        // wifi连接状态改变
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        
        // 移动网络相关
        // SIM卡状态改变
        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
        // 数据语音订阅修改
        filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
        filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
        filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
        filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
        // 连接状态相关
        // 网络连接状态发生变化
        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        // 网络连接可能不好
        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
        // 切换飞行模式时
        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
        mContext.registerReceiver(this, filter, null, mReceiverHandler);
        mListening = true;

        // 4.更新移动网络控制器
        updateMobileControllers();
    }
    
    private void updateMobileControllers() {
        if (!mListening) {
            return;
        }
        doUpdateMobileControllers();
    }
    @VisibleForTesting
    void doUpdateMobileControllers() {
        // 获得当前插入的SIM卡的列表
        List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
        ....
        // 设置当前插入SIM卡的Subscriptions(订阅信息)
        setCurrentSubscriptions(subscriptions);
        ....
    }
    @VisibleForTesting
    void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) {
        mCurrentSubscriptions = subscriptions;
        
        // 建立一个缓存列表存放之前的的MobileSignalController列表
        SparseArray<MobileSignalController> cachedControllers =
                new SparseArray<MobileSignalController>();
        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
            cachedControllers.put(mMobileSignalControllers.keyAt(i),
                    mMobileSignalControllers.valueAt(i));
        }
        // 清空当前的MobileSignalController列表
        mMobileSignalControllers.clear();
        
        // 获得当前SIM卡数量
        final int num = subscriptions.size();
        
        // 每个卡有一个对应的MobileSignalController
        for (int i = 0; i < num; i++) {
            int subId = subscriptions.get(i).getSubscriptionId();
            // 如果之前缓存列表存在这个SIM卡的控制器直接复用,否则新建
            if (cachedControllers.indexOfKey(subId) >= 0) {
                mMobileSignalControllers.put(subId, cachedControllers.get(subId));
                cachedControllers.remove(subId);
            } else {
                MobileSignalController controller = new MobileSignalController(mContext, mConfig,
                        mHasMobileDataFeature, mPhone, mCallbackHandler,
                        this, subscriptions.get(i), mSubDefaults, mReceiverHandler.getLooper());
                controller.setUserSetupComplete(mUserSetup);
                mMobileSignalControllers.put(subId, controller);
                // 设置第0个插槽的SIM卡的控制器为默认的控制器
                if (subscriptions.get(i).getSimSlotIndex() == 0) {
                    mDefaultSignalController = controller;
                }
                if (mListening) {
                    // 调用MobileSignalController的registerListener注册监听
                    controller.registerListener();
                }
            }
            ....
            // 强制更新SignalClusters和NetworkSignalChangedCallbacks上的所有回调。
            notifyAllListeners();
            ....
        }
    }
}

看一下MobileSignalControllerregisterListener方法,该方法内设置了一些telephony状态,数据变化相关的监听。有变化就会调用updateTelephony进行状态更新。

/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java

public class MobileSignalController extends SignalController<
        MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
    ....
    private final TelephonyManager mPhone;
    public void registerListener() {
        // 通过TelephonyManager注册监听,当telephony状态发生变化时
        // 会调用updateTelephony()方法进行状态更新
        mPhone.listen(mPhoneStateListener,
                PhoneStateListener.LISTEN_SERVICE_STATE
                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
                        | PhoneStateListener.LISTEN_CALL_STATE
                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
                        | PhoneStateListener.LISTEN_DATA_ACTIVITY
                        | PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE
                        | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE);
        ....
    }
    //基于mServiceState、mSignalStrength、mDataNetType、mDataState和mSimState更新当前状态。
    // 当其中一个状态更新时,就会调用。这将在必要时调用监听。
    private final void updateTelephony() {
        ....
        // 最后调用到notifyListeners通知所有的SignalCallback
        notifyListenersIfNecessary();
    }
    @Override
    public void notifyListeners(SignalCallback callback) {
        MobileIconGroup icons = getIcons();
        // 根据当前网络状态配置各项参数
        ....
        // 这里的callback就是NetworkControllerImpl中的mCallbackHandler
        // 调用其setMobileDataIndicators方法进行信号状态更新
        callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, networkIcon, volteIcon,
                qsTypeIcon,activityIn, activityOut, dataContentDescription, description,
                 icons.mIsWide, mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming,
                 false);
    }
}

至此就完成了WIFI以太网移动网络三个网络信号控制器的初始化,之后每个控制器会根据对应的变化显示和更新图标。

移动网络Icon添加和更新流程

以网络状态变化为例,此时在NetworkControllerImpl中收到CONNECTIVITY_ACTION广播,然后执行如下操作:
/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java

public class NetworkControllerImpl extends BroadcastReceiver
        implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider,
        ConfigurationChangedReceiver, Dumpable {
        ....
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        switch (action) {
            ....
            case ConnectivityManager.CONNECTIVITY_ACTION:
            case ConnectivityManager.INET_CONDITION_ACTION:
                updateConnectivity();
                break;
            ....
        }
    }
    private void updateConnectivity() {
        ....
        pushConnectivityToSignals();
    }
    // 将当前连接状态推送到所有SignalController。
    private void pushConnectivityToSignals() {
        // 更新移动网络Icon图标
        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
            mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
        }
        // 更新WiFi网络Icon图标
        if (mWifiSignalController != null)
            mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
        // 更新以太网Icon图标
        mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
    }

这里主要看移动网络信号Icon的更新,进入MobileSignalController.updateConnectivity方法
/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java

public class MobileSignalController extends SignalController<
        MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
    ....
    @Override
    public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
        ....
        notifyListenersIfNecessary();
    }    
    public void notifyListenersIfNecessary() {
        if (isDirty()) {
            saveLastState();
            // 这部分就回到上面分析的通知所有的SignalCallback
            notifyListeners();
        }
    }
     @Override
    public void notifyListeners(SignalCallback callback) {
        MobileIconGroup icons = getIcons();
        // 根据当前网络状态配置各项参数
        ....
        // 这里的callback就是NetworkControllerImpl中的mCallbackHandler
        // 调用其setMobileDataIndicators方法进行信号状态更新
        callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, networkIcon, volteIcon,
                qsTypeIcon,activityIn, activityOut, dataContentDescription, description,
                 icons.mIsWide, mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming,
                 false);
    }
}

/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java

public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
    ....
    @Override
    public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon,
            final int statusType, final int networkIcon, final int volteType,
            final int qsType,final boolean activityIn,
            final boolean activityOut, final String typeContentDescription,
            final String description, final boolean isWide, final int subId, boolean roaming,
            boolean isDefaultData) {
        post(new Runnable() {
            @Override
            public void run() {
                for (SignalCallback signalCluster : mSignalCallbacks) {                    signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType,
                            networkIcon, volteType, qsType, activityIn, activityOut,
                            typeContentDescription, description, isWide, subId, roaming,
                            isDefaultData);
                }
            }
        });
    }
    @Override
    @SuppressWarnings("unchecked")
    public void handleMessage(Message msg) {
        switch (msg.what) {
            ....
            case MSG_ADD_REMOVE_SIGNAL:
                if (msg.arg1 != 0) {
                    mSignalCallbacks.add((SignalCallback) msg.obj);
                } else {
                    mSignalCallbacks.remove((SignalCallback) msg.obj);
                }
                break;
            ....
        }
    }
    ....
    public void setListening(SignalCallback listener, boolean listening) {
        obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget();
    }
}

这里就会调用mSignalCallbacks列表中所有SignalCallback对象的setMobileDataIndicators方法,mSignalCallbacks内部的SignalCallback对象通过setListening方法添加,这里的SignalCallback对象是在StatusBarSignalPolicy中进行添加的,前面提到StatusBar.start方法内初始化了其内部一个StatusBarSignalPolicy对象,这里看下其内部的构造方法:
/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java

public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
        SecurityController.SecurityControllerCallback, Tunable {
    private final StatusBarIconController mIconController;
    private final NetworkController mNetworkController;
    
    public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) {
        mContext = context;

        ....

        mIconController = iconController;
        mNetworkController = Dependency.get(NetworkController.class);
        mSecurityController = Dependency.get(SecurityController.class);
        
        // 调用NetworkControllerImpl.addCallback添加到
        // CallbackHandler的信号回调列表mSignalCallbacks中
        mNetworkController.addCallback(this);
        mSecurityController.addCallback(this);
    }
    ....
   @Override
    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
            int networkType, int volteIcon, int qsType, boolean activityIn, boolean activityOut,
            String typeContentDescription, String description, boolean isWide, int subId,
            boolean roaming, boolean isDefaultData) {
        MobileIconState state = getState(subId);
        ....
        // 更新当前网络信号状态

        state.visible = statusIcon.visible && !mBlockMobile;
        state.strengthId = statusIcon.icon;
        state.typeId = statusType;
        state.contentDescription = statusIcon.contentDescription;
        state.typeContentDescription = typeContentDescription;
        state.roaming = roaming;
        state.activityIn = activityIn && mActivityEnabled;
        state.activityOut = activityOut && mActivityEnabled;
        state.networkIcon = networkType;
        state.volteIcon = volteIcon;

        ....
        mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
        ....
    }

}

最后调用到mIconController来更新移动网络图标,mIconController即是StatusBarStatusBarIconControllerImpl对象mIconControllerStatusBarIconControllerImpl.setMobileIcons方法源码如下:
/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java

public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
        ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
    ....
    private final ArrayList<IconManager> mIconGroups = new ArrayList<>()
    @Override
    public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
        Slot mobileSlot = getSlot(slot);
        int slotIndex = getSlotIndex(slot);

        for (MobileIconState state : iconStates) {
            StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
            if (holder == null) {
                holder = StatusBarIconHolder.fromMobileIconState(state);
                setIcon(slotIndex, holder);
            } else {
                holder.setMobileState(state);
                handleSet(slotIndex, holder);
            }
        }
    }
    
    @Override
    public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
        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) {
        String slot = getSlotName(index);
        int viewIndex = getViewIndex(index, holder.getTag());
        boolean blocked = mIconBlacklist.contains(slot);

        mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
        mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
    }
    
    private void handleSet(int index, StatusBarIconHolder holder) {
        int viewIndex = getViewIndex(index, holder.getTag());
        mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
        mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
    }
    
    @Override
    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);
            List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder();
            boolean blocked = mIconBlacklist.contains(slot.getName());

            for (StatusBarIconHolder holder : holders) {
                int tag = holder.getTag();
                int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());
                group.onIconAdded(viewIndex, slot.getName(), blocked, holder);
            }
        }
    }
}

这里根据是否是新增的Icon,分别进入到addSystemIconhandleSet两个方法,最后遍历mIconGroups分别执行onIconAddedonSetIconHolder回调。历mIconGroups是一个IconManager列表,通过addIconGroup向其内部添加IconManager

IconManager用于将信息从StatusBarIconController转换为ViewGroup中的ImageViews

查看源码有四个地方调用添加,主要看包含折叠状态的状态栏CollapsedStatusBarFragment类,这个就是我们状态栏,以及扩展状态下的状态栏,其中添加的是IconManager的子类DarkIconManager:
/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java

public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks {
    ....
    private DarkIconManager mDarkIconManager;
    ....
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.status_bar, container, false);
    }
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        ....
        // 这里可以看出status_bar布局中的statusIcons就是我们展示各种Icon的区域
        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
        ....
        
    }
    
}

看下内部的onIconAddedonSetIconHolder方法:
/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java

public interface StatusBarIconController {
    ....
    public static class DarkIconManager extends IconManager {
        ....
        public DarkIconManager(LinearLayout linearLayout) {
            // 将布局传入IconManager
            super(linearLayout);
            mIconHPadding = mContext.getResources().getDimensionPixelSize(
                    R.dimen.status_bar_icon_padding);
            mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
        }
        ....
        @Override
        protected void onIconAdded(int index, String slot, boolean blocked,
                                   StatusBarIconHolder holder) {
            // 调用到父类的addHolder方法
            StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
            ....
        }
    }
    
    public static class IconManager implements DemoMode {
        ....
        protected final ViewGroup mGroup;
        protected final Context mContext;
        public IconManager(ViewGroup group) {
            mGroup = group;
            mContext = group.getContext();
            mIconSize = mContext.getResources().getDimensionPixelSize(
                    R.dimen.status_bar_height);
            ....
        }
        ....
        protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
                                                  StatusBarIconHolder holder) {
            switch (holder.getType()) {
                case TYPE_ICON:
                    return addIcon(index, slot, blocked, holder.getIcon());
                case TYPE_WIFI:
                    return addSignalIcon(index, slot, holder.getWifiState());
                case TYPE_MOBILE:
                    return addMobileIcon(index, slot, holder.getMobileState());
            }

            return null;
        }
        @VisibleForTesting
        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
            // 创建一个StatusBarMobileView
            StatusBarMobileView view = onCreateStatusBarMobileView(slot);
            view.applyMobileState(state);
            // 将view 添加进ViewGroup
            mGroup.addView(view, index, onCreateLayoutParams());
            ....
            return view;
        }
        
        private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
            StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
            return view;
        }
    
        ....
        public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
            switch (holder.getType()) {
                case TYPE_ICON:
                    onSetIcon(viewIndex, holder.getIcon());
                    return;
                case TYPE_WIFI:
                    onSetSignalIcon(viewIndex, holder.getWifiState());
                    return;
                case TYPE_MOBILE:
                    onSetMobileIcon(viewIndex, holder.getMobileState());
                default:
                    break;
            }
        }
        public void onSetMobileIcon(int viewIndex, MobileIconState state) {
            StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex);
            if (view != null) {
                view.applyMobileState(state);
            }
            ....
        }
        ....
    }
    
}

这里根据不同的StatusBarIconHolder类型,设置不同的网络Icon,上面列出了移动信号Icon相关的方法。
SystemUI状态栏图标根据源码可大体分为三种:

  1. StatusBarIconView
  2. StatusBarWifiView
  3. StatusBarMobileView

这里主要以移动网络相关图标(StatusBarMobileView)进行分析,添加Icon时首先会创建一个
StatusBarMobileView,然后调用StatusBarMobileViewapplyMobileState更新其显示状态,最后将其加入到CollapsedStatusBarFragment中放置IconViewGroup中,这样就完成了添加过程;

而更新过程则是直接调用StatusBarMobileViewapplyMobileState更新其显示状态,更新部分代码如下:
/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java

public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
        StatusIconDisplayable {
    ....
    public void applyMobileState(MobileIconState state) {
        if (state == null) {
            setVisibility(View.GONE);
            mState = null;
            return;
        }

        if (mState == null) {
            // 添加View时
            mState = state.copy();
            // 初始化移动网络图标状态
            initViewState();
            return;
        }
        if (!mState.equals(state)) {
            // 状态变化了,更新移动网络图标显示状态
            updateState(state.copy());
        }
    }
    
    private void updateState(MobileIconState state) {
        setContentDescription(state.contentDescription);
        if (mState.visible != state.visible) {
            // 可见状态变化
            mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);.
            requestLayout();
        }
        if (mState.strengthId != state.strengthId) {
            // 信号强度变化
            mNewMobile.setImageResource(state.strengthId);
        }

        if (mState.typeId != state.typeId) {
            if (state.typeId != 0) {
                mMobileType.setContentDescription(state.typeContentDescription);
                mMobileType.setImageResource(state.typeId);
                // 网络类型图标G/3G/4G的显示
                mMobileType.setVisibility(View.VISIBLE /*View.VISIBLE*/);
            } else {
                mMobileType.setVisibility(View.GONE);
            }
        }
        
        // 移动漫游Icon
        mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
        // 数据输入Icon
        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
        // 数据输出Icon
        mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
        mInoutContainer.setVisibility((state.activityIn || state.activityOut)
                ? View.VISIBLE : View.GONE);

        mState = state;
    }
            
}

至此就完成了移动网络图标的添加和更新。

流程图

SystemUI StatusBar移动图标显示和更新流程.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容