Android 10.0 狀態(tài)欄系統(tǒng)圖標(biāo)顯示分析

SystemUI中StatusBar的圖標(biāo)控制器實(shí)現(xiàn)類為StatusBarIconControllerImpl,其繼承了StatusBarIconController的接口,用于跟蹤所有圖標(biāo)的狀態(tài),并將對應(yīng)的狀態(tài)發(fā)送給注冊的圖標(biāo)管理器(IconManagers)。當(dāng)我們在StatusBar中獲取到它的實(shí)例后,還會將它傳給PhoneStatusBarPolicy和StatusBarSignalPolicy對象。PhoneStatusBarPolicy控制啟動(dòng)時(shí)裝載哪些圖標(biāo)(藍(lán)牙,定位等),而StatusBarSignalPolicy控制網(wǎng)絡(luò)信號圖標(biāo)(移動(dòng)網(wǎng)絡(luò),WiFi,以太網(wǎng))的變化。
一起來看 StatuBar 的 start() 方法:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java

@Override
public void start() {
    
    // 省略部分代碼......
    // 創(chuàng)建整個(gè)SystemUI視圖并添加到WindowManager中
    createAndAddWindows();//這個(gè)重點(diǎn)方法,創(chuàng)建相關(guān)的視圖
    // 省略部分代碼......
    // Lastly, call to the icon policy to install/update all the icons.
    mIconPolicy.init();
    mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
    // 省略部分代碼......
}

這里的 mIconPolicy 就是 PhoneStatusBarPolicy對象,mSignalPolicy 就是 StatusBarSignalPolicy 對象。我們這里以 StatusBarSignalPolicy 為例去研究。
StatusBarSignalPolicy實(shí)現(xiàn)了NetworkControllerImpl.SignalCallback接口,SignalCallback接口定義在NetworkControllerImpl實(shí)現(xiàn)的接口NetworkController中。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java

    @Inject
    public NetworkControllerImpl(Context context, @Background Looper bgLooper,
            DeviceProvisionedController deviceProvisionedController,
            BroadcastDispatcher broadcastDispatcher, ConnectivityManager connectivityManager,
            TelephonyManager telephonyManager, WifiManager wifiManager,
            NetworkScoreManager networkScoreManager) {
        this(context, connectivityManager,
                telephonyManager,
                wifiManager,
                networkScoreManager,
                SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
                new CallbackHandler(),
                new AccessPointControllerImpl(context),
                new DataUsageController(context),
                new SubscriptionDefaults(),
                deviceProvisionedController,
                broadcastDispatcher);
        mReceiverHandler.post(mRegisterListeners);
    }
    private final Runnable mRegisterListeners = new Runnable() {
        @Override
        public void run() {
            registerListeners();
        }
    };
    void registerListeners() {
        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
            mobileSignalController.registerListener();
        }
        if (mSubscriptionListener == null) {
            mSubscriptionListener = new SubListener();
        }
        mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
        mPhone.listen(mPhoneStateListener, LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
        // broadcasts
        IntentFilter filter = new IntentFilter();
        // wifi相關(guān)
        // wifi信號強(qiáng)度廣播
        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
        // wifi狀態(tài)變化廣播
        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        // wifi連接狀態(tài)改變
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        
        // 移動(dòng)網(wǎng)絡(luò)相關(guān)
        // SIM卡狀態(tài)改變
        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
        // 數(shù)據(jù)語音訂閱修改
        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);
        // 連接狀態(tài)相關(guān)
        // 網(wǎng)絡(luò)連接狀態(tài)發(fā)生變化
        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        // 網(wǎng)絡(luò)連接可能不好
        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
        // 切換飛行模式時(shí)
        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
        mContext.registerReceiver(this, filter, null, mReceiverHandler);
        mListening = true;
        // 省略部分代碼......
        // 4.更新移動(dòng)網(wǎng)絡(luò)控制器
        updateMobileControllers();
    }

在NetworkControllerImpl 的構(gòu)造方法里,最終會調(diào)用到:registerListeners() 方法進(jìn)行廣播的注冊。
廣播處理:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java

@Override
public void onReceive(Context context, Intent intent) {
    if (CHATTY) {
        Log.d(TAG, "onReceive: intent=" + intent);
    }
    final String action = intent.getAction();
    switch (action) {
        case ConnectivityManager.CONNECTIVITY_ACTION:
        case ConnectivityManager.INET_CONDITION_ACTION:
            // 省略部分代碼......
            break;
        case Intent.ACTION_AIRPLANE_MODE_CHANGED:
                // 省略部分代碼......
            break;
        case TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
            // We are using different subs now, we might be able to make calls.
                // 省略部分代碼......
            break;
        case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
            // Notify every MobileSignalController so they can know whether they are the
            // data sim or not.
            // 省略部分代碼......
            break;
        case Intent.ACTION_SIM_STATE_CHANGED:
            // Avoid rebroadcast because SysUI is direct boot aware.
            // 省略部分代碼......
            break;
        case Intent.ACTION_SERVICE_STATE:
            // 省略部分代碼......
            break;
        case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
            // 省略部分代碼......
            break;
        case ImsManager.ACTION_IMS_SERVICE_UP:
        case ImsManager.ACTION_IMS_SERVICE_DOWN:
            // 省略部分代碼......
            break;
        case ACTION_HIGH_DEF_AUDIO_SUPPORT:
            // 省略部分代碼......
            break;
        case ACTION_MODEM_CHANGE:
            // 省略部分代碼......
            break;
        default:
            int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
            if (SubscriptionManager.isValidSubscriptionId(subId)) {
                if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
                    mMobileSignalControllers.get(subId).handleBroadcast(intent);
                } else {
                    // Can't find this subscription...  We must be out of date.
                    updateMobileControllers();
                }
            } else {
                    // wifi狀態(tài)圖標(biāo)處理
                // No sub id, must be for the wifi.
                mWifiSignalController.handleBroadcast(intent);
            }
            break;
    }
}

這里以 wifi狀態(tài)圖標(biāo)處理 為例;接下來看WifiSignalController#handleBroadcast():
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java

public void handleBroadcast(Intent intent) {
    mWifiTracker.handleBroadcast(intent);
    mCurrentState.enabled = mWifiTracker.enabled;
    mCurrentState.connected = mWifiTracker.connected;
    mCurrentState.ssid = mWifiTracker.ssid;
    mCurrentState.rssi = mWifiTracker.rssi;
    mCurrentState.level = mWifiTracker.level;
    mCurrentState.statusLabel = mWifiTracker.statusLabel;
    notifyListenersIfNecessary();
}

在WifiSignalController#handleBroadcast()方法中,就兩個(gè)實(shí)現(xiàn),一個(gè)是獲取 WiFi 的狀態(tài),一個(gè)是通知更新狀態(tài)。
我們直接看通知SignalController# notifyListenersIfNecessary() :
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java

public void notifyListenersIfNecessary() {
    if (isDirty()) {
        saveLastState();    // 保持此時(shí)的狀態(tài)
        notifyListeners();    // 通知監(jiān)聽器
    }
}
public final void notifyListeners() {
    notifyListeners(mCallbackHandler);
}
public abstract void notifyListeners(SignalCallback callback);

notifyListener()方法的實(shí)現(xiàn)在WifiSignalController類中:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java

@Override
public void notifyListeners(SignalCallback callback) {
    // only show wifi in the cluster if connected or if wifi-only
    boolean visibleWhenEnabled = mContext.getResources().getBoolean(
            R.bool.config_showWifiIndicatorWhenEnabled);
    boolean wifiVisible = mCurrentState.enabled && (
            (mCurrentState.connected && mCurrentState.inetCondition == 1)
                    || !mHasMobileDataFeature || mWifiTracker.isDefaultNetwork
                    || visibleWhenEnabled);
    String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null;
    boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
    String contentDescription = getTextIfExists(getContentDescription()).toString();
    if (mCurrentState.inetCondition == 0) {
        contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
    }
    IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
    IconState qsIcon = new IconState(mCurrentState.connected,
            mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
                    : getQsCurrentIconId(), contentDescription);
    // callback為 CallbackHandler對象
    callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
            ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
            wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
}

可以看到,這里回調(diào)了StatusBarSignalPolicy#setWifiIndicators() 方法:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java

@Override
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
        boolean activityIn, boolean activityOut, String description, boolean isTransient,
        String statusLabel) {
    boolean visible = statusIcon.visible && !mBlockWifi;
    boolean in = activityIn && mActivityEnabled && visible;
    boolean out = activityOut && mActivityEnabled && visible;
    WifiIconState newState = mWifiIconState.copy();
    newState.visible = visible;
    newState.resId = statusIcon.icon;
    newState.activityIn = in;
    newState.activityOut = out;
    newState.slot = mSlotWifi;
    newState.airplaneSpacerVisible = mIsAirplaneMode;
    newState.contentDescription = statusIcon.contentDescription;
    MobileIconState first = getFirstMobileState();
    newState.signalSpacerVisible = first != null && first.typeId != 0;
    updateWifiIconWithState(newState);
    mWifiIconState = newState;
}
private void updateWifiIconWithState(WifiIconState state) {
    if (state.visible && state.resId > 0) {
        mIconController.setSignalIcon(mSlotWifi, state);
        mIconController.setIconVisibility(mSlotWifi, true);
    } else {
        mIconController.setIconVisibility(mSlotWifi, false);
    }
}

通過StatusBarIconController接口設(shè)置圖標(biāo)的套路都是一樣的:

  • 獲取圖標(biāo)名字
  • 監(jiān)聽事件
  • 通過StatusBarIconControllerImpl相應(yīng)的方法設(shè)置圖標(biāo)。
    接下來再看StatusBarIconControllerImpl#setSignalIcon():
    frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
/**
 * Signal icons need to be handled differently, because they can be
 * composite views
 */
@Override
public void setSignalIcon(String slot, WifiIconState state) {
    int index = getSlotIndex(slot);
    if (state == null) {
        removeIcon(index, 0);
        return;
    }
    StatusBarIconHolder holder = getIcon(index, 0);
    if (holder == null) {
        holder = StatusBarIconHolder.fromWifiIconState(state);
        setIcon(index, holder);
    } else {
        holder.setWifiState(state);
        handleSet(index, holder);
    }
}

首先設(shè)置WiFi的狀態(tài)信息,遍歷mIconGroups分別執(zhí)行StatusBarIconController接口中靜態(tài)類IconManager中的onIconAdded()和onSetIconHolder()的回調(diào)。
IconManager用于將信息從StatusBarIconController轉(zhuǎn)換為ViewGroup中的ImageViews(com.android.systemui.statusbar.AlphaOptimizedImageView)。
接著看IconManager中的onIconAdded()和onSetIconHolder()方法:這兩個(gè)方法一個(gè)用于添加、一個(gè)用于更新。
frameworks/base/packages/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) {
            // 調(diào)用到父類的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 StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
            // 創(chuàng)建一個(gè)StatusBarWifiView 
            StatusBarWifiView view = onCreateStatusBarWifiView(slot);
            view.applyWifiState(state);
            // 將view 添加進(jìn)ViewGroup
            mGroup.addView(view, index, onCreateLayoutParams());
            if (mIsInDemoMode) {
                mDemoStatusIcons.addDemoWifiView(state);
            }
            return view;
        }
        private StatusBarWifiView onCreateStatusBarWifiView(String slot) {
            StatusBarWifiView view = StatusBarWifiView.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 onSetSignalIcon(int viewIndex, WifiIconState state) {
            StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex);
            if (wifiView != null) {
                wifiView.applyWifiState(state);
            }
            if (mIsInDemoMode) {
                mDemoStatusIcons.updateWifiState(state);
            }
        }
        ....
    }
    
}

這里根據(jù)不同的StatusBarIconHolder類型,設(shè)置不同的網(wǎng)絡(luò)Icon,上面列出了 Wifi 圖標(biāo)相關(guān)的方法。
SystemUI狀態(tài)欄圖標(biāo)根據(jù)源碼可大體分為三種:

  1. StatusBarIconView
  2. StatusBarWifiView
  3. StatusBarMobileView

這里主要以Wifi 相關(guān)圖標(biāo)(StatusBarWifiView)進(jìn)行分析,添加Icon時(shí)首先會創(chuàng)建一個(gè)
StatusBarWifiView,然后調(diào)用StatusBarWifiView的applyWifiState更新其顯示狀態(tài),最后將其加入到CollapsedStatusBarFragment中放置Icon的ViewGroup中,這樣就完成了添加過程;
再來看看 CollapsedStatusBarFragment:
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的區(qū)域
        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
        mDarkIconManager.setShouldLog(true);
        Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
        ....   
    }
}

補(bǔ)充:
notifyListenersIfNecessary()在其父類SignalController中定義,
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java

    private final CallbackHandler mCallbackHandler;
    public void notifyListenersIfNecessary() {
        if (isDirty()) {
            saveLastState();
            notifyListeners();
        }
    }
    // 在這里注意了,在這里的的參數(shù)是 CallbackHandler 的對象
    public final void notifyListeners() {
        notifyListeners(mCallbackHandler);
    }
// callback 則是 CallbackHandler 的對象。
  public abstract void notifyListeners(SignalCallback callback);

CallbackHandler維護(hù)了所有需要監(jiān)聽的SignalCallback接口對象,我們的StatusBarSignalPolicy就實(shí)現(xiàn)了該接口。
StatusBarSignalPolicy主要執(zhí)行網(wǎng)絡(luò)圖標(biāo)的刷新動(dòng)作,其實(shí)現(xiàn)了NetworkControllerImpl.SignalCallback接口,然后注冊到NetworkController,其具體實(shí)現(xiàn)類NetworkControllerImpl會根據(jù)WIFI,SIM等狀態(tài)廣播來進(jìn)一步派發(fā)給具體的Controller,例如WifiSignalController,每個(gè)Controller只與CallbackHandler交互,然后CallbackHandler繼續(xù)轉(zhuǎn)交給維護(hù)的SignalCallback接口的具體實(shí)現(xiàn)類,例如StatusBarSignalPolicy

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容