2023年6月28日发(作者:)
MTK平台性能优化---(1)综述1、概述 ⼿机性能优化越来越重要,本⽂介绍了性能评估、分析和优化的⽅法。在接下来的章节中,将会分别介绍关于系统、开机启动时间、应⽤启动速度性能优化,同时也会介绍如何使⽤⼀些有⽤的⼯具去评测性能瓶颈。可能对性能产⽣影响的⽅式有多种,例如:测试⽅法、系统配置和驱动的修改。因此,⾸先要分析环境对性能瓶颈的影响,通过消除不同测试⽅案中的配置能够找到产⽣性能瓶颈的原因。同样要注意到低内存的配置也会影响性能。在低内存的环境中,有可能启动linux kernel的内存压缩机制。这将会使性能降低取决于在运⾏态压缩和被压缩的内存。另外关于低内存使⽤的问题,总内存⼤⼩不会使所有的进程都处于存在的状态,⼀些进程会不停的杀死和重启在⼀定程度上会影响性能。低内存的配置将会导致cache的⼤⼩⼩于正常的⽔平,因此性能可能会受到影响。除了环境因素外,⼀些⼯具被⽤来分析性能瓶颈问题。性能瓶颈可能会发⽣当意想不到的应⽤运⾏在后台时跟前台UI进程竞争系统资源(CPU或内存)。此时可以使⽤⼯具”top”查看CUP占有率情况。2、系统性能2.1 本节简介⾸先,我们介绍⼀些正确的评测⽅法和有⽤的⼯具去分析⾼效的分析性能下降的根本原因。并举例说明,你可以参考⼀下并快速定位同⼀类问题产⽣的根本原因。2.2 如何缩⼩可能的根本原因?2.2.1 性能评测和环境搭建在搞清楚瓶颈或优化性能之前,我们必须建⽴⼀个具体的⽬标和建⽴⼀个准确的评测环境。1、量化性能:定义性能⾸先需要定义性能的好或差,虽然⽤户体验可以感觉出来,但是我们需要量化性能的状态。例如:应⽤启动时间太慢是啥意思?是指⽤户等待应⽤布局界⾯完全显⽰出来,或者⽤户等待所有的功能都准备好么?如:浏览器需要完全加载web⽹页信息。2、环境定位:准备的评估硬件定位:系统的⾏为会受到各种因素的影响,尤其是后台进程或者监控进程,例如:Wi-Fi、USB、⽇志系统(adb)。同样外设可能产⽣不同的结果,如:SD卡。我们必须评估性能在同样的环境中,例如:同样的SD卡,没有USB连接。软件定位:⾸先确保你的代码版本是正确的,如:打上补丁的最新版本;其次,纯净的代码库,意味着你的代码不包括其他不相关的修改。2.2.2 有⽤的分析⼯具有⼀些从系统级别到⽤户级别的分析⼯具可以帮助分析性能。通过恰当的使⽤这些⼯具,提供⽤户指导在其他章节。本节介绍何时使⽤这些⼯具。1、搞清楚系统的脚本[ftrace-scheduleswitch tracer]通过ftrace显⽰进程/中断的CPU占⽤情况,⼀般情况下,这个⼯具能够帮助:1) 确保是否有任何不期望的后台进程或监控程序;2) 确保每个进程的执⾏序列;3) 在特定时间段每个进程或中断的执⾏时间;4) 验证是否有不需要的空闲时间
2、找到可能的瓶颈理解了系统脚本和它正常的⾏为,如:没有后台进程/监控程序,没有不必要的空闲时间。然后需要进⼀步找可能的瓶颈。有两个有⽤的⼯具可以帮助验证可能的瓶颈。[cputime – Athread level profiling tool]它提供了每个进程在特定时间段的执⾏时间;[oprofile –Function/Library level profiling tool]它提供了⼀个统计的结果显⽰每个函数或库执⾏的频率。
3、分析可能的瓶颈⼀旦找到可能的瓶颈,例如:某个进程、应⽤、库、函数,可以使⽤更精确的⼯具去分析它,通过在源代码⾥嵌⼊相应的调试⽅法。[Traceview] 分析⼯具可以帮忙分析java应⽤程序;[Insertdebug log] 稳健和精准的⽅法。SW开发者可以嵌⼊他们的计算器API去评估每⼀个热点或打印出事件⽇志去验证SW阶段的持续时间。2.3 内存压缩(ZRAM)2.3.1 什么是ZRAM?对于低成本的项⽬,的内存空间有限,在android系统⾥⾯会运⾏⼤量的进程。内核、驱动、服务、后台监控程序占据内存,留下⾮常有限的内存给应⽤。为了更⼤化的利⽤内存,可以启⽤⼀种内存压缩机制叫做ZRAM。当内存不充⾜时,内核将会对最近不使⽤的内存进⾏压缩,在运⾏时获取更多的剩余内存可以使⽤。当被压缩的内存再次使⽤时,内核将会在使⽤之前解压缩它。因为并不是所有的内存都在运⾏时使⽤的,这种⽅式很有⽤。2.3.2 缺陷ZRAM并不是⼀个完美的解决⽅案,它也会引起⼀些问题。如:在压缩和解压缩内存过程中对CPU的消耗。如果内核频繁进⾏压缩和解压缩内存的操作,将会对CPU占⽤时间产⽣影响,进⽽导致性能下降。2.3.3 ZRAM在系统能够负担的起的情况下,使⽤更多的内存,⼤多数内存被压缩,然后被⽴即使⽤。在这种情况下,内存将会不停地压缩和解压缩,叫做ZRAM Thrashing。2.3.4 如何检查影响ZRAM痛打可能会影响性能,要注意以下⼏点: 2.3.4.1 cat/proc/vmstatZRAM是基于Linux交换机制,每个页在换出过程中压缩,在换进过程中解压缩。因此可以通过访问pswpin、pswpout的值来检查内核是否在进⾏ZRAM压缩和解压缩。由于内核内存的回收将会释放⼀些不使⽤的页⾯缓存,⼀旦空闲的页⾯缓存再次使⽤的时候,将会导致⽂件映射故障过程,并从存储中获取页⾯缓存。应该通过访问pgfmfault的值去检查加载的过程。可以通过查看SwapTotal获取zram的总⼤⼩,通过SwapFree获取可以使⽤的zram⼤⼩。 2.3.4.3 cat /proc/zraminfo不同的场景压缩率是不⼀样的。在⼤多数情况下,压缩率可以达到30%,在多媒体场景下可以达到40%。DisSize相当于SwapTotal;OrigSize意思是压缩页的原始⼤⼩;ComprSize意思是压缩页的压缩⼤⼩;MemUsed意思是实际内存使⽤情况。
2.3.4.4 cat/proc/mlog or dmlog系统时刻会记录重要的内存信息,这些数据将保存在⼀个环形缓冲区,⼀旦数据超过⼤⼩,将会被覆盖。可以使⽤cat /proc/mlog 或dmlog命令获取内存⽇志信息。⽇志格式如下所⽰(PS:⾼通8916 5.1平台不⽀持)2.3.5 如何防⽌Thrashing问题 ZRAMThrashing问题是由于较⾼的内存使⽤引起的,要从内存优化⽅⾯⼊⼿避免产⽣这种现象。2.3.5.1 低内存被杀(Low memory killer) LMK能够减少这种痛打问题。当内存不充⾜时,LMK将会杀死后台的java进程获取更多的内存。这将充分利⽤zram内存回收,并防⽌导致系统性能下降。尽管如此,LMK只会杀死那些优先级⼤于0的java进程。如果有很多常驻进程,LMK不会杀死它们,可能会导致zram颠簸。2.3.5.2 减少内存使⽤率换句话说,如果总的内存使⽤率⽐较⾼的话,应该尽量减少内存的使⽤。它可能是由于内存泄漏或异常的内存使⽤引起的,可以通过使⽤procrank(命令:procrank -s)或者librank来检查内存使⽤明细。2.4 进程颠簸问题 2.4.1 进程颠簸的定义进程颠簸定义为:“进程在⼀段时间内不停地被杀死然后⼜重启,这种被杀/重启占据⼤量的时间,会使性能变差。”2.4.2 如何识别进程颠簸进程颠簸跟进程杀死/重启相关的,进程的⽣命周期可以从event⽇志中分析。2.4.3 如何处理进程颠簸1)修改LMK的阀值 2)使进程的adj优先级变⼩ 3)让进程使⽤较少的内存(java进程合并、java进程转变成native进程、改变⽤户场景)2.5 ANR(Application Not Responding)2.5.1 概览本章将会描述ANR的基本概念,调试技巧,并给⼀些有⽤的指导去分析解决ANR的问题。2.5.2 基本概念在android系统中,⽤户在⼀段时间内操作应⽤没有及时响应的话,就会弹出⼀个对话框,即应⽤⽆响应对话框,包含两个选择按钮:强制关闭和等待。2.5.3 ANR类型应⽤程序响应受到AMS、WMS系统服务监控,⼀般在满⾜如下条件之⼀时对于特定的应⽤android才会显⽰ANR对话框:1、按键分发超时:对于输⼊事件(按键、触屏)在8s内⽆响应2、⼴播超时:前台⼴播接收器在10s内没有处理完;后台⼴播接收器在60s内没有处理完3、服务超时:在20s内请求服务失败2.5.4 ANR⽇志分析⽅法ANR的分析将基于MTK平台讨论ANR的⼀般解决思路,旨在通过了解本⽂后能够对ANR问题能够快速定位,减少排查的时间。最好使⽤userdebug版本测试,因为ENG版本测试的话本⾝会加重CPU loading获取⽇志还有⼀点需要注意,发⽣ANR后,不要选择结束进程,因为这样AMS会kill掉该进程,有些信息会打印不出来,最好是ANR发⽣后等两三分钟左右,再将mtklog收集起来(写⼊到aee_exp⽂件夹下需要时间)。这⾥的aee_exp⽂件夹⼀般都是需要的, 对DB进⾏dump解析,得到ANR发⽣时场景信息,⽐如主线程callstack,CPU,memory等,在分析问题根因时很关键。⽇志分析⼀般步骤Step1:查看mobilelog⽂件夹下的events_log,搜索关键字"am_anr",这⼀步⽤于确认ANR时间点,可以搜索到类似如下信息11-09 21:33:21.518: I/am_anr(1041): [0,1848,ts,953728589,Input dispatching timed out (Waiting tosend key event because the focused window has not finished processing all of the input events that were previouslydelivered to it. Outbound queue length: 0. Wait queue length: 1.)]这个例⼦中的ANR类型为Input dispatching timed out, 这种anr的原因的是在viewrootimpl分发事件时,并没有找到focuswindow导致的。时间点:21:33:21,进程号:1848 注意这⾥的进程号⽐较重要,后⾯要去或者db⽂件中去看这个process(1848)对应的backtraceStep2:查看mobilelog⽂件夹下的main_log,搜索关键字"Application is not responding",发⽣ANR的时间或DB(MTK⼯具GAT)解析的_exp_ktrace:
Process: tsFlags: 0x38d8be4dPackage: ts v24 (1.2)Foreground: YesActivity: ts/tListMultiChoiceActivitySubject: Input dispatching timed out (Waiting to send key event because the focused window has not finished processing allof the input events that were previously delivered to it. Outbound queue length: 0. Wait queue length: 1.)Build: D-171117V33:user/release-keysnullAndroid time :[2017-11-19 03:31:26.86] [107321.313]ANR期间的 Cpu usage,前⼏个占⽤情况以及Total如下:CPU usage from 74494ms to 0ms ago (2019-11-09 21:32:06.966 to 2019-11-09 21:33:21.460): 104% 323/mobile_log_d: 17% user + 86% kernel / faults: 2622 minor 56% 1041/system_server: 41% user + 14% kernel / faults: 30698 minor 36 major 34% 1408/: 28% user + 6% kernel / faults: 17027 minor 35 major 15% 5541/: 13% user + 2.3% kernel / faults: 10865 minor 49 major 10% 248/servicemanager: 4.4% user + 6.5% kernel / faults: 63 minor 10% 249/surfaceflinger: 5.1% user + 5.6% kernel / faults: 1645 minor 1 major 8.6% 215/logd: 2.7% user + 5.8% kernel / faults: 310 minor 11 major 8.6% 215/logd: 2.7% user + 5.8% kernel / faults: 310 minor 11 major 5.6% 1848/ts: 4.4% user + 1.1% kernel / faults: 9569 minor 677 major 3.7% 1155/ui: 2.9% user + 0.7% kernel / faults: 12145 minor 50 major98% TOTAL: 54% user + 42% kernel + 0.1% iowait + 0% softirq很多时候需要注意iowait的占有率,如果占⽐⽐较⾼,则排查的⽅向要倾向与读取⽂件操作有关的信息,可以看trace⽇志中有没有读取⽂件或者操作SD卡的动作还有注意观察⼀下在出现“Application is not responding”信息往上看,注意主线程是不是有耗时的动作,可以搜索关键字"ActivityThread"且搜索到的⽚段含有ANR的进程名。如果发现ANR时CPUloading并不⾼,再去检查memory的情况,以排查是否存在竞争memory的情况。aee_exp⽂件下的db⽂件解析后会有⼀个 SYS_MEMORY_INFO⽂件,这个⽂件会列出问题发⽣时的MemTotal,MemFree,MemAvailable等信息。Step3:查看时间段内对应线程的 call stack----- pid 1848 at 2019-11-09 21:33:21 -----Cmd line: tsBuild fingerprint: 'TECNO/H3721A1/TECNO-IN5:7.0/NRD90M/A-171108V56:user/release-keys'ABI: 'arm64'Build type: optimizedZygote loaded classes=4375 post zygote classes=1641Intern table: 50890 strong; 232 weakJNI: CheckJNI is off; globals=848 (plus 2739 weak)Libraries: /system/lib64/ /system/lib64/libcompiler_ /system/lib64//system/lib64/ /system/lib64/ /system/lib64/libmedia_/system/lib64/libwebviewchromium_ (9)Heap: 6% free, 35MB/37MB; 294645 objectsDumping cumulative Gc timings"main" prio=5 tid=1 Native | group="main" sCount=1 dsCount=0 obj=0x75740b50 self=0x7128096a00 | sysTid=1848 nice=0 cgrp=default sched=0/0 handle=0x712c693a98 | state=S schedstat=( 343191137004 3 540669 ) utm=28155 stm=6164 core=0 HZ=100 | stack=0x7fdc4ca000-0x7fdc4cc000 stackSize=8MB | held mutexes= at ctNative(Native method) at ct(:622) at dow(:163) at (:82) at Position(:236) at Position(:197) at m(:436) at aId(:230) at mId(:249) at mId(:194) at erSyncState(:1255) at mChecked(:1142) at CheckBoxState(:742) at ctAll(:261) at tListMultiChoiceActivity$k(:383) at mClick(:6076) at mClick(:122) at $(:23138) at Callback(:836) at Callback(:836) at chMessage(:103) at (:203) at (:6292) at !(Native method) at Init$(:1094) at (:955)可以看到在等待binder返回Step4:binder分析 binder_analysis[Info]: Start parse executing & pending transactions for 1848! incoming transaction 118494543: 0000 from 1848:1848 to 5541:5554 code 1 flags 10 pri 0 r1 node118458515 size 68:0 data 0000[Analysis]: This binder call is waiting for 5541:5554 to executing complete. outgoing transaction 118494543: 0000 from 1848:1848 to 5541:5554 code 1 flags 10 pri 0 r1 node118458515 size 68:0 data 0000[Analysis]: This binder call is waiting for 5541:5554 to executing complete. pending transaction 118494731: 0000 from 0:0 to 1041:3702 code 0 flags 0 pri 0 r0 size 8:0 data0000[Analysis]: This binder call is waiting for 1041:3702 to executing complete.查看SYS_PROCESSES_AND_THREADS 线程的信息对端1041(system_server):3702(1041_D)"Binder:1041_D" prio=5 tid=61 Native | group="main" sCount=1 dsCount=0 obj=0x14b0c160 self=0x7110f02200 | sysTid=3702 nice=0 cgrp=default sched=0/0 handle=0x71078e1450 | state=S schedstat=( 775971117390 49 2178398 ) utm=57038 stm=20559 core=2 HZ=100 | stack=0x71077e7000-0x71077e9000 stackSize=1005KB | held mutexes= kernel: (couldn't read /proc/self/task/3702/stack) native: #00 pc bcec /system/lib64/ (syscall+28) native: #01 pc e77c8 /system/lib64/(_ZN3art17ConditionVariable16WaitHoldingLocksEPNS_6ThreadE+156) native: #02 pc aaac /system/lib64/ (_ZN3artL12GoToRunnableEPNS_6ThreadE+328) native: #03 pc a920 /system/lib64/ (_ZN3art12JniMethodEndEjPNS_6ThreadE+28) native: #04 pc b6a0 /system/framework/arm64/(Java_android_os_BinderProxy_transactNative__ILandroid_os_Parcel_2Landroid_os_Parcel_2I+220) at ctNative(Native method) at ct(:622) at vice(:123) at vice(:55) at neId(:1020) at h(:1983) at SignalStrengthForPhoneId(:901) - locked <0x0bb31bba> (a ist) at honyRegistry$sact(:174) at ansact(:570)案例⼀:等待binder调⽤返回SYS_BINDER_INFO这个⽂件查找当前这个线程在和谁通信案例⼆:主线程等待锁主线程的CallStack DVM thread Status是Blocked案例三:卡在IO上这种情况⼀般是和⽂件操作相关,判断是否是这种情况。IO占⽐很⾼,这个时候就需要查看trace⽇志看当时的callstack,着重看有没有file相关的动作。案例四:主线程作耗时的动作耗时操作造成主线程堵塞案例五:binder线程被占满系统对每个process最多分配15个binder线程,这个是⾕歌的设计(/frameworks/native/libs/binder/)如果另⼀个process发送太多重复binder请求,那么就会导致接收端binder线程被占满,从⽽处理不了其它的binder请求这本⾝就是系统的⼀个限制,如果应⽤未按照系统的要求来实现对应逻辑,那么就会造成问题。⽽系统端是不会(也不建议)通过修改系统⾏为来兼容应⽤逻辑,否则更容易造成其它根据系统需求正常编写的应⽤反⽽出现不可预料的问题。判断Binder是否⽤完,可以在trace中搜索关键字"binder_f",如果搜索到则表⽰已经⽤完,然后就要找log其他地⽅看是谁⼀直在消耗binder或者是有死锁发⽣,对于binder⽤完的前期思路⼤致如此。案例六:JE或者NE导致ANR案例七:只存在于Monkey测试下只在Monkey环境下才能跑出来,⽽user版本是不会出现的,这种问题没有改动的意义。另外SAT由于STK应⽤的特殊性,跑monkey会引发⼀些问题,如下为MTK的解释:[DESCRIPTION]Monkey测试时,测试SAT会经常发⽣ ANR 或者phone进程挂掉的问题。有时虽然没有跑到SAT中做测试,仍会因SAT应⽤导致许多异常现象。[SOLUTION]因为monkey测试时根本不适合跑SAT应⽤。主要是SAT应⽤涉及到卡跟⼿机或者卡跟⽹络的交互,这些交互过程并⽆固定的时间长短,且都是需要耗费⼀定时间。因此,monkey测试时如果包含或者跑SAT应⽤就很容易发⽣问题(⽐如很常见的ANR和⼀些其他问题)。建议: 不同于其他 APP,不建议monkey测试包含 SAT 应⽤和测试SAT应⽤。⼩结:总的说来,拿到⼀份eng的完整⽇志,⼀般步骤如下: ANR time,PID,ANR CPU usage and ,mapping process ID and time more information about main thread through main log and event log附录:trace解析该⽂档我们主要说明trace的解析和⼀些客观数据的如何获取。trace它是分析死机的⾮常重要的⼿段,我们可以快速的知道,对应的process/thread 在当时正在执⾏哪些动作,卡住哪⾥等。可以⾮常trace它是分析死机的⾮常重要的⼿段,我们可以快速的知道,对应的process/thread 在当时正在执⾏哪些动作,卡住哪⾥等。可以⾮常直观的分析死机现场。如果现场有问题机的话,我们还可以通过⼀些客观的执⾏环境来分析,通常包括如CPU 利⽤率,Memory 使⽤情况, Storage 剩余情况等。这些资料也⾮常重要,⽐如可以快速的知道,当时是否有Process 在疯狂的执⾏,当时是不是处于严重的low memory 情况,Storage 是否有耗尽的情况发⽣等。我们知道出现问题java bt会在data/anr 保存相关的bt 信息,所以data/anr ⾥⾯的数据针对分析问题是很关键的,正常执⾏adb pulldata/anr 我们都可以获取⼀些⽐较完整的trace信息。下⾯是⼀⼩段system server 的java trace的开始,从第⼀⾏开始我们来按每⼀⾏解析其所代表的含义。1,代表 PID at time2,然后接着Cmd line: process name3,固定头,指明下⾯都是当前运⾏的dvm thread ,“DALVIK THREADS:” 及所包含的线程数 1064,"main" prio=5 tid=1 Native分别代表thread name, java thread Priority, DVM thread id, DVM thread status"main" -> main thread -> activity threadprio :java thread priority default is 5, (正常区域是1-10)tid:是DVM thread id, 不是 linux thread id(下⼀⾏的sysTid才是)Native:DVM thread Status 正常有这些状态(ZOMBIE, RUNNABLE, TIMED_WAIT, MONITOR, WAIT, INITALIZING,STARTING,NATIVE, VMWAIT, SUSPENDED,UNKNOWN)5,group="main" sCount=1 dsCount=0 obj=0x75720fb8 self=0x7f7e8af800代表 DVM thread status。group:是线程所处的线程组 default is “main”sCount: 线程被正常挂起的次数 1 (thread suspend count)dsCount: 线程因调试⽽挂起次数 0 (thread dbg suspend count)obj: 当前线程所关联的java线程对象 0x75720fb8 (thread obj address)Sef: 该线程本⾝的地址 0x7f7e8af800 (thread point address)6,sysTid=775 nice=-2 cgrp=default sched=0/0 handle=0x7f827d5e58代表Linux thread status显⽰线程调度信息sysTId: linux系统下得本地线程idlinux thread tid
Nice:线程的调度有优先级 linux thread nice value
cgrp: 优先组属 c groupsched: 调度策略 cgroup policy/gourp id
handle: 处理函数地址 handle address7,state=S schedstat=( 225588889412 97525 ) utm=20901 stm=1657 core=3 HZ=100代表CPU Sched stat显⽰更多该线程当前上下⽂State:调度状态 process/thread state (正常有 "R (running)", "S (sleeping)", "D (disk sleep)", "T (stopped)", "t (tracing stop)", "Z(zombie)", "X (dead)", "x (dead)", "K (wakekill)", "W (waking)",),通常⼀般的Process 处于的状态都是S (sleeping), ⽽如果⼀旦发现处于如D (disk sleep), T (stopped), Z (zombie) 等就要认真审查.Schedstat (Run CPU Clock/ns, Wait CPU Clock/ns, Slice times) 该线程运⾏信息utm: utime, user space time 线程⽤户态下使⽤的时间值(单位是jiffies)stm: stime, kernel space time 内核态下得调度时间值core: now running in cpu. 最后运⾏改线程的cup标识8,stack=0x7f7dc93000-0x7f7dc95000 stackSize=1020KB代表堆栈地址区域及size9,held mutexes=代表是否被锁住,正常有四个属性(mutexes: tll=0 tsl=0 tscl=0 ghl=0),0表⽰unlock,其它值都代表被lock,tll: thread List Lock,
tsl: thread Suspend Lock,
tscl: thread Suspend Count Lock
ghl: gc Heap Lock10,剩余的就是⼀些 Call StackCPU Usage追查CPU 利⽤率可⼤体的知道,当时机器是否有Process 在疯狂的运⾏, 当时系统运⾏是否繁忙。通常死机分析,只需要抓取基本的使⽤情况即可。通常使⽤的命令如 toptop 可以简单的查询Cpu 的基本使⽤情况,注意的是top 的CPU% 是按全部CPU 来计算的,如果以单线程来计算,⽐如当时有开启4个核top 可以简单的查询Cpu 的基本使⽤情况,注意的是top 的CPU% 是按全部CPU 来计算的,如果以单线程来计算,⽐如当时有开启4个核⼼,那么最多吃到25%.Memory Usage
我们通常会审查,系统当时memory 是否⾜够, 是否处于low memory 状态, 是否可能出现因⽆法申请到memory ⽽卡死的情况. 常见的⼀些基本信息如下:
* meminfo: basic memory status
adb shell cat proc/meminfo
adb shell cat proc/pid/maps
adb shell cat proc/pid/smaps
* procrank info: all process memory status
adb shell procrank
adb shell procmem pid
adb shell dumpsys meminfo pid
adb shell cat procadb shell cat /proc/buddyinfoStorage Usage查看Storage 的情况,通常主要是查询data 分区是否已经刷满, sdcard 是否已经刷满, 剩余的空间是否⾜够。以及是否有产⽣超⼤⽂件等。 通常使⽤的命令如 df2.5.5 android默认分析⼯具android_、/data/anr/2.6 ⼩结MTK参考设计交付致⼒于较好的⽤户体验,尽管如此,在以下情况下系统可能出现性能问题:1、使⽤不同的硬件元件,如:触摸屏、相机传感器等,以及驱动尚未优化;2、修改默认的应⽤⾏为;3、安装⼀些⾃⼰的应⽤程序或第三⽅的应⽤程序。 可以利⽤现有的性能⼯具来识别问题。出现异常时,可以使⽤top命令、ps命令、renice命令等查看当时的状态和统计信息。Oprofile、Ftrace、Traceview⼯具3、开机速度优化3.1 开机时间评估硬件环境:1、不要拔电池关机;2、定义好开始时间和结束时间,测试3~5次,取平均值。软件环境:1、第三⽅应⽤安装的数量;2、应⽤列表的补丁;其他修改(内核、驱动、系统框架)在优化开机时间之前,需要获取开机每个阶段的时间,然后找到影响速度的瓶颈。影响开机速度的各个阶段如下所⽰: 1、内核初始化时间:⼏乎是固定不变的时间 2、挂载分区的总时间:⼏乎是固定不变的时间,若拔电池再开机的话这个阶段时间会增加 3、Android启动时间:zygote预加载类(⼏乎是固定不变的时间);包扫描(验证每个apk扫描安装的时间);AP初始化时间(取决于安装的apk)3.2 开机时间分析注意:预加载和lk启动时间仅仅在android 4.1版本中启⽤。⽅法:cat /proc/bootprof
3.3 开机时间优化为了优化开机时间,必须要知道哪些进程⽐较耗时。可以通过整理每个进程cpu的时间从系统服务开启到关机动画结束(桌⾯图标呈现出来)。使⽤⽅法:获取⽂件节点的值:/proc/mtprof/cputimecat/proc/mtprof/cputime1、阻⽌不必要的ap应⽤进程启动;(应⽤进程被定义为常驻;通过服务请求、content Provider、⼴播拉起)2、开机动画开机动画⽐较耗时的任务:动画越多cpu占⽤的时间越长;每秒传输的帧数越多,cpu越耗时。3.4 odex预加载配置为了减⼩开机启动时间,可以选择在编译期间把部分APK odex 打包到.在⾸次启动时不需要进⾏APK odex 优化。4、应⽤启动速度优化MTK⼯具:系统层次(内核—ftrace、cpu占⽤时间)、AP层次(traceview) GAT tools.4.1 应⽤启动⽅式通常来说,在安卓中应⽤的启动⽅式分为两种:冷启动和热启动。1、冷启动:当启动应⽤时,后台没有该应⽤的进程,这时系统会重新创建⼀个新的进程分配给该应⽤,这个启动⽅式就是冷启动。2、热启动:当启动应⽤时,后台已有该应⽤的进程(例:按back键、home键,应⽤虽然会退出,但是该应⽤的进程是依然会保留在后台,可进⼊任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应⽤,这个⽅式叫热启动。特点1、冷启动:冷启动因为系统会重新创建⼀个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括⼀系列的测量、布局、绘制),最后显⽰在界⾯上。2、热启动:热启动因为会从已有的进程中来启动,所以热启动就不会⾛Application这步了,⽽是直接⾛MainActivity(包括⼀系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化⼀个MainActivity就⾏了,⽽不必创建和初始化Application,因为⼀个应⽤从新进程的创建到进程的销毁,Application只会初始化⼀次。上⾯说的启动是点击app的启动图标来启动的,⽽另外⼀种⽅式是进⼊最近使⽤的列表界⾯来启动应⽤,这种不应该叫启动,应该叫恢复。4.2 应⽤启动流程在安卓系统上,应⽤在没有进程的情况下,应⽤的启动都是这样⼀个流程:当点击app的启动图标时,安卓系统会从Zygote进程中fork创建出⼀个新的进程分配给该应⽤,之后会依次创建和初始化Application类、创建MainActivity类、加载主题样式Theme中的windowBackground等属性设置给MainActivity以及配置Activity层级上的⼀些属性、再inflate布局、当onCreate/onStart/onResume⽅法都⾛完了后最后才进⾏contentView的measure/layout/draw显⽰在界⾯上,所以直到这⾥,应⽤的第⼀次启动才算完成,这时候我们看到的界⾯也就是所说的第⼀帧。所以,总结⼀下,应⽤的启动流程如下:Application的构造器⽅法——>attachBaseContext()——>onCreate()——>Activity的构造⽅法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显⽰在界⾯上。测量应⽤启动的时间在上⾯这个启动流程中,任何⼀个地⽅有耗时操作都会拖慢我们应⽤的启动速度,⽽应⽤启动时间是⽤毫秒度量的,对于毫秒级别的快慢度量我们还是需要去精确的测量到到底应⽤启动花了多少时间,⽽根据这个时间来做衡量。什么才是应⽤的启动时间从点击应⽤的启动图标开始创建出⼀个新的进程直到我们看到了界⾯的第⼀帧,这段时间就是应⽤的启动时间。我们要测量的也就是这段时间,测量这段时间可以通过adb shell命令的⽅式进⾏测量,这种⽅法测量的最为精确,命令为: 1、ThisTime:⼀般和TotalTime时间⼀样,除⾮在应⽤启动时开了⼀个透明的Activity预先处理⼀些事再显⽰出主Activity,这样将⽐TotalTime⼩。 2、TotalTime:应⽤的启动时间,包括创建进程+Application初始化+Activity初始化到界⾯显⽰。 3、WaitTime:⼀般⽐TotalTime⼤点,包括系统影响的耗时。下⾯是测量⼀个应⽤冷启动和热启动的时间: 可以看到在进程已经存在的情况下,只需要重新初始化MainActivity,这样的启动⽐较快,不过⼤多数情况下应⽤的启动都是冷启动,因为⽤户都会在任务列表中⼿动关闭遗留的应⽤进程。4.3减少应⽤启动时的耗时针对冷启动时候的⼀些耗时,如上测得这个应⽤算是中型的app,在冷启动的时候耗时已经快700ms了,如果项⽬再⼤点在Application中配置了更多的初始化操作,这样将可能达到1s,这样每次启动都明显感觉延迟,所以在进⾏应⽤初始化的时候采取以下策略: 1、在Application的构造器⽅法、attachBaseContext()、onCreate()⽅法中不要进⾏耗时操作的初始化,⼀些数据预取放在异步线程中,可以采取Callable实现。 2、对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反⽽会延迟应⽤的启动速度,对于这个还是需要放在异步线程中处理。 3、对于MainActivity,由于在获取到第⼀帧前,需要对contentView进⾏测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume⽅法中避免做耗时操作。遵循上⾯三种策略可明显提⾼app启动速度。4.4优化应⽤启动时的体验对于应⽤的启动时间,只能是尽量的避免⼀些耗时的、⾮必要的操作在主线程中,这样相对可以缩减⼀部分启动的耗时,另外⼀⽅⾯在等待第⼀帧显⽰的时间⾥,可以加⼊⼀些配置以增加体验,⽐如加⼊Activity的background,这个背景会在显⽰第⼀帧前提前显⽰在界⾯上。 1、先为主界⾯单独写⼀个主题style,设置⼀张待显⽰的图⽚,这⾥我设置了⼀个颜⾊,然后在manifest中设置给MainActivity:
发布者:admin,转转请注明出处:http://www.yc00.com/news/1687955210a60595.html
评论列表(0条)