欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Mr.Smile填坑记——安卓P、安卓10 Google原生设置中音量条调节的3个bug

程序员文章站 2022-05-12 09:17:32
...

先来看看问题

  1. 先调节一次通话音量,之后铃声音量和闹钟音量会发生联动现象
  2. 调节任一音量,有音量条的回弹现象
  3. 打开音量的高级,快速上下滑动页面,音量条会跳到上次调节的位置,然后再回到正常位置(前提是要调节过音量,并且要快速滑动页面)

再来看看方案

     第一个问题最后再说,先从第二个问题说起。一些问题,通过重复的复现问题,可以发现一些规律,从规律当中可以大胆的推测定位问题或者绕过某些bug.

     问题二:

     音量回弹这个,通过反复的测试发现,每次回弹的位置都是上次调节的位置,并且音量大小是弹回位置的大小,这说明,数据库的值没有设置过来,那么我们大胆的推测为什么会是上次的数值,有没有可能是赋值的时候出问题,先去看看逻辑所在位置,验证下猜测。alps/frameworks/base/core/java/android/preference/SeekBarVolumizer.java 代码在此,打开看看吧。

     postSetVolume(int progress) 调试发现问题果然还是在这个progress,设置的是mLastAudibleStreamVolume 这个东西,但是事实是这个东西变化并不是很及时,逻辑是拖动音量滑块后再调用此方法更新数据库。因此,问题的解决法案是onProgressChanged的时候,搞一个成员变量记录当前的位置,然后在手指离开的时候去改变数据库onStopTrackingTouch。

    问题三:

    根据上面问题二的思路,我们先来分析一下问题三的现象,从设置首页第一次进入音量的时候不会出现此问题,进入之后需要先调节一次音量,然后再快速滑动页面,这里需要注意下,把音量最下面那个高级的部分取消折叠才能复现问题,并且多次测试发现,滑块闪动的位置,也是上次调节的位置,到这里我们得到3个信息:

  • 初始化进入页面的时候无此现象
  • 进来没有条调节过的时候无此现象,调节过后的位置,就是闪动的位置
  • 音量条一直在视野范围的时候无此现象

    好,从信息中得出,问题肯定是进度条的progress有问题,并且有问题的地方是在音量条重新加载的时候,那么带着问题我们再去看代码alps/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/widget/SeekBarPreference.java果然,在快速滑动音量又出现的时候重新走了 onBindViewHolder(PreferenceViewHolder view) 的方法

 @Override
    public void onBindViewHolder(PreferenceViewHolder view) {
        super.onBindViewHolder(view);
        view.itemView.setOnKeyListener(this);
        mSeekBar = (SeekBar) view.findViewById(
                com.android.internal.R.id.seekbar);
        mSeekBar.setOnSeekBarChangeListener(this);
        mSeekBar.setMax(mMax);
        mSeekBar.setMin(mMin);
        // 添加
        Log.d("HZH", "onBindViewHolder: " + mCount);
        boolean useSeekProgress = mCount > 0 && this instanceof VolumeSeekBarPreference;
        mSeekBar.setProgress(useSeekProgress ? mSeekBar.getProgress() : mProgress);
        mCount++;
        // 添加结束
        final CharSequence title = getTitle();
        mSeekBar.setEnabled(isEnabled());
        .......
}

mSeekBar.setProgress的时候,mProgress就是上次条结果的progress,所以断定就是这里的问题了,至于为什么要用mCount >0的判断呢,那是因为从新看到音量条的时候,走了两次onBindViewHolder的方法,我们要在第二次的时候去改变才可以,另外用,instanceof VolumeSeekBarPreference是为了避免影响设置中其他位置的seekbar,比如无障碍中的语音转文字的seekbar也用的这个,不做判断的话会导致,这个音量条无法重置。以上问题三解决。

      问题一:

      再看最后一个问题,这个bug确实很奇怪,真的,代码确实是玄学,Google的大佬也是有不少bug的,并且隐藏的很深很深。表面上看确实没什么头绪,只有 “卧槽,这特么什么情况?!” 浮现在我的脑海,但是后面仔细想想,还是有了点方向,我调节铃声的时候并没有触摸到闹钟的seekbar啊,能不能区分出那个是用手触摸过的呢?

      带着唯一的线索去看代码,SeekBarVolumizer.java和问题一用的是同一个东西,果然发现了有可以区分的地方,onProgressChanged的时候可以区分,于是按照下面注释的改法试了一把,果然搞定了。

public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
    private static final String TAG = "SeekBarVolumizer";

    ...................................

    private boolean mFromTouch;
    private int mProgress;

    ......................

    protected void updateSeekBar() {
        final boolean zenMuted = isZenMuted();
        mSeekBar.setEnabled(!zenMuted);
        // Modify 
        if((mLastProgress == -1) || (mLastAudibleStreamVolume == mLastProgress)){
        //Modify end
            if (zenMuted) {
                mSeekBar.setProgress(mLastAudibleStreamVolume, true);
            } else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
                mSeekBar.setProgress(0, true);
            } else if (mMuted) {
                mSeekBar.setProgress(0, true);
            } else {
                mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume, true);
            }
        }
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_SET_STREAM_VOLUME:
                // 删除
                //if (mMuted && mLastProgress > 0) {
                //    mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_UNMUTE, 0);
                //} else if (!mMuted && mLastProgress == 0) {
                //    mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_MUTE, 0);
                //}
                // 删除结束
                mAudioManager.setStreamVolume(mStreamType, mLastProgress,
                        AudioManager.FLAG_SHOW_UI_WARNINGS);
                break;
           .......
    }

   

    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
        // 添加
        mProgress = progress;
        mFromTouch = fromTouch;
        //添加
    }

    //添加
    private void startChangeProgress(SeekBar seekBar){
        Log.d(TAG,"mProgress: "+mProgress);
        if (mFromTouch) {
            postSetVolume(mProgress);
        }
        if (mCallback != null) {
            mCallback.onProgressChanged(seekBar, mProgress, mFromTouch);
        }
    }
    //添加

   ............

    public void onStopTrackingTouch(SeekBar seekBar) {
        // 添加
        startChangeProgress(seekBar);
        // 添加结束
        postStartSample();
    }

    ...........
}

到此,3个问题都得以解决,我发现有篇文章这个代码居然要 5个C币在我这里完全免费,但是,如果有幸帮到你,希望能留个赞,如果有问题或更好的解决办法,请不吝赐教。