首页 > 其他分享 >Android系统开发之TextView跑马灯效果导致系统卡的惨案

Android系统开发之TextView跑马灯效果导致系统卡的惨案

时间:2024-02-01 21:01:22浏览次数:28  
标签:动态显示 界面 效果 导致系统 跑马灯 刷新 Android TextView

问题描述:

客户反馈投诉说:
低端设备上,在桌面时,当音乐名过长时,音乐名称就会有一个跑马灯动态效果,此时调节设备的音量,设备极其的卡,音量调节界面会晚将近10秒才显示。
但是如果音乐名不长可以正常显示时,音乐名称就不会有跑马灯动态效果,此时调节设备的音量,设备正常,音量调节界面也不会慢显示。

歌名跑马灯动态显示效果:

Android系统开发之TextView跑马灯效果导致系统卡的惨案_动态显示


初步处理

此问题先是反馈到应用组桌面负责的同志,这个因为原因比较简单,就是因为跑马灯动态显示效果显示导致,那直接将此效果关闭就解决了此问题了。

跑马灯动态显示效果的代码也是非常简单,就是设置TextView的几个属性:

<TextView
......
android:marqueeRepeatLimit="marquee_forever" 
android:ellipsize="marquee" 
android:focusable="true" 
android:focusableInTouchMode="true" 
android:scrollHorizontally="true"

大家以为就这样解决了,不是的,这仅仅只是一个开始,为什么了?

最核心的原因是,如果这样修改,需要给各个客户发不同的升级包来处理此问题,而事实上此桌面应用的客户有上百个客户,发每个客户发升级包不现实。所以领导决定此问题让系统组来统一处理此问题。


初步分析

1.分析桌面应用的traceview日志:
a.跑马灯动态显示时桌面应用的traceview日志,将方法调用次数按倒数来排序:

Android系统开发之TextView跑马灯效果导致系统卡的惨案_android_02


直接关闭跑马灯动态显示效果

b.无跑马灯动态显示时桌面应用的traceview日志,将方法调用次数按倒数来排序:

Android系统开发之TextView跑马灯效果导致系统卡的惨案_android_03

可以看出,跑马灯动态显示时桌面应用在特别频繁的刷新界面,其次数特别多。

2.查看界面刷新情况
将设置中开发者选项--显示面(surface)更新打开
可以看到在跑马灯动态显示歌词时,桌面应用在不断的闪红色,表示桌面应用在不断的刷新。当无跑马灯动态显示歌词时,桌面应用在几秒钟才闪一次红色,表示桌面应用在几秒才的刷新一次。

从上面二点,都可以看出这就是TextView的跑马灯动态显示歌词效果,引起的界面频繁刷新,从而导致系统卡。


追踪源码

明显这就是TextView跑马灯效果的一个系统设计导致的低端设备上的一个性能问题,那我们就去看一下这个实现情况吧。
需要重点关注二个属性:

android:marqueeRepeatLimit="marquee_forever" 
android:ellipsize="marquee" 

其对应在源码:
frameworks\base\core\java\android\widget\TextView.java

方法一:TextView--TextView方法中:
ellipsize和marqueeRepeatLimit属性赋值:

case com.android.internal.R.styleable.TextView_ellipsize:
    ellipsize = a.getInt(attr, ellipsize);
    break;

case com.android.internal.R.styleable.TextView_marqueeRepeatLimit:
    setMarqueeRepeatLimit(a.getInt(attr, mMarqueeRepeatLimit));
    break;
......
switch (ellipsize) {
    case 1:
        setEllipsize(TextUtils.TruncateAt.START);
        break;
    case 2:
        setEllipsize(TextUtils.TruncateAt.MIDDLE);
        break;
    case 3:
        setEllipsize(TextUtils.TruncateAt.END);
        break;
    case 4:
        //代码1.1  这个就是设置跑马灯循环效果处
        if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
            setHorizontalFadingEdgeEnabled(true);
            mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
        } else {
            setHorizontalFadingEdgeEnabled(false);
            mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
        }
        setEllipsize(TextUtils.TruncateAt.MARQUEE);
        break;
}

和跑马灯相关的变量:

private Marquee mMarquee;
private boolean mRestartMarquee;
private int mMarqueeRepeatLimit = 3;

解决方案

直接屏蔽跑马灯动态效果

这种思路最简单,直接将桌面应用的对应id的跑马灯效果的TextView的设置跑马灯效果在方法一中“//代码1.1”处屏蔽。
此方案解决问题简单粗暴,但是这样就彻底将TextView的跑马灯效果全部关闭了,这导致高配置设备也没有此效果,不是一个好的选择。

降低跑马灯移动的速度

跑马灯移动的速度相关逻辑:
方法二:TextView--Marquee--Marquee方法:

Marquee(TextView v) {
    final float density = v.getContext().getResources().getDisplayMetrics().density;
    //代码2.1 跑马灯移动的速度
    mPixelsPerSecond = MARQUEE_DP_PER_SECOND * density;
    mView = new WeakReference<TextView>(v);
    mChoreographer = Choreographer.getInstance();
}

方法三:TextView--tick方法----TextView界面跑马灯界面刷新

void tick() {
    if (mStatus != MARQUEE_RUNNING) {
        return;
    }

    mChoreographer.removeFrameCallback(mTickCallback);

    final TextView textView = mView.get();
    if (textView != null && (textView.isFocused() || textView.isSelected())) {
        long currentMs = mChoreographer.getFrameTime();
        long deltaMs = currentMs - mLastAnimationMs;
        mLastAnimationMs = currentMs;
        //代码3.1 跑马灯移动的速度
        float deltaPx = deltaMs / 1000f * mPixelsPerSecond;
        mScroll += deltaPx;
        if (mScroll > mMaxScroll) {
            mScroll = mMaxScroll;
            mChoreographer.postFrameCallbackDelayed(mRestartCallback, MARQUEE_DELAY);
        } else {
            //代码3.2 回调mTickCallback,以循环的实现跑马灯效果
            mChoreographer.postFrameCallback(mTickCallback);
        }
        //代码3.3 textview刷新
        textView.invalidate();
    }
}

我们调整方法二中 "//代码2.1"和 方法三中"//代码3.1"的跑马灯移动的速度,可以降低跑马灯移动的速度,但是问题仍然存在,也就是说降低跑马灯移动的速度不能降低降低跑马灯效果导致的界面刷新的速度,此方案无效。

降低跑马灯效果界面刷新的速度

那么这个跑马灯效果界面刷新的速度到底是由什么决定的呢?
查看回调方法:

//变量mTickCallback 
private Choreographer.FrameCallback mTickCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        //调用界面刷新
        tick();
    }
};

//变量mStartCallback 
private Choreographer.FrameCallback mStartCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        mStatus = MARQUEE_RUNNING;
        mLastAnimationMs = mChoreographer.getFrameTime();
        //调用界面刷新
        tick();
    }
};

TextView的跑马灯效果就是通过mStartCallback 回调来循环的调用TextView--tick方法来实现的,且其跑马灯效果界面刷新的速度为每一帧(doFrame)都调用。

其跑马灯循环效果流程为:

Android系统开发之TextView跑马灯效果导致系统卡的惨案_android_04

那此这就简单了,我的处理方案就是设置一个变量,当变量满足一定条件时(我验证值为10),才调用方法三:TextView--tick方法中的//代码3.3 textview刷新。这样就将跑马灯效果界面刷新的速度降低为原来的十分之一。

测试验证结果

当歌词跑马灯动态显示效果时,设备不卡了,且其traceview的日志调用也是正常的,打开显示面(surface)更新后,界面刷新也不会特别快,问题解决。

Android系统开发之TextView跑马灯效果导致系统卡的惨案_动态显示_05

标签:动态显示,界面,效果,导致系统,跑马灯,刷新,Android,TextView
From: https://blog.51cto.com/u_16502883/9536355

相关文章

  • Android系统中/system/priv-app/和/system/app/以及/system_ext/app/的区别
    在Android系统中,这三个目录都是与应用程序相关的,但它们在系统中的位置和用途上有一些区别。/system/priv-app/目录:该目录包含被认为是系统的一部分的特权应用程序(privilegedapps)。特权应用程序通常是由设备制造商或ROM开发者预先安装的,它们在系统中运行时具有更高的权限。......
  • Android 开机流程介绍
    目录一、目的二、环境三、相关概念3.1Android平台架构3.2Android启动架构3.3zImage3.4RAMDISK3.5RC文件四、详细设计4.1BootRom4.2BootLoader4.3Kernel4.3.1zImage解压缩阶段4.3.2kernel的汇编启动阶段4.3.3Kernel的C启动阶段4.3.3.1kernel启动log4.3.3.2init进程&k......
  • App requires Multidex support Multidex support is required for your android app
    flutterandroid报错64k!]ApprequiresMultidexsupportMultidexsupportisrequiredforyourandroidapptobuildsincethenumberofmethodshasexceeded64k.Seehttps://docs.flutter.dev/deployment/android#enabling-multidex-supportformoreinformation......
  • Android安卓开发:RecyclerView的快速使用
    Android安卓开发:RecyclerView的快速使用前言:1.我借鉴了网络上的代码2.我并没有深入了解,难免出错3.默认已经导入了依赖包,跳过效果(并非此次给出代码的效果):代码:1.准备layout文件(命名格式为adapter_*.xml)2.准备*Adapter.java文件3.默认存在Activity.java或Fragment.java......
  • 资深Android逆袭、华为鸿蒙为安卓程序员开辟了一条新道路
    本文章主要从以下5个方面来展开聊聊这个话题:1.什么是鸿蒙2.鸿蒙系统发展时间线3.鸿蒙是套壳Android吗?4.鸿蒙的生态(用户以及开发者)5.一些建议1月18日,在鸿蒙生态千帆启航仪式上,华为宣布了继鸿蒙4.0之后的鸿蒙操作系统,星河版的预览版本,引起了广泛的讨论,这是一款完全剥离安卓......
  • android setprop getprop, 调整app heap 堆 大小
    网上的截图: 通过setprop设置的更改的属性,重启之后就会消失。 调整app堆大小。      一些基本的了解。  ......
  • Android的ListView分页功能(上滑加载更多)
    Android的ListView分页功能(上滑加载更多)首先要定义一个footer.xml作为进度条和提示加载中的底部布局,代码如下:<LinearLayoutandroid:id="@+id/load_layout"android:layout_width="match_parent"android:layout_height="wrap_content"......
  • android 框架搭建
    1.下载androidstdio工具:如下:![image.png](/img/bVc2Mvo)2.下载对应的SDKtools.最好下载SDKzip.访问地址:https://www.androiddevtools.cn/![image.png](/img/bVc2Mvp)3.选择SDKpath.将解压后的目录进行选择。删除原下载文件。![image.png](/img/bVc2Mvl), 4.下载工程导入,这......
  • Android开发笔记[8]-基于Compose布局的开屏页
    摘要基于Compose布局的开屏页,显示进度条;自动跳转到其他页面.关键信息AndroidStudio:ElectricEel|2022.1.1Patch2Gradle:distributionUrl=https://services.gradle.org/distributions/gradle-7.5-bin.zipjvmTarget='1.8'minSdk21targetSdk33compileSdk33开......
  • Android原生定位
    使用LocationManager和LocationListener结合进行简单定位功能1.创建LocationManagerLocationManagerlocationManager=(LocationManager)this.getSystemService(Context.LOCATION_SERVICE);2.创建LocationListenerprivatefinalLocationListenerlocationListener=newLocati......