Android优化_Aron
2019-07-12 10:01:03 1 举报
AI智能生成
优化建议
作者其他创作
大纲/内容
调试
耗时
共享参数
3次写入 平均每次11.8ms
android.app.SharedPreferencesImpl$EditorImpl.commit<span> </span>3<span> </span>34.133ms
18次 212.5ms
日期格式化
3次时间格式化 平均每次16.9ms
java.text.DateFormat.format<span> </span>3<span> </span>48.366ms
18次 305.8ms
读文件大小
file.length
大约1ms
调试Debug
logcat定位错误
关键字
Caused by
fatal
FATAL
Fatal
ANR
广播
BroadcastQueue: Timeout
SCREEN_OFF
包名
jv.ink.launcherink
反向过滤log
^(?!.*(http|GET)).*$
过滤掉http和GET
打印log
打印方法调用堆栈
Thread.currentThread().getStackTrace();
// 获取当前线程的堆栈<br> for (StackTraceElement i : Thread.currentThread().getStackTrace()) {<br> Log.i(TAG, i.toString());<br> }
<div>new Exception("this is a log").printStackTrace();</div>
RuntimeException re = new RuntimeException();<br> re.fillInStackTrace();<br> Log.i(TAG, "stackTrace", re);
// 调试打印堆栈而不退出<br> Log.d(TAG, Log.getStackTraceString(new Throwable()));
ANR
简介trace.txt
//开头显示进程号、ANR发生的时间点和进程名称<br>----- pid 21786 at 2016-01-01 09:47:34 -----<br>Cmd line: com.example.androidtest <br>
"main" prio=5 tid=1 Sleeping<br> | group="main" sCount=1 dsCount=0 obj=0x7285da50 self=0xb47f6a00<br>//sysTid是线程号,主线程的线程号和进程号相同<br> | sysTid=21786 nice=0 cgrp=bg_non_interactive sched=0/0
//JDWP线程是支持虚拟机调试的线程,不需要关心<br>"JDWP" daemon prio=5 tid=4 WaitingInMainDebuggerLoop <br>
/线程名称后面标识有daemon,说明这是个守护线程<br>"HeapTaskDaemon" daemon prio=5 tid=5 Blocked <br>
| held mutexes=<br> at java.lang.Thread.sleep!(Native method)<br> - sleeping on <0x02b0b44c> (a java.lang.Object)<br> at java.lang.Thread.sleep(Thread.java:1031)<br> - locked <0x02b0b44c> (a java.lang.Object)<br> at java.lang.Thread.sleep(Thread.java:985) <br>
线程状态
anr/trace文件错误定位
ANR关键字
held by thread
查找Block
held by tid
cmd line
查看所有的进程
Blocked
locked, sleeping, held,
sleeping on
waiting on
event log中检索 am_anr 关键字
原因
CPU使用过高、事件没有得到及时的响应、死锁等
UI 5秒未响应, 广播 10s 服务20s
CPU使用过高、事件没有得到及时的响应、死锁等
UI 5秒未响应, 广播 10s 服务20s
AS的debug
Evaluate Expression<br>计算表达式<br>
Alt+F8
修改变量的值
可快速调试一些其他情况
输入key=value后,按Ctrl+Enter确定
Esc 关闭窗口
处理混淆后的log
该工具位于 <android-sdk>/tools/proguard/bin/ 目录下。<br>里面的 proguardgui.bat 为 GUI 工具,
1) 运行 proguardgui.bat<br>2) 从左边的菜单选择 “ReTrace”<br>3) 在上面的 mapping 文件中选择你的 mapping 文件 ,在下面输入框输入要还原的代码<br>4) 点击 “ReTrace!” 按钮
输入
at eink.plugin.mediacontrol.f$1.handleMessage(SourceFile:114)<br>at android.os.Handler.dispatchMessage(Handler.java:102)<br>at android.os.Looper.loop(Looper.java:136)<br>at android.app.ActivityThread.main(ActivityThread.java:5017)
结果
at eink.plugin.mediacontrol.global.GlobalPlayerController$1.void handleMessage(android.os.Message)(SourceFile:114)<br>at android.os.Handler.dispatchMessage(Handler.java:102)<br>at android.os.Looper.loop(Looper.java:136)<br>at android.app.ActivityThread.main(ActivityThread.java:5017)
retrace.bat 为命令行工具, 把 mapping 文件和 要还原的堆栈信息保存在 stacktrace 文件中,<br>然后把这两个文件复制到 retrace.bat 目录下,运行如下命令即可。<br>retrace.bat -verbose mapping.txt stacktrace.txt > out.txt<br> 这里就对ProGuard 的一个小功能简单的介绍下。方便自己和大家的学习。<br>
retrace.bat -verbose mapping.txt stacktrace.txt > out.txt
Native (NE)
原始的linux,对于用户进程崩溃之后,处理方式有2种:直接终止进程;输出coredump再终止进程。 而在Android,为了方便调试,在收到崩溃信号后,会先输出tombstone,然后在根据设置是否抓取coredump,最后再终止进程
mtk平台上会在这基础上将coredump及其他关键信息打包成一个db文件,位于mtklog下的aee_exp中,db文件的生成前提条件是eng版本或是user版本打开了mtklog
Native Excption
SIGSEGV(段错误),SIGBUS(内存访问错误),SIGFPE(算数异常)属于这种信号。<br>2、进程调用的库发现错误,给自己发送中止信号,默认情况下,该信号会终止进程。在本文中,SIGABRT(中止进程)属于这种信号
AEE DB 和 Coredump
log中的core-dump
超时5秒机制
关键字
startTime
kDefaultTimeOutMs
TimeOut
位置
cat /proc/sys/kernel/core_pattern
cat /proc/sys/kernel/core_pattern<br>/data/corefile/core-%e-%p@%t <br>
/sbin/sysctl kernel.core_pattern
AndroidLog
关键字
signal
SIGSEGV
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xfc430e78 in tid 3750 (dex2oat), pid 3750 (dex2oat)
疑似地址映射错误
典型的多线程引起的问题
SIGABRT
Fatal signal 6 (SIGABRT), code -6 (SI_TKILL) in tid 27037 (Binder:26386_4), pid 26386 (droid.launcher3)
/system/bin/tombstoned: received crash request for pid 15049
/system/bin/tombstoned: Tombstone written to: /data/tombstones/tombstone_07
backtrace
如果crash发生在oat文件里面,所以用下面的命令把oat文件dump出来,先查看下汇编代码:<br>adb shell oatdump --oat-file= /system/framework/arm64/boot.oat > oatdump_boot.txt<br>adb shell oatdump --oat-file= /system/framework/arm64/boot-framework.oat > oatdump_boot-framework.txt <br>
网络优化
磁盘优化
IO读写优化
数据库频繁读写可以视情况改成 事务处理
beginTransaction
setTransactionSuccessful()
endTransaction()
Serializable是Java中的序列化接口,其使用起来简单但是开销很大,在序列化和反序列化过程中需要大量的I/O操作。而Parcelable是Android中的序列化方式,因此更适合用在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高。
内存优化
代码获取内存
<div>@TargetApi(Build.VERSION_CODES.KITKAT) </div><div>public static int getMemory() { </div><div> Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo(); </div><div> Debug.getMemoryInfo(memoryInfo); </div><div> // dalvikPrivateClean + nativePrivateClean + otherPrivateClean; </div><div> int totalPrivateClean = memoryInfo.getTotalPrivateClean(); </div><div> // dalvikPrivateDirty + nativePrivateDirty + otherPrivateDirty; </div><div> int totalPrivateDirty = memoryInfo.getTotalPrivateDirty(); </div><div> // dalvikPss + nativePss + otherPss; </div><div> int totalPss = memoryInfo.getTotalPss(); </div><div> // dalvikSharedClean + nativeSharedClean + otherSharedClean; </div><div> int totalSharedClean = memoryInfo.getTotalSharedClean(); </div><div> // dalvikSharedDirty + nativeSharedDirty + otherSharedDirty; </div><div> int totalSharedDirty = memoryInfo.getTotalSharedDirty(); </div><div> // dalvikSwappablePss + nativeSwappablePss + otherSwappablePss; </div><div> int totalSwappablePss = memoryInfo.getTotalSwappablePss(); </div><div> </div><div> int total = totalPrivateClean + totalPrivateDirty + totalPss + totalSharedClean + totalSharedDirty + totalSwappablePss; </div><div> return total ; </div><div>} </div>
图片大小优化
图片资源尝试放在mipmap中,会做优化处理
BitmapFactory优化
内存分析工具
MAT
MemoryAnalysisTool
内存泄漏 LeakMemory<br>leakcanary
build.gradle中配置<br>debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.2'<br> releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.2'
Project Structure -- app -- Dependencies 点击加号搜索
leakcanary-android
Application的onCreate中调用
if (LeakCanary.isInAnalyzerProcess(this)) {<br> // This process is dedicated to LeakCanary for heap analysis.<br> // You should not init your app in this process.<br> return;<br> }<br> LeakCanary.install(this);<br>
内存问题分析
粗略判断是否存在内存泄漏
工具Android Studio
先打开应用,随便点点,然后退出
Android Monitor --> 左侧System Information --> Memory Usage
查看Objects
log关键字
lowmemorykiller: Error opening /proc/8295/oom_score_adj; errno=2
内存占用
adb shell
procrank
可以查看 分应用
adb shell procrank
<div>VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)</div><div>RSS- Resident Set Size 实际使用物理内存(包含共享库占用的内存)</div><div>PSS- Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)</div><div>USS- Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)</div><div>一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS</div>
案例分析
GC问题 Object.finalize
常见泄漏
Handler持有Context或Activity等不同生命周期的对象
弱引用, 生命周期结束时调用 mHandler.removeCallbacksAndMessages(null);
资源性流 未关闭
注册 忘记反注册
WebView
而外开启一个独立进程.利用AIDL等方式进程间通信,结束后杀死独立进程
android:process
不正确的单例模式 传入Activity的Context
参数传的APP的context
匿名类和非静态内部类
匿名对象生成引用,及时回收; 改成静态内部类
性能/CPU优化
性能分析工具
Android官方
StrictMode<br><br>
严格模式<br><ul><li>主要用来做主线程优化分析</li></ul>
代码中APP或Activity中onCreate中加入<br>最好加个判断区分Debug模式
<div>if (BuildConfig.DEBUG) {</div><div> // 针对线程的相关策略</div><div> StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()</div><div> .detectDiskReads()</div><div> .detectDiskWrites()</div><div> .detectNetwork() // or .detectAll() for all detectable problems</div><div> .penaltyLog()</div><div> .build());</div><div><br></div><div> // 针对VM的相关策略</div><div> StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()</div><div> .detectLeakedSqlLiteObjects()</div><div> .detectLeakedClosableObjects()</div><div> .penaltyLog()</div><div> .penaltyDeath()</div><div> .build());</div><div> }</div>
<div> if (true) {</div><div>// if (BuildConfig.DEBUG) {</div><div> // 针对线程的相关策略</div><div> StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()</div><div> .detectDiskReads()</div><div> .detectDiskWrites()</div><div> .detectNetwork() // or .detectAll() for all detectable problems</div><div> .penaltyLog()</div><div> .build());</div><div><br></div><div> // 针对VM的相关策略</div><div> StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()</div><div> .detectLeakedSqlLiteObjects()</div><div> .detectLeakedClosableObjects()</div><div> .penaltyLog()</div><div> .penaltyDeath()</div><div> .build());</div><div> }</div><div><br></div>
开发者选项开启严格模式
Systrace
使用
4.3以上版本
<div>cd android-sdk/platform-tools/systrace</div>
python systrace.py --time=10 -o mynewtrace.html sched gfx view wm
4.2及以下
python systrace.py --set-tags=gfx,view,wm
<div>adb shell stop</div><div>adb shell start</div>
停止和重启
Hierarchy Viewer
AndroidSDK发布的工具,位置在tools文件夹下,名为hierarchyviewer.bat
TraceView
CPU线程分析
AS Monitor --> CPU<br>即TraceView
Start Methods Tracing
APP内执行一些操作
Stop
生成trace文件,分析线程耗时
adb shell
top -m 10 -s cpu
(-m显示最大数量,-s 按指定行排序)
界面卡顿
FPS
adb shell dumpsys gfxinfo <package | pid>
前提:开发者选项=>GPU呈现模式分析确保打开=>在adb shell dumpsys gfxinfo中or 在屏幕上显示为线型图
adb shell dumpsys gfxinfo com.android.launcher3
dumpsys gfxinfo com.android.launcher3
Draw: 表示在Java中创建显示列表部分中,OnDraw()方法占用的时间。<br>Prepare:表示程序准备时间<br>Process:表示渲染引擎执行显示列表所花的时间,view越多,时间就越长<br>Execute:表示把一帧数据发送到屏幕上排版显示实际花费的时间。<br>Draw + Prepare+Process + Execute = 完整显示一帧 ,这个时间要小于16ms才能保存每秒60帧
可绘制出折线图和柱状图
文件读取优化
案例1:读取json文件264kb,转成5196个对象,测试耗时1.5s
ByteArrayOutputStream,buffer=4096个字节
JSONArray和JSONObject
耗时1.3s
改成json文件193kb,转成1300个对象(对象解析8个字段)
耗时1.18s
改成解析2个字段
耗时优化不明显
改成gson或者fastJson
启动优化
计算启动时间
adb shell am force-stop com.android.jv.ink.launcherink
am force-stop com.android.launcher3
am force-stop com.transsion.hilauncher
adb shell am start -W com.coolyota.logreport/com.coolyota.logreport.LogSettingActivity
am start -W com.android.launcher3/.Launcher
am start -W -p com.android.launcher3 -c android.intent.category.HOME
am start -W -p com.transsion.hilauncher -c android.intent.category.HOME
adb shell am start -W com.android.jv.ink.launcherink/.ui.home.JvMainActivity
背屏冷启动
优化前
1217ms
1138ms
<div>λ adb shell am start -W com.android.jv.ink.launcherink/.ui.home.JvMainActivity</div><div>Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.jv.ink.launcherink/.ui.home.JvMainActivity }</div><div>Status: ok</div><div>Activity: com.android.jv.ink.launcherink/.ui.home.JvMainActivity</div><div>ThisTime: 749</div><div>TotalTime: 749</div><div>WaitTime: 791</div><div>Complete</div>
<div>λ asdb shell am start -W com.android.jv.ink.launcherink/.ui.home.JvMainActivity</div><div>Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.jv.ink.launcherink/.ui.home.JvMainActivity }</div><div>Status: ok</div><div>Activity: com.android.jv.ink.launcherink/.ui.home.JvMainActivity</div><div>ThisTime: 642</div><div>TotalTime: 642</div><div>WaitTime: 680</div><div>Complete</div>
log中筛选Displayed,级别I
<div>01-08 03:23:08.457 1671-1813/system_process I/ActivityManager: Displayed com.android.jv.ink.launcherink/.ui.home.JvMainActivity: +642ms</div><div>01-08 03:23:28.116 1671-1813/system_process I/ActivityManager: Displayed com.android.jv.ink.launcherink/.ui.edit.JvEditActivity: +187ms</div>
Displayed
app启动时间
adb shell am start -W packagename/MainActivity命令,计算启动时间
adb pull sdcard/BLauncher.mp4
logcat 过滤 Displayed
Displayed
录制查看帧
adb shell screenrecord --bugreport /sdcard/BLauncher.mp4
用KMP逐帧播放
mac上quicktime
延时调用 / 子线程调用 / 空闲时调用耗时方式
空闲时调用
将onCreate()中的耗时操作放到Idle中
但有个问题,如果UI线程的任务一直不执行完呢?会有这情况?<br><br>举个🌰,Activity首页顶部有个滚动的Banner,banner的滚动是通过不断增加延迟Runnable实现。那么,初始化任务就可能一直没法执行。
延时调用
弊端
不同手机性能时间不确定,体验不一定好
UI绘制结束时调用
onWindowFocusChanged()
至于为什么要在onWindowFocusChanged()再通过Handler.post()延后一个任务,一开始我是通过打点,发现没post()时,onWindowFocusChanged()打点在Log“Displayed”之前,增加post()便在Log“Displayed”之后,梳理了下调用流程,大概是渲染调用requestLayout()也是增加任务监听,只有SurfaceFlinger渲染信号回来时才会触发渲染,因此延后一个任务,刚好在其之后
View.post(Runnable runnable)
需要注意的是,该方案只有在onResume()或之前调用有效。
View内部维护了一个HandlerActionQueue,我们可以在DecorView attachToWindow前,通过View.post()将任务Runnables存放到HandlerActionQueue中。
设置全屏无标题主题提升体验
Activity
主要使用Traceview、monkey、monkey runner调试,traceview类似java web调优的visualvm,使用方法如下:在需要调优的activity <br>onCreate函数中添加<br>android.os.debug.startMethodTracing("Entertainment");<br>onDestrory函数中添加<br>android.os.debug.stopMethodTracing();
数据库DB
线程优化
线程池<br>ExecutorService
开单个子线程,按顺序执行命令
Android中数据不多时表查询可能耗时不多,不会导致anr,<br>不过大于100ms时同样会让用户感觉到延时和卡顿,可以放在线程中运行,<br>但sqlite在并发方面存在局限,多线程控制较麻烦,这时候可使用单线程池,<br>在任务中执行db操作,通过handler返回结果和ui线程交互,<br>既不会影响UI线程,同时也能防止并发带来的异常。
流畅度
布局优化
merge
3.<merge>标签的限制<br>小白: <merge />标签有什么限制没?<br>小黑: <merge />只能作为XML布局的根标签使用。当Inflate以<merge />开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true。
<merge/>多用于替换frameLayout或者当一个布局包含另一个布局的时候,<merge/>标签用于消除师徒层次结构中多余的视图组。<br>一般配合<include/>使用,当把有<merge>标签的布局放在<include>中的时候,就会忽视<merge>
代码优化
AS代码分析检查
左上角菜单中Analyze-Inspect code
编译优化
组件化
if (isDebug.toBoolean()) {<br> apply plugin: 'com.android.application'<br>} else {<br> apply plugin: 'com.android.library'<br>}
子模块在debug模式下单独作为APP运行
if(isDebug.toBoolean()) {<br> manifest.srcFile 'src/debug/AndroidManifest.xml'<br> } else {<br> manifest.srcFile 'src/release/AndroidManifest.xml'<br> }
资源包大小优化
0 条评论
下一页
为你推荐
查看更多