Android优化_Aron
2019-07-12 10:01:03 1 举报
AI智能生成
优化建议
作者其他创作
大纲/内容
调试
耗时
共享参数
3次写入 平均每次11.8ms
android.app.SharedPreferencesImpl$EditorImpl.commit 3 34.133ms
18次 212.5ms
日期格式化
3次时间格式化 平均每次16.9ms
java.text.DateFormat.format 3 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();
// 获取当前线程的堆栈
for (StackTraceElement i : Thread.currentThread().getStackTrace()) {
Log.i(TAG, i.toString());
}
for (StackTraceElement i : Thread.currentThread().getStackTrace()) {
Log.i(TAG, i.toString());
}
new Exception("this is a log").printStackTrace();
RuntimeException re = new RuntimeException();
re.fillInStackTrace();
Log.i(TAG, "stackTrace", re);
re.fillInStackTrace();
Log.i(TAG, "stackTrace", re);
// 调试打印堆栈而不退出
Log.d(TAG, Log.getStackTraceString(new Throwable()));
Log.d(TAG, Log.getStackTraceString(new Throwable()));
ANR
简介trace.txt
//开头显示进程号、ANR发生的时间点和进程名称
----- pid 21786 at 2016-01-01 09:47:34 -----
Cmd line: com.example.androidtest
----- pid 21786 at 2016-01-01 09:47:34 -----
Cmd line: com.example.androidtest
"main" prio=5 tid=1 Sleeping
| group="main" sCount=1 dsCount=0 obj=0x7285da50 self=0xb47f6a00
//sysTid是线程号,主线程的线程号和进程号相同
| sysTid=21786 nice=0 cgrp=bg_non_interactive sched=0/0
| group="main" sCount=1 dsCount=0 obj=0x7285da50 self=0xb47f6a00
//sysTid是线程号,主线程的线程号和进程号相同
| sysTid=21786 nice=0 cgrp=bg_non_interactive sched=0/0
//JDWP线程是支持虚拟机调试的线程,不需要关心
"JDWP" daemon prio=5 tid=4 WaitingInMainDebuggerLoop
"JDWP" daemon prio=5 tid=4 WaitingInMainDebuggerLoop
/线程名称后面标识有daemon,说明这是个守护线程
"HeapTaskDaemon" daemon prio=5 tid=5 Blocked
"HeapTaskDaemon" daemon prio=5 tid=5 Blocked
| held mutexes=
at java.lang.Thread.sleep!(Native method)
- sleeping on <0x02b0b44c> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:1031)
- locked <0x02b0b44c> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:985)
at java.lang.Thread.sleep!(Native method)
- sleeping on <0x02b0b44c> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:1031)
- locked <0x02b0b44c> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:985)
线程状态
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
计算表达式
计算表达式
Alt+F8
修改变量的值
可快速调试一些其他情况
输入key=value后,按Ctrl+Enter确定
Esc 关闭窗口
处理混淆后的log
该工具位于 <android-sdk>/tools/proguard/bin/ 目录下。
里面的 proguardgui.bat 为 GUI 工具,
里面的 proguardgui.bat 为 GUI 工具,
1) 运行 proguardgui.bat
2) 从左边的菜单选择 “ReTrace”
3) 在上面的 mapping 文件中选择你的 mapping 文件 ,在下面输入框输入要还原的代码
4) 点击 “ReTrace!” 按钮
2) 从左边的菜单选择 “ReTrace”
3) 在上面的 mapping 文件中选择你的 mapping 文件 ,在下面输入框输入要还原的代码
4) 点击 “ReTrace!” 按钮
输入
at eink.plugin.mediacontrol.f$1.handleMessage(SourceFile:114)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
结果
at eink.plugin.mediacontrol.global.GlobalPlayerController$1.void handleMessage(android.os.Message)(SourceFile:114)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
retrace.bat 为命令行工具, 把 mapping 文件和 要还原的堆栈信息保存在 stacktrace 文件中,
然后把这两个文件复制到 retrace.bat 目录下,运行如下命令即可。
retrace.bat -verbose mapping.txt stacktrace.txt > out.txt
这里就对ProGuard 的一个小功能简单的介绍下。方便自己和大家的学习。
然后把这两个文件复制到 retrace.bat 目录下,运行如下命令即可。
retrace.bat -verbose mapping.txt stacktrace.txt > out.txt
这里就对ProGuard 的一个小功能简单的介绍下。方便自己和大家的学习。
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(算数异常)属于这种信号。
2、进程调用的库发现错误,给自己发送中止信号,默认情况下,该信号会终止进程。在本文中,SIGABRT(中止进程)属于这种信号
2、进程调用的库发现错误,给自己发送中止信号,默认情况下,该信号会终止进程。在本文中,SIGABRT(中止进程)属于这种信号
AEE DB 和 Coredump
log中的core-dump
超时5秒机制
关键字
startTime
kDefaultTimeOutMs
TimeOut
位置
cat /proc/sys/kernel/core_pattern
cat /proc/sys/kernel/core_pattern
/data/corefile/core-%e-%p@%t
/data/corefile/core-%e-%p@%t
/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出来,先查看下汇编代码:
adb shell oatdump --oat-file= /system/framework/arm64/boot.oat > oatdump_boot.txt
adb shell oatdump --oat-file= /system/framework/arm64/boot-framework.oat > oatdump_boot-framework.txt
adb shell oatdump --oat-file= /system/framework/arm64/boot.oat > oatdump_boot.txt
adb shell oatdump --oat-file= /system/framework/arm64/boot-framework.oat > oatdump_boot-framework.txt
网络优化
磁盘优化
IO读写优化
数据库频繁读写可以视情况改成 事务处理
beginTransaction
setTransactionSuccessful()
endTransaction()
Serializable是Java中的序列化接口,其使用起来简单但是开销很大,在序列化和反序列化过程中需要大量的I/O操作。而Parcelable是Android中的序列化方式,因此更适合用在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高。
内存优化
代码获取内存
@TargetApi(Build.VERSION_CODES.KITKAT)
public static int getMemory() {
Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memoryInfo);
// dalvikPrivateClean + nativePrivateClean + otherPrivateClean;
int totalPrivateClean = memoryInfo.getTotalPrivateClean();
// dalvikPrivateDirty + nativePrivateDirty + otherPrivateDirty;
int totalPrivateDirty = memoryInfo.getTotalPrivateDirty();
// dalvikPss + nativePss + otherPss;
int totalPss = memoryInfo.getTotalPss();
// dalvikSharedClean + nativeSharedClean + otherSharedClean;
int totalSharedClean = memoryInfo.getTotalSharedClean();
// dalvikSharedDirty + nativeSharedDirty + otherSharedDirty;
int totalSharedDirty = memoryInfo.getTotalSharedDirty();
// dalvikSwappablePss + nativeSwappablePss + otherSwappablePss;
int totalSwappablePss = memoryInfo.getTotalSwappablePss();
int total = totalPrivateClean + totalPrivateDirty + totalPss + totalSharedClean + totalSharedDirty + totalSwappablePss;
return total ;
}
图片大小优化
图片资源尝试放在mipmap中,会做优化处理
BitmapFactory优化
内存分析工具
MAT
MemoryAnalysisTool
内存泄漏 LeakMemory
leakcanary
leakcanary
build.gradle中配置
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.2'
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.2'
Project Structure -- app -- Dependencies 点击加号搜索
leakcanary-android
Application的onCreate中调用
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
内存问题分析
粗略判断是否存在内存泄漏
工具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
VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS- Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS- Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS- Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS
案例分析
GC问题 Object.finalize
常见泄漏
Handler持有Context或Activity等不同生命周期的对象
弱引用, 生命周期结束时调用 mHandler.removeCallbacksAndMessages(null);
资源性流 未关闭
注册 忘记反注册
WebView
而外开启一个独立进程.利用AIDL等方式进程间通信,结束后杀死独立进程
android:process
不正确的单例模式 传入Activity的Context
参数传的APP的context
匿名类和非静态内部类
匿名对象生成引用,及时回收; 改成静态内部类
性能/CPU优化
性能分析工具
Android官方
StrictMode
严格模式
- 主要用来做主线程优化分析
代码中APP或Activity中onCreate中加入
最好加个判断区分Debug模式
最好加个判断区分Debug模式
if (BuildConfig.DEBUG) {
// 针对线程的相关策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
// 针对VM的相关策略
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
if (true) {
// if (BuildConfig.DEBUG) {
// 针对线程的相关策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
// 针对VM的相关策略
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
开发者选项开启严格模式
Systrace
使用
4.3以上版本
cd android-sdk/platform-tools/systrace
python systrace.py --time=10 -o mynewtrace.html sched gfx view wm
4.2及以下
python systrace.py --set-tags=gfx,view,wm
adb shell stop
adb shell start
停止和重启
Hierarchy Viewer
AndroidSDK发布的工具,位置在tools文件夹下,名为hierarchyviewer.bat
TraceView
CPU线程分析
AS Monitor --> CPU
即TraceView
即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()方法占用的时间。
Prepare:表示程序准备时间
Process:表示渲染引擎执行显示列表所花的时间,view越多,时间就越长
Execute:表示把一帧数据发送到屏幕上排版显示实际花费的时间。
Draw + Prepare+Process + Execute = 完整显示一帧 ,这个时间要小于16ms才能保存每秒60帧
Prepare:表示程序准备时间
Process:表示渲染引擎执行显示列表所花的时间,view越多,时间就越长
Execute:表示把一帧数据发送到屏幕上排版显示实际花费的时间。
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
λ adb shell am start -W com.android.jv.ink.launcherink/.ui.home.JvMainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.jv.ink.launcherink/.ui.home.JvMainActivity }
Status: ok
Activity: com.android.jv.ink.launcherink/.ui.home.JvMainActivity
ThisTime: 749
TotalTime: 749
WaitTime: 791
Complete
λ asdb shell am start -W com.android.jv.ink.launcherink/.ui.home.JvMainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.jv.ink.launcherink/.ui.home.JvMainActivity }
Status: ok
Activity: com.android.jv.ink.launcherink/.ui.home.JvMainActivity
ThisTime: 642
TotalTime: 642
WaitTime: 680
Complete
log中筛选Displayed,级别I
01-08 03:23:08.457 1671-1813/system_process I/ActivityManager: Displayed com.android.jv.ink.launcherink/.ui.home.JvMainActivity: +642ms
01-08 03:23:28.116 1671-1813/system_process I/ActivityManager: Displayed com.android.jv.ink.launcherink/.ui.edit.JvEditActivity: +187ms
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线程的任务一直不执行完呢?会有这情况?
举个🌰,Activity首页顶部有个滚动的Banner,banner的滚动是通过不断增加延迟Runnable实现。那么,初始化任务就可能一直没法执行。
举个🌰,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
onCreate函数中添加
android.os.debug.startMethodTracing("Entertainment");
onDestrory函数中添加
android.os.debug.stopMethodTracing();
onCreate函数中添加
android.os.debug.startMethodTracing("Entertainment");
onDestrory函数中添加
android.os.debug.stopMethodTracing();
数据库DB
线程优化
线程池
ExecutorService
ExecutorService
开单个子线程,按顺序执行命令
Android中数据不多时表查询可能耗时不多,不会导致anr,
不过大于100ms时同样会让用户感觉到延时和卡顿,可以放在线程中运行,
但sqlite在并发方面存在局限,多线程控制较麻烦,这时候可使用单线程池,
在任务中执行db操作,通过handler返回结果和ui线程交互,
既不会影响UI线程,同时也能防止并发带来的异常。
不过大于100ms时同样会让用户感觉到延时和卡顿,可以放在线程中运行,
但sqlite在并发方面存在局限,多线程控制较麻烦,这时候可使用单线程池,
在任务中执行db操作,通过handler返回结果和ui线程交互,
既不会影响UI线程,同时也能防止并发带来的异常。
流畅度
布局优化
merge
3.<merge>标签的限制
小白: <merge />标签有什么限制没?
小黑: <merge />只能作为XML布局的根标签使用。当Inflate以<merge />开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true。
小白: <merge />标签有什么限制没?
小黑: <merge />只能作为XML布局的根标签使用。当Inflate以<merge />开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true。
<merge/>多用于替换frameLayout或者当一个布局包含另一个布局的时候,<merge/>标签用于消除师徒层次结构中多余的视图组。
一般配合<include/>使用,当把有<merge>标签的布局放在<include>中的时候,就会忽视<merge>
一般配合<include/>使用,当把有<merge>标签的布局放在<include>中的时候,就会忽视<merge>
代码优化
AS代码分析检查
左上角菜单中Analyze-Inspect code
编译优化
组件化
if (isDebug.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
子模块在debug模式下单独作为APP运行
if(isDebug.toBoolean()) {
manifest.srcFile 'src/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/release/AndroidManifest.xml'
}
manifest.srcFile 'src/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/release/AndroidManifest.xml'
}
资源包大小优化
0 条评论
下一页