SystemUI bug案例

SystemUI bug案例

1、没有收到通知

每次版本升级是的时候,容易出现这个问题,要搞清楚这个问题,需要你明白通知发送的流程。![在这里插入图片描述]
在这里插入图片描述
从这个张时序,可以知道有没有发通知到framework这边来,要看看NotificationManagerService的enqueueNotificationInternal的方法有没有日志打印处理,如果有,不会打印包名、id和notification,所以我们可以通过这个方法知道,通知有没有从系统发出来。

2、修改状态栏4G图标

要想修改4G图标,就需要知道它的初始化流程,知道它的图片是在那个地方设置的。流程图如下:
在这里插入图片描述
从数据信号的获取与显示流程中,可以谁知道TelephonyIcons是配置4g图片的地方,所以我们只有把对应图片资源资源id赋值给对应的变量即可。当流程走到MobileSignalController的notifyListeners()方法时调用了getIcons()这个方法,它是mCurrentState.iconGroup获取图片的,所以我们应该再了解一下mCurrentState是怎么初始化的。mCurrentState是在

public SignalController(String tag, Context context, int type, CallbackHandler callbackHandler,
        NetworkControllerImpl networkController) {
    mTag = TAG + "." + tag;
    mNetworkController = networkController;
    mTransportType = type;
    mContext = context;
    mCallbackHandler = callbackHandler;
    mCurrentState = cleanState();
    mLastState = cleanState();
    if (RECORD_HISTORY) {
        mHistory = new State[HISTORY_SIZE];
        for (int i = 0; i < HISTORY_SIZE; i++) {
            mHistory[i] = cleanState();
        }
    }
}

初始化的,而cleanState()是一个抽象方法,由它的子类实现,我们再看看MobileSignalController是怎么实现的

@Override
protected MobileState cleanState() {
    return new MobileState();
}

MobileState 是SignalController.State的子类。那么它的数据是怎么初始化的呢?mCurrentState.iconGroup的值是在两个地方被初始化的,一个是在MobileSignalController的构造函数中:

public MobileSignalController(Context context, Config config, boolean hasMobileData,
        TelephonyManager phone, CallbackHandler callbackHandler,
        NetworkControllerImpl networkController, SubscriptionInfo info,
        SubscriptionDefaults defaults, Looper receiverLooper) {
……
    mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
……
}

一个是在updateTelephony()方法中

private final void updateTelephony() {
……
    MobileIconGroup nr5GIconGroup = getNr5GIconGroup();
    if (nr5GIconGroup != null) {
        mCurrentState.iconGroup = nr5GIconGroup;
    } else if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
        mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
    } else {
        mCurrentState.iconGroup = mDefaultIcons;
    }
    mCurrentState.dataConnected = mCurrentState.connected
            && mDataState == TelephonyManager.DATA_CONNECTED;

    mCurrentState.roaming = isRoaming();
    if (isCarrierNetworkChangeActive()) {
        mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
    } else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) {
        if (mSubscriptionInfo.getSubscriptionId()
                != mDefaults.getDefaultDataSubId()) {
            mCurrentState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
        } else {
            mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
        }
    }
……
}

从方法中我们可以看出一般都是从TelephonyIcons获取的,那么nr5GIconGroup和mDefaultIcons是在哪里初始化的?
nr5GIconGroup是从nr5GIconMap中获取的,在NetworkControllerImpl的add5GIconMapping()方法中被赋值的

static void add5GIconMapping(String keyValuePair, Config config) {
    ……
    if (NR_STATUS_STRING_TO_INDEX.containsKey(key)
            && TelephonyIcons.ICON_NAME_TO_ICON.containsKey(value)) {
        config.nr5GIconMap.put(
                NR_STATUS_STRING_TO_INDEX.get(key),
                TelephonyIcons.ICON_NAME_TO_ICON.get(value));
    }
}

所以还是通过TelephonyIcons赋值的。
在看看mDefaultIcons是在哪里初始化的,是在mapIconSets()中被初始化的。

private void mapIconSets() {
    ……
    if (!mConfig.showAtLeast3G) {
        ……
        mDefaultIcons = TelephonyIcons.G;
    } else {
        ……
        mDefaultIcons = TelephonyIcons.THREE_G;
    }
    ……
}

总结如果想修改4G图片,只需要修改TelephonyIcons类中对4G图片的引用。

3、添加或修改导航栏图标

添加和修改导航栏图片,需要了解导航栏的初始化流程
在这里插入图片描述
然后是NavigationBarInflaterView的初始化
在这里插入图片描述
状态栏的布局初始化,其实是在NavigationBarInflaterView里面进行的,在构造方法中会调用createInflaters()创建两个横竖布局填充器,在onFinishInflate()中,会调用inflateChildren()将横布局navigation_layout和竖向布局navigation_layout_vertical填充进来。调用clearViews()清除之前的子view的缓存。调用getDefaultLayout()获取系统配置字符串,在这个方法中,我们可以添加或者修改导航栏的图标。

protected String getDefaultLayout() {
    final int defaultResource = QuickStepContract.isGesturalMode(mNavBarMode)
            ? R.string.config_navBarLayoutHandle
            : mOverviewProxyService.shouldShowSwipeUpUI()
                    ? R.string.config_navBarLayoutQuickstep
                    : R.string.config_navBarLayout;
    return getContext().getString(defaultResource);
}

字符串配置:

<!-- Nav bar button default ordering/layout -->
<string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string>
<string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]</string>
<string name="config_navBarLayoutHandle" translatable="false">back[40AC];home_handle;ime_switcher[40AC]</string>

修改这些字符串,或者添加字符串配置,然后在代码中解析自己的配置,这样就可以做到“添加或修改导航栏图标” ,但是这仅仅只是修改了一部分代码,还有其它地方需要修改。需要继续看下面的流程。在inflateLayout()方法中将这个字符串切割成三部分,然后调用inflateButtons()生成布局(其实就是按钮)。

private void inflateButtons(String[] buttons, ViewGroup parent, boolean landscape,
        boolean start) {
    for (int i = 0; i < buttons.length; i++) {
        inflateButton(buttons[i], parent, landscape, start);
    }
}

每一个子view都是通过inflateButton()生成的。

protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,
        boolean start) {
    LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
    View v = createView(buttonSpec, parent, inflater);
    if (v == null) return null;

    v = applySize(v, buttonSpec, landscape, start);
    parent.addView(v);
    addToDispatchers(v);
    View lastView = landscape ? mLastLandscape : mLastPortrait;
    View accessibilityView = v;
    if (v instanceof ReverseRelativeLayout) {
        accessibilityView = ((ReverseRelativeLayout) v).getChildAt(0);
    }
    if (lastView != null) {
        accessibilityView.setAccessibilityTraversalAfter(lastView.getId());
    }
    if (landscape) {
        mLastLandscape = accessibilityView;
    } else {
        mLastPortrait = accessibilityView;
    }
    return v;
}

我们可以看到调用了createView()这个方法生成了一个view,我们在看看createView()这个方法。

private View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {
    View v = null;
    String button = extractButton(buttonSpec);
    if (LEFT.equals(button)) {
        button = extractButton(NAVSPACE);
    } else if (RIGHT.equals(button)) {
        button = extractButton(MENU_IME_ROTATE);
    }
    if (HOME.equals(button)) {
        v = inflater.inflate(R.layout.home, parent, false);
    } else if (BACK.equals(button)) {
        v = inflater.inflate(R.layout.back, parent, false);
    } else if (RECENT.equals(button)) {
        v = inflater.inflate(R.layout.recent_apps, parent, false);
    } else if (MENU_IME_ROTATE.equals(button)) {
        v = inflater.inflate(R.layout.menu_ime, parent, false);
    } else if (NAVSPACE.equals(button)) {
        v = inflater.inflate(R.layout.nav_key_space, parent, false);
    } else if (CLIPBOARD.equals(button)) {
        v = inflater.inflate(R.layout.clipboard, parent, false);
    } else if (CONTEXTUAL.equals(button)) {
        v = inflater.inflate(R.layout.contextual, parent, false);
    } else if (HOME_HANDLE.equals(button)) {
        v = inflater.inflate(R.layout.home_handle, parent, false);
    } else if (IME_SWITCHER.equals(button)) {
        v = inflater.inflate(R.layout.ime_switcher, parent, false);
    } else if (button.startsWith(KEY)) {
        String uri = extractImage(button);
        int code = extractKeycode(button);
        v = inflater.inflate(R.layout.custom_key, parent, false);
        ((KeyButtonView) v).setCode(code);
        if (uri != null) {
            if (uri.contains(":")) {
                ((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));
            } else if (uri.contains("/")) {
                int index = uri.indexOf('/');
                String pkg = uri.substring(0, index);
                int id = Integer.parseInt(uri.substring(index + 1));
                ((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));
            }
        }
    }
    return v;
}

在这个方法中,我们可以看到解析buttonSpec这个字符串,然后根据字符串去填充不同的布局,因此我们要在这个地方定义自己的业务字符串,新建填充的布局,才能够做到“添加或修改导航栏图标”。

4、设置的音量没有变化

要想知道设置的音量为什么没有变化,需要了解音量的设置流程:
在这里插入图片描述
用户按了音频按钮后,音量对话框会显示出来或者调解音量。PhoneWindow的onKeyDown()方法会被调用,接着调用MediaSessionManager的dispatchVolumeKeyEventAsSystemService()来分发事件。调用MediaSessionService.SessionManagerImpl的dispatchVolumeKeyEvent()方法会在debug模式下,打印包名、pid、uid、stream、musicOnly这些信息,这样我们可以通过日志知道Android的framework层有没有收到事件。还有一个地方,它会收到底层更新音量的广播。VolumeDialogControllerImpl的onReceive()中收到android.media.VOLUME_CHANGED_ACTION,查看有没有相关的日子打印出来。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值