fWX228941 / project

all by myself
1 stars 0 forks source link

性能优化设计思路 #63

Open fWX228941 opened 2 months ago

fWX228941 commented 2 months ago

1.老化测试(硬件测试) 1.1.含义 测试轮数,压力测试,系统测试,后台测试,前台测试,并行串行测试,压力测试允许选择测试功能项以及修改测试次数 1.2.平台化 差异点:项目差异,终端硬件差异 实现方式:采用配置文件的方式,每个项目对应一个配置文件,定制项目优先级高于固定机型,比如config_product_gh860_sx.xml的优先级高于config_product_gh860.xml 1.3.流程

Image

读取配置文件流程(图上)

Image

配置文件(图上)

Image

界面显示(图上) 备注:aging属性:测试项是否需要在老化测试模式中显示

老化测试模式的设置界面(如下)

Image

压力测试模式的设置界面(如下)

Image

Image

单体测试流程(如上)

代码的精华点就是抽象方法的定义:onTestStart代表测试开始(一次),onTestRepeat代表重复测试(多次),onTestFinish代表测试结束(一次)

Image

测试流程图(如上)

备注:定制属性可配置化,system代表是否是系统测试项,foreground代表是否是前台测试项,系统测试>后台测试>前台测试

Image

整体活动流程图(如上) 备注: 1)(配置结果保存)不同测试模式下的配置结果保存到不同的文件 2)测试项类型的测试差异,系统测试如重启测试需要单独进行测试;后台测试不用显示UI,子线程中执行,可多个任务并发执行;前台测试需要显示UI,主线中执行,同一时间只能显示一个

Image

重启测试项流程(如上)

Image

休眠唤醒流程(如上)

Image

Memory测试流程(如上)

Image

Emmc测试流程(如上)

Image

音频测试流程(如上)

Image

CPU测试流程(如上)

Image

视频测试流程(如上)

Image

LCD测试流程图(如上)

Image

Camera测试流程图(如上)

fWX228941 commented 2 months ago

1.3.类图

Image

1.BaseActivity:继承于Activity,所有界面的公共父类,负责保持屏幕常亮,消耗按键,长按退出等功能 2.SettingsActivity:设置界面,主要用于显示所有测试项及对应的测试次数 3.TestActivity:测试界面,显示前台测试时的测试界面 4.TestReportActivity:测试报告,用于显示上一次测试结束后的测试情况 5.Config:解析配置文件的内容,保存到Product类中,提供静态方法获取当前的配置信息 6.Product:对应配置文件中的Product节点,保存了该节点的所有属性,及Product节点下的所有Test子节点 7.Test:对应配置文件中的Test节点,保存了该节点的所有属性,同时时所有测试项的父类,实现了单个测试的测试流程

1.5.需求 特殊性 客户群体影响具体操作,老化测试面向的是产线工作人员,在面向产线时,操作要尽量简单,步骤尽量少,最好就只点击开始测试就行了。

1.6测试项 1.6.1.系统测试 1)重启测试:模拟用户早期使用过程中开关机操作,来检测终端是否能够正常启动,判断标准,如果时间大于3分钟则认为失败,PowerManager.reboot 2)休眠唤醒:验证终端系统是否能正常休眠和唤醒 PowerManage.goToSleep / PowerManage.wakeUp 1.6.2.后台测试 1)EMMC测试:读写测试,判断标准,复制黏贴,比较新旧文件的长度,原始文件位于res/raw文件夹下,打包在apk中,FileInputStream读取test_emmc.mp3文件,新文件被复制到FileOutputStream写入到/data/data/com.caltta.aging/文件夹下

2)Memory测试:比较标准是内存操作前后文件大小的对比,内存操作包括对二进制数据进行乱序,移位,取反等操作 1.读取资源中的test_memory.jpg文件到buffer中(res/raw文件夹下,打包在apk中); 2.分别对数据依次进行移位、取反运算(按照Memory测试次数,第奇数次左移,第偶数次右移); 3.将变换后的数据写入到DDR的block2区中; 4.将DDR的block2中的文件读到buffer中。 5.比较两个buffer,如果相同,则测试通过,并且删除写入的文件;否则,测试失败

3)马达测试:马达震动,振2秒停1秒,升温过程中的马达震动,激发器件虚焊,接触不良,脏污等问题

4)声音测试:MediaPlayer播放音乐,AudioManager.setMode()切换,切换时调用AudioManager的setStreamVolume方法将音量设置为最大

5)CPU测试:高低温循环,启动多个线程,对CPU施加高负载压力,模拟用户高负载CPU场景(CPU的使用率在99%以上)

1.6.3.前台测试 1)视频测试:模拟终端用户看视频的操作,MediaPlayer播放视频,声音开大 2)LCD测试:红、绿、蓝、白、黑五种颜色轮流在LCD上全屏显示,切换消耗功耗较大,2D和3D图形显示,检测终端有无冻屏等现象

Image

Image

2D测试效果图

Image

Image

Image

Image

3D测试效果图 3)Camera测试:打开摄像头、参数设置、预览、关闭摄像头,前后摄像头切换

fWX228941 commented 2 months ago

2.省电模式 2.1.架构

Image

职责划分 1)CalttaBatteryPolicySerivice.apk 省电策略服务应用(黄色)系统服务BatteryPolicyService,保存各电池模式下的所有省电策略配置,维护系统省电模式状态,以及在系统省电模式状态或者省电策略发送变化时,通知给注册的模块 BatteryPolicyProvider:保存电池模式,省电策略等数据,提供增删改查功能,对外提供原始查询接口ContentProvider BatterySaverController:封装原始数据,提供业务接口,比如是否是省电模式接口,注册注销接口等 BatterySaverStateMachine:省电模式状态的切换 (切换条件)

2)BatteryPolicyManager 封装省电服务并对外提供统一外观模式接口(橙色),通过Binder机制来获取系统服务BatteryPolicyService服务接口(参考其他模块的manager 与 service) 3)3)提供客户端需要的业务接口以及通知回调机制CalttaBatterySettings 设置客户端(青色)用于UI展示以及人机交互,BatteryPolicy.jar由BatteryPolicyManager相关类打包而成,UI与业务(CalttaBatteryView / CalttaBatteryController)将BatteryPolicyManager以及BatteryPolicyConstanst类打包成BatteryPolicy.jar提供给电池设置应用调用

4)真正实现省电策略的各个系统组件,包括进程管理,定位,蓝牙,屏幕等等 具体实现 1.整个数据管理以及接口实现参考原生SettingsProvider以及其接口Settings类来实现的 第一次进入系统,并且 /system/etc/calttaconfig/battery-saver-policy-XXX.xml文件存在,则将这些文件复制到data/system/calttaconfig/battery-saver-policy-XXX.xml下面。然后解析省电模式,超级省电模式这2个策略文件,解析是通过BatteryPolicySettingsState类完成的,解析完2个xml文件数后,数据会保存到SparseArray mSettingsStates集合中,集合key为电池模式,value为BatteryPolicySettingsState代表着xml文件数据。 2.对于这些数据的增删改查操作,都是通过调用BatteryPolicySettingsState方法来实现的,对于这些数据的变化,最后需进行URL通知

Image

Image

BatteryPolicySettingsState类用于解析省电策略xml文件,将数据保存到内部缓存,提供增改查接口,用与对缓存数据的操作,最后将这些缓存保存到xml文件。缓存数据分为:

//其中key表示省电策略名,value表示策略配置信息。 1)ArrayMap<String, BatteryPolicySetting> mSettings = new ArrayMap<>; //其中key表示白名单应用包名,value表示所有策略配置信息。 2)ArrayMap<String, List> mSettings = new ArrayMap<>; BatteryPolicySetting数据结构如下:

Image

BatteryPolicySettingsState提供的接口如下:

Image

Image

Image

对于每个xml文件数据的访问,都是通过调用BatteryPolicySettings对应一个静态内部类的方法来实现的。基本信息对应BatteryPolicySettings.Setting类,省电模式策略对应: BatteryPolicySettings.PowerSettings类,超级省电模式策略对应: BatteryPolicySettings.SuperPowerSettings。 每一个省电模式下都有一套可定制的具体省电策略

BatterySaverStateMachine监听充电,电量状态,电池模式变化

Image

BatterySaverController

Image

Image

1)电池模式设置以及查询接口,如下:

Image

2)各电池模式下的省电策略设置以及查询接口,如下:

Image

3)当前状态下省电策略查询以及变化注册接口,如下:

Image

Image

1.核心服务的标准配置,userid = system ,coreApp = true ,process = system

Image

SystemServer会在startOtherServices阶段, 由AMS调用installSystemProviders()去创建和初始化BatteryPolicyProvider对象,我们可以在其onCreate方法去添加BatteryPolicyService系统服务

Image

Image

2.系统服务,还需要配置selinux相关规则,服务添加完成后,其他系统服务或模块就可以访问该服务接口,通过ContentProvider这种方式去启动服务,会比设置persist应用要快, 3.provider中数据的保存方式,基本信息保存到/data/system/calttaconfig/Settings.xml文件中 (系统配置文件的默认保存位置,保持一致即可)

Image

//电池模式saver_mode ,0:普通模式;1:省电模式;2:超级省电模式 //触发方式 auto/manual 自动与手动 手动设置的电池模式manual_battery_saver_mode 取值范围0-2,默认值0 自动设置的电池模式auto_battery_saver_mode取值范围0-2,默认值0 自动触发电池电量百分比阈值auto_battery_saver_trigger_level 取值为5~75,默认值15

4.具体省电策略的配置功能项配置文件,保存在,存在项目定制等 /data/system/calttaconfig/battery-saver-policy-XXX.xml /system/etc/calttaconfig/calttaconfig/battery-saver-policy-XXX.xml

Image

白名单,具体功能项包括 震动,动画,防火墙,休眠,背光灯,GPS,锁,闹钟,cpu

用于保存省电策略项,用于配置应用对一些省电策略的白名单。其中name表示省电策略项,value表示省电策略配置值。version表示配置版本号,用于数据升级 对于系统服务调用BatteryPolicyManager接口时,需要在其SystemReady方法后调用。调用接口来获取省电策略,根据策略值来决定是否限制一些行为,从而达到降低功耗的作用。调用接口方式,分为两种,如下: 1)主动调用 在执行某个业务时,获取该业务对应的省电策略,然后限制行为。 2)注册监听 在收到某个省电策略发生变化时,获取省电策略值,然后对某个业务限制其行为。 例如,在VibratorService.java服务中systemReady方法,实施震动策略,如下: ![Image](https://github.com/fWX228941/project/assets/12897054/aa36febe-6286-4db3-ab28-a40fe9ebf1a7)
fWX228941 commented 2 months ago

2.2.需求 2.2.1.类型 1)普通模式:默认模式 2)省电模式/超级省电模式:省电策略配置,功耗影响程度不同(在省电模式下,定位策略为降低后台定位频率,但在超级模式下,定位策略为禁止后台定位) 2.2.2.触发/工作模式 手动设置与自动触发(低电量)

2.3.原生省电(参考展锐方案,太多需要源码研究下) 2.3.1.doze模式 (Android M) 低功耗模式,系统会限制应用访问网络等

Image

备注: adb shell dumpsys deviceidle whitelist 查看idle豁免白名单 2.3.2.light doze模式 //活动状态 static final int LIGHT_STATE_ACTIVE = 0; //非活动状态 static final int LIGHT_STATE_INACTIVE = 1; //light doze状态,该状态下系统会限制 static final int LIGHT_STATE_IDLE = 4; //light doze状态,等待有网络后会进入维护状态 static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5; //维护状态 static final int LIGHT_STATE_IDLE_MAINTENANCE = 6; //此时light doze状态被覆盖,进入deep doze状态 static final int LIGHT_STATE_OVERRIDE = 7;

//进入light idle 状态alarm触发时间 Private long mNextLightAlarmTime; //进入maintenance状态alarm触发时间 Private long mNextLightMaintenanceAlarmTime; //maintenance状态持续时间 Private long mCurLightIdleBudget; //idle状态持续时间 Private long mNextLightIdleDelay;

Image

Image

Image

2.3.3.Background Execution Limits(Android O) 2.3.4.App Standby Buckets (Android P)应用待机模式 会延迟用户近期未与之交互的应用的耗时操作,比如网络访问等 2.3.5.Limit Implicit Broadcast(Android N) 2.4.功耗原理 硬件模块上电后开始耗电了,软件层各种进程持锁保活假死,在后台轮询搜集用户行为或者保持某些长链接来保障数据的实时性,导致系统根本无法休眠下去 (定位,网路,持锁) 2.5.工具 battery historian /adb shell dumpsys batterystat/matrix 2.6日志分析 判断终端是否进入Doze状态,搜索关键字device_idle= device_idle=off 退出doze状态 device_idle=light 进入light doze状态 device_idle=full 进入deep doze状态

3.测试指标

Image

备注:应用的核心指标包括ANR,Crash崩溃,持锁唤醒,其他包括启动时间,后台耗时任务,耗时渲染,冻帧,权限否决

3.1.速度 正常开机速度/恢复出厂设置速度/驻网速度/模式切换速度/起呼速度/IO读写速度/模式切换/ 界面启动速度

工具:Perfboot/测试脚本/打桩测试,埋点LOG/Stagesepx

adb shell screenrecord --time-limit 10 /sdcard/demo.mp4 录制视频 adb shell dumpsys gfxinfo adb devices命令能查看到手机的sn号

3.2.时间(时延) 应用启动时间/亮灭屏时间/起呼延迟 1)应用启动延时 (热冷启动)Startup time – How long does it take your app to startup from click to responsiveness? 操作场景:点击每个应用,统计时延,响应 2)操作时延 (每个进程核心业务的操作) 操作场景:通话过程中接近光亮/主桌面滑动/接听来电/HOME 键退出/批量删除图片/锁屏 界面默认解锁/滑动列表响应时间/power按键点亮屏幕/呼出电话/微信聊天框口打开窗口.... 3)安装时延 (应用安装)

3.3.流畅度 3.4.monkey/压力测试 重启/相机反复打开/CP模块上下电/呼叫(核心业务)

3.5.功耗

工具:matrix

3.6.内存 初始内存(待机休眠)/待机内存(不做业务)/工作内存(持续做业务)/内存泄漏 3.7.跑分 安兔兔 3.8.应用性能管理APM 3.9.界面帧率FPS 掉帧、卡顿、界面响应、界面切换,流程性 3.10.ANR 3.11.文件IO 3.12.apk静态检测 包体积、冗余的文件 3.13.游戏 3.14.开关机专项 1)开机测试 首次开机 非首次正常开机 非首次填充内存开机 非首次拔电池开机 进入模式开机(校准模式开机、 BBAT模式开机、恢复出厂设置开机、老化设备开机)

2)关机测试

3)重启测试

Image

Image

备注:曲线图等各种图形化界面就很直观

4.性能工具 4.1.perfetto 4.2.Stagesepx 4.3. matrix 4.4. bugly 4.5. ArgusAPM 4.6. perfetto-trace 4.7. Vitals dashboard 4.8. Android Studio Profiler 4.9. Android CPU Profiler 4.10.其他 Elicpse MAT、GPU profiling、Hierarchy View、LeakCanary、Lint、Memery、Profile GPU Rendering、Strict Mode、Systrace、Tracer for OpenGL、Traceview、Vitals dashboard、Hertz 4.11.跑分

Image

5.内存泄漏 5.1.原理 当短期对象的生命周期结束的时候,本该被释放的对象却被更长生命周期的对象所持有,导致短期对象无法被GC回收与释放,造成内存泄露 There are some objects which are considered “important” by the GC. These are called GC roots and are (almost) never discarded. They are, for example, currently executing method’s local variables and input parameters, application threads, references from native code and similar “global” objects. Any objects referenced from those GC roots are assumed to be in use and hence not discarded by the GC. One object can reference another in different ways in Java, in the most common case an object A is stored in a field of an object B. In such case we say “B references A”. The process is repeated until all objects that can be transitively reached from GC roots are visited and marked as “in use”. Everything else is unused and can be thrown away. Android (JAVA) 的内存分配的策略包括三种,分别是: 1)静态存储区:存放静态数据和常量,与应用生命周期保持一致,编译阶段,固定内存。 2)栈区:存放方法体中的临时变量,方法执行结束后被回收, 3)堆区:存放对象,其生命周期由GC控制,动态分配内存,由于不规范的编码方式,影响到了GC行为,导致内存泄露, 5.2.内存泄漏优化建议 1)全局工具类或者单例模式对外提供构造接口时,Static 的 Context取Application的Context值 , 而不应该持有Activity ,Service 等短暂生命周期的Context,同时提供无参的单例模式。 2)资源未关闭造成的内存泄露,BroadcastReceiver/ ContentObserver /File /Cursor/ Stream /Bitmap/ Handler,需要在回调方法中,成对创建或者销毁,比如Activity/Fragment.onCreate() 创建注册,Activity/Fragment.onDestroy() 销毁注销,bitmap图像视频对象的溢出是最常见的,包括采样率,压缩 3)非静态内部类/匿名内部类默认会持有外部类的引用,如果将这个引用再传入一个异步线程,并被异步线程持有,此线程和此Activity生命周期不一致的时候,则会导致内存泄露,延迟的Handler事件处理也要特别注意,一旦延迟的时间超过了Activity的销毁时间,就会造成内存泄露 以Handler为例,延迟执行任务的Message将会持续存在于主线程中,它持有该Activity的Handler引用,然后又因为Handler为匿名内部类,它会持有外部类Activity的引用,所以此时Finish()掉的Activity就不会被GC回收掉,而Activity出现内存泄露代价是昂贵的,因为它里面包含了UI等所有的视图层级,这占用了很多的空间。 一种解决方案是将内部类声明为静态的,Context声明为弱引用,则其存活期跟Activity的生命周期无关,但这样一来临时量就变为了持久量,不予推荐。第二种方案是在Activity的onDestry()中及时终止任务,取消线程,清空消息。

Image

备注:体现出了ViewModel的好处了 4)循环引用,相互持有对象引用容易造成内存泄露,弱引用允许更灵活的对象生命周期管理。当对象不再被强引用引用时,垃圾回收器可以自动回收它,而无需手动处理,多久处理呢,无需开发者干预,弱引用的生命周期是由垃圾回收器来管理的,开发者无法确定确切的回收时机。弱引用引用的对象可能在任何时候被回收,当没有强引用引用该对象时,垃圾回收器可能会回收它,对象的生命周期不可控,weak reference建议还是慎用吧 5)View对象本身对所属的Activity是有引用关系的,每个 View 都持有当前 Context, Activity 的引用,如果工作线程持续保有View的引用,这就可能导致Activity无法完全释放,这就是为什么工作线程,业务中不要持有UI相关的任何东西,UI代码里面也不要看见任何线程相关的逻辑,不要在任何非UI线程里面去持有UI对象的引用,当Activity被销毁的时候,由该Activity所触发的非UI线程都将无法对UI对象进行操作,所以触发非UI线程的操作不要放在UI线程里面 6)虚拟机GC做STW(stop the world);也就是说,为了回收那些不再使用的对象,在App自己的进程空间,拟机必须要停止所有的线程来进行必要的工作,版本迭代这一部分应该是有优化的,每一个App都有一个单独的虚拟机实例,我们有相当大的主动权。这样来影响GC的策略,使得GC尽量减少 5.3.内存保活以及如何给进程分配最大内存,避免被GC回收 Application Framework内存回收优先级 Empty process(空进程) Background process(后台进程) Service process(服务进程) Visible process(可见进程) Foreground process(前台进程)

提高进程优先级,互相唤醒,native保活 AMS负责评分obj,最终算出一个最低分交给Linux内核,由它来真正实现内存回收,耗内存的操作放到Native层,或者使用分进程的方式突破每个进程的Dalvik Heap内存限制

最近分配的对象会存放在Young Generation区域,当这个对象在这个区域停留的时间达到一定程度,它会被移动到Old Generation,最后累积一定时间再移动到Permanent Generation区域。系统会根据内存中不同的内存数据类型分别执行不同的gc操作

省内存的策略 1)数据结构优化 SparceArray 2)图片压缩复用,开源框架 5.4.OOM分析 一方面当JVM没有足够的内存空间来为申请对象分配可用内存,一方面GC也没有多余的空间来回收空间,会抛出java.lang.OutOfMemoryError: Failed to allocate /java.lang.StackOverflowError

一个对象被回收, 必须满足两个条件: 1)没有任何引用指向它 2)GC有空

内存溢出:申请的超出内存了JVM 申请的内存超过了Dalvik Heap的最大值能提供的内存大小 (备注:Android的Native Heap可分配的空间完全是由硬件RAM来决定的,硬件才是真正空间的原始提供者)

内存泄露:申请的存在内用完后没有释放,JVM不能再次分配内存

内存抖动:内存频繁地分配和回收,而频繁的gc会导致卡顿

Manager: Killing会触发杀进程的操作,关键字包括:crash,kill,died,signal发信号杀死,EventLog中最后一个参数reason代表是通过何在方法触发kill,原因包括异常杀死,主动杀死,调度杀死,其他杀死,am_proc_died :进程死亡

Image

Image

Image

Image

5.5.Leakcanary工具 配置方式,Androidstuido/eclipse/源码集成

Image

Image

备注:源码有专门的目录来存放,配置好MK文件 内存文件 .hprof文件可以具体分析,至少是可以找到哪一个进程占用了大量的内存

MAT + LeakCanary + Monkey Test

5.6.减少内存优化建议 极端低内存场景测试,开机内存等 1)限制常驻进程和服务的数量和大小 2)使用惰性的内存加载机制,当使用完后尽快释放内存 (按需加载,懒加载) 3)减少不必要应用在开机阶段启动 (提高开机时间,裁剪应用) 4)限制预置应用启动个数,因为预置应用会通过服务和通知等机制增加额外的内存占用 5)关注应用启动的内存占用峰值,关注应用是否存在内存泄露 6)降低 APK 的大小,例如移除无用的资源、在打包时需要对代码优化(代码混淆) 7)合理配置系统功能、ART 参数、LMKD 水位以及Kernel配置 8)

6.性能优化 6.1.性能类别

Image

特点: 1)各个模块都不是孤立存在的,模块之间相互联系、相互影响、相互冲突。 2)其他还包括代码优化,安装包优化等。 6.2.性能目标

Image

6.3.线程优化 6.3.1.线程分类

Image

6.3.2.主线程堵塞

Image

一旦我们在主线程里面执行的任务过于繁重,就可能导致接收到刷新信号的时候因为资源被占用无法完成此次刷新操作,这样就产了掉帧的现象,刷新频率也就跟着下降了,一旦刷新帧率降到20fps左右,用户就明显感受到了卡顿不流畅了,严重会导致ANR

6.3.3.多线程技术

Image

备注: 1)可以使用线程安全的组件,比如LiveData 2)工具推荐:systrace MAT 6.4.绘制优化 6.4.1.显示原理

Image

Android应用程序把经过测量、布局、绘制后的surface缓存数据,通过SurfaceFlinger把数据渲染到屏幕上,通过Android的刷新机制来刷新数据。即应用层负责绘制,系统层负责渲染,通过进程间通信把应用层需要绘制的数据传递到系统层服务,系统层服务通过显示刷新机制把数据更新到屏幕 6.4.2.过渡绘制

Image

过度绘制会导致屏幕显示的色块不同 6.4.3.优化绘制

Image

工具推荐:Hierarchy View / Profile GPU Rendering / Systrace

6.5.内存优化 原理:尤其是Activity容易出现内存泄露,其代价也是昂贵的,因为它里面包含了UI中所有的视图层级,这占用了很多的内存空间,会减少可用内存并频繁触发GC事件尝试释放更多空间,而这些GC事件会让CPU很忙,结果是降低了整机性能。

工具:MAT/LeakCanary/Memory Profiler

6.6.耗电优化 背景:电量消耗问题一直是困扰Android设备商和Google的大问题。 由于越来越多的应用会在后台运行时“假死”,不断在后台轮询搜集用户行为或者保持某些长链接来保障数据的实时性,当这类应用越来越多,就会导致用户的Android设备电量越来越难维持,手机越来越烫。

措施:Android自身并未出台对应的策略来约束或者限制这类应用行为,不过最近几个系统版本,针对电量优化这一块做出了不同程度的限制优化,以确保后台任务对电量的消耗影响足够小。

工具:battery historian

Image

6.7.数据库优化

Image

结论:如果时间允许,项目建议使用更成熟的ORM开源框架,推荐greenDAO / ORMLite / LitePal / Realm。

7.APK体积廋身 7.1.apk构成要素 1)classes.dex 代码文件 2)资源文件:Res/assets/resources.arsc/清单文件/So文件 3)META-INF 签名信息

7.2.测试工具 APK Analyzer

8.ANR 8.1.触发场景 (实际原因往往与此无关) ANR是一套监控Android应用响应是否及时的机制 按键超时:KeyDispatchTimeout (5 seconds) 广播超时:BroadcastTimeout(10 seconds) 服务超时:ServiceTimeout(20 seconds) 数据源超时:ProviderTimeout

UI主线程堵塞过长时间,耗时,等待,死锁 8.2.分析工具 /data/anr/traces.txt https://github.com/SalomonBrys/ANR-WatchDog

9.UI优化 9.1.过度绘制 卡顿与丢帧,Design与Performance平衡,Overdraw(过度绘制)指屏幕上的某个像素在同一帧的时间内被绘制了多次 9.2.工具 Hierarchy Viewer/Profile GPU Rendering/Systrace/Systrace WalkThrough 

9.3.刷新机制与View原理 surface缓存数据,SurfaceFlinger渲染数据到屏幕上,缓存的数据是经过View的绘制 Measure测量 Layout 布局 Draw 绘制

Image

A View occupies a rectangular area on the screen and is responsible for drawing and event handling. interactive UI components  View最本质的就是提供人机交互,不仅仅只是画图,如果是纯粹画图大可用其他接口 CPU负责计算显示的内容,视图创建,布局计算,图片编码,文本绘制,CPU将计算好的内容提交给GPU,由GPU进行变换,合成,渲染 9.4.过度绘制优化方案

Image

Background 背景色的绘制(设置为Transparent)Android系统会通过避免绘制那些完全不可见的组件来尽量减少Overdraw,所以优先使用Android的高级组件,尽量减少自定义绘制,这里面有很多学问,学习成本是很高的,比如可以利用canvas.clipRect()来帮助系统识别那些可见的区域,避免Overdraw,UI占用的内存越少,绘制的越流畅

9.5.卡顿 9.5.1.卡顿场景

Image

备注:计算启动时间

9.5.2.卡顿优化 1)规范主线程的行为,职责是处理用户交互,图形化用户数据,管理UI生命周期 2)布局优化,避免过度绘制,减少层级,合理使用Merge,ViewStub,include 3)bitmap 留心注意

10.终端质量 10.1.设备类型

Image

备注:好的应用是都要适配的,不同的操作系统下,差别很大的,参考原生是如何兼容的 10.2.用户体验

Image

11.多线程 11.1.主线程堵塞 1)IPC跨进程binder调用会使UI线程暂停使用,所以Binder调用通常不应该在UI线程中执行,Binder调用通常是同步的,即调用方会等待调用的返回结果。如果在UI线程中进行同步的Binder调用,而远程服务的响应时间较长,就会导致UI线程被阻塞,用户界面会变得不响应 2)频繁GC 3)UI线程在等待其他线程某个业务操作的锁,UI线程不应该等待其他线程的执行结果,别的线程应该在拿到结果之后post给UI线程 (在UI线程中使用同步锁或执行等待操)主线程慎用等待锁,所有同步的方式都是存在堵塞等待的弊端,这种就适配工作线程,UI线程只适合异步的方式

11.2.线程技术

Image

Image

Image

Image

Image

1)AsyncTask : 为UI线程和与工作线程之间进行快速的切换提供一种便捷简单的机制,适用于当下立即需要启动,但是异步执行的生命周期短暂的任务 2)HandlerThread: 为某些回调方法或者等待某些任务的执行设置一个专属的线程,并提供线程的调度任务,保证同步性。 3)ThreadPool : 把任务分解为不同的单位,分发到各个不同的线程上,同时进行并发处理。 4)IntentService : 适合于执行由UI触发的后台Service任务,并可以把后台任务执行的情况通过一定的机制反馈给UI

11.3.线程的生命周期与死亡复活 进程的核心之一就是协调好各个组件的生命周期,在他们活着的时候好好干活,互不干扰又能相互协作,共同完成复杂的业务

Image

Image

Looper:能够确保线程持续存活并且可以不断的从任务队列中获取任务并进行执行,这就是为什么要优先使用ThreadHandler的原因,保证线程不被回收,由代码业务来控制生死,

11.4.示例 问题:在工作线程中进行了UI相关操作,对短信合法性检查放在工作线程中,包括弹出Toast提示,Dead Thread的提示是因为工作线程处理完事件后被销毁了,但是Toast并没有一同销毁,还在工作,而当Toast打算弹出Toast时(出现概率性不弹框),发现拉起它的工作线程已经Invalid,所以这个EC单其实是一个偶现问题,当工作线程在Toast工作结束后还完好存活着,则不会报这个错误,但会报另外一个错误Only the original thread that created a view hierarchy can touch its views 所以最规范的做法是把UI相关的工作放在UI主线程中进行.

Image

Image

Image

都采用通知的方式,如果UI 还活着自己监听,如果UI已经死亡了,就没有必要订阅了,

11.5.主线程 职责: 1)thread-safe work queue 线程安全 2)Lifecycle-related Application components 四大组件的生命周期由主线程管控,包括回调方法的执行,四大组件也是原始数据的一个源头之一,在这些组件活着的时候,他们持续工作,接受原始数据,我们需要把第一手数据打包给子线程来管控,不能让主线程同步干等着 3)User events including input events, 4)UI 更新 Android UI toolkit is not thread-safe, and UI updates must be performed on the main thread to avoid synchronization issues 非线程安全的UI 组件,需要主线程来管理排队管理,保证安全性

11.6.工作线程 1.events from other apps and processes 事件携带的数据,用户交互事件产生的数据,其他进程产生的数据比如IPC拿到的数据源其线程是由其绑定发起的线程决定的,不一定是主线程,inter-process communication (IPC) events or events from other system components, are not necessarily processed on the main thread. These events are typically processed on the thread that dispatches them. 其他进程产生的数据,本质就是调用系统服务拿取到的原生数,比如获取定位数据,可以传递一个工作线程的通道下去,这样就建立了跨进程的数据通路的链接 different components may communicate with each other, and events (e.g., messages, broadcasts) are sent between them. When such events are dispatched or sent, they are usually handled on the same thread that initiated the dispatch. This means that the processing of these events occurs on the thread that originally generated or dispatched the events. (其他进程中拿到的数据包括调用API都要在工作线程中进行) 2.工作线程的指导原则就是,千万不要持有View的任何引用,做View的任何更新工作, If you try to modify or even reference a UI object in a thread other than the main thread, the result can be exceptions, silent failures, crashes, and other undefined misbehavior.Issues with references fall into two distinct categories: explicit references and implicit references 两个后果 1)View在工作线程存活期间被销毁,而持有View引用的对象还在,造成UI的内存泄漏 2)工作线程销毁,View还存活 也是一堆的问题,甚至崩溃

3.UI线程在进程运行时是不会销毁的,是无法干预的,其生命周期等价于应用的生命周期。工作线程的生命周期是可以人为干预控制的,但人为控制是存在风险的,一旦处理不好就容易造成内存泄漏或者其他问题,线程的管控就是业务的管控 4.一个线程的开销成本是64K内存,Reusing thread pools is a strategy that can contribute to more efficient resource,推荐优先使用线程池 12.数据库 原生的SQLite框架 不推荐使用,批量操作,索引,事务

13.性能管控服务Performance Manager Service

Image

是一套基于场景和系统状态的集数据采集、数据分发、功能管控、策略下发、动作执行于一体的性能和功耗调优框架

Image

用户的行为 -> 应用的行为 -> 交互场景与非交互场景 -> 行为轨迹 ->状态

Image

1)场景包括:appstart应用启动/xtest测试/touch触摸/localechange本地配置/preforkprocess进程/rotateScreen旋转屏幕/keepappalive保活 2)特性包括:snapshot快照/AppAnim 应用动画/MemoryClean 内存清理 事件包括:activitystart界面启动/focusactivity界面焦点/shutdown关机/localechange配置/orientationchange 屏幕旋转/screen onoff 亮灭屏/ device usb plugged usb插拔/motion 触摸/user changed 切换用户/ systemui_ini 状态栏/ call state 呼叫状态/ restart app 应用重启

Image

Image