性能优化
1.布局优化
2.绘制优化
3.内存泄漏优化
4.响应速度优化
5.ListView/RecycleView及Bitmap优化
6.线程优化
7.其他性能优化的建议
App优化
1、App启动优化
因为这个App集成了Bugly, Push, Feedback等服务, 所以Application的onCreate有很多第三方平台的初始化工作...
public class GithubApplication extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
// init logger.
AppLog.init();
// init crash helper
CrashHelper.init(this);
// init Push
PushPlatform.init(this);
// init Feedback
FeedbackPlatform.init(this);
// init Share
SharePlatform.init(this);
// init Drawer image loader
DrawerImageLoader.init(new AbstractDrawerImageLoader() {
@Override
public void set(ImageView imageView, Uri uri, Drawable placeholder) {
ImageLoader.loadWithCircle(GithubApplication.this, uri, imageView);
}
});
}
}
Traceview上场
接下来我们结合我们上文的理论知识, 和介绍的Traceview工具, 来分析下Application的onCreate耗时.
在onCreate开始和结尾打上trace.
Debug.startMethodTracing("GithubApp");
...
Debug.stopMethodTracing();
运行程序, 会在sdcard上生成一个"GithubApp.trace"的文件.
注意: 需要给程序加上写存储的权限:
通过adb pull将其导出到本地
adb pull /sdcard/GithubApp.trace ~/temp
在下方的方法区点击"Real Time/Call", 按照方法每次调用耗时降序排.
耗时超过500ms都是值得注意的.
看左边的方法名, 可以看到耗时大户就是我们用的几大平台的初始化方法, 特别是Bugly, 还加载native的lib, 用ZipFile操作等.
点击每个方法, 可以看到其父方法(调用它的)和它的所有子方法(它调用的).
点击方法时, 上方的该方法执行时间轴会闪动, 可以看该方法的执行线程及相对时长.
3, 调整Application onCreate再试
既然已经知道了哪些地方耗时长, 我们不妨调整下Application的onCreate实现, 一般来说我们可以将这些初始化放在一个单独的线程中处理, 为了方便今后管理, 这里我用了一个InitializeService的IntentService来做初始化工作.
明确一点, IntentService不同于Service, 它是工作在后台线程的.
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) {
performInit();
}
}
}
2、布局优化
查看是否有过度绘制
1、尽量减少布局层级和复杂度
尽量不要嵌套使用RelativeLayout.
尽量不要在嵌套的LinearLayout中都使用weight属性.
Layout的选择, 以尽量减少View树的层级为主.
去除不必要的父布局.
善用TextView的Drawable减少布局层级
如果H Viewer查看层级超过5层, 你就需要考虑优化下布局了~
2、善用Tag
使用include来重用布局.
使用
3、ListView优化
contentView复用
引入holder来避免重复的findViewById.
分页加载
3、响应优化
anr拿到trace文件
拿到trace信息, 一切好说.
如上trace信息中的添加的中文注释已基本说明了trace文件该怎么分析:
1、文件最上的即为最新产生的ANR的trace信息.
2、前面两行表明ANR发生的进程pid, 时间, 以及进程名字(包名).
3、寻找我们的代码点, 然后往前推, 看方法调用栈, 追溯到问题产生的根源.
以上的ANR trace是属于相对简单, 还有可能你并没有在主线程中做过于耗时的操作, 然而还是ANR了. 这就有可能是如下两种情况了:
3种anr
1、普通阻塞导致的ANR 查看堆栈
2、cpu满负荷
当是CPU占用100%, 满负荷了.
其中绝大数是被iowait即I/O操作占用了.
3、内存原因 可以看到free的内存已所剩无几.
针对三种不同的情况, 一般的处理情况如下
主线程阻塞的
开辟单独的子线程来处理耗时阻塞事务.
CPU满负荷, I/O阻塞的
I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.
内存不够用的
增大VM内存, 使用largeHeap属性, 排查内存泄露(这个在内存优化那篇细说吧)等.
4、内存优化
Android 对象、变量的内存策略
Android 对对象、变量的内存策略和Java是一样的,对内存的管理即为 对象&变量的内存分配和内存释放。下面我们讲一下内存分配和内存释放的策略:
a. 内存分配策略
对象&变量的内存分配有系统负责,共有三种:静态分配、栈式分配、堆式分配,分别面向静态变量,动态变量和对象实例。
b. 内存释放策略
对象&变量的内存释放由Java的垃圾回收器GC负责。
二、常见的内存问题及优化方案
常见的内存问题如下: 内存抖动、内存泄漏、内存溢出。
1. 内存泄漏
内存泄漏的定义是:内存中存在已经不再使用,但是并未回收的对象。表现的现象为:内存出现抖动和可用内存逐渐变少。存在的危害是:会造成内存不足,GC频繁,甚至出现OOM。
常见引发内存泄漏的主要情况有如下情况:
1. static 关键字修饰的成员变量
核心点:被static修饰过的成员变量的生命周期 = 应用程序的生命周期。
泄漏原因:若被static修饰的成员变量引用短生命周期的实例,则容易出现该成员变量的生命周期 > 引用实例生命周期的情况,当引用实例需结束生命周期销毁时,会因静态变量的持有而无法被回收,从而出现内存泄露。
解决方案:
a. 尽量避免static成员变量引用资源消耗过多的实例(若需引用Context,则尽量使用Application的Context)。
b. 使用弱引用WeakReference代替强引用持有实例。
经典案例 ==》单例模式
核心点:单例模式 其生命周期的长度 = 应用程序的生命周期
泄漏原因:若1个对象已不需再使用 而单例对象还持有该对象的引用,那么该对象将不能被正常回收 从而 导致内存泄漏
解决方案:单例模式引用的对象的生命周期 = 应用的生命周期。 或者编写代码保证在合适的时机释放资源。
2. 非静态内部类/匿名类
核心点:非静态内部类默认持有外部类的引用,可能会导致外部类对象无法释放,造成内存泄漏。
解决方案:使用静态内部类。
经典案例 ==》Runnable、AsyncTask、Thread、Handler 等类
3. 资源使用后未关闭
泄露原因:对于资源的使用(如 广播BraodcastReceiver
、文件流File
、数据库游标Cursor
、),若在Activity
销毁时没有及时关闭或注销这些资源,则这些资源将不会被回收,从而造成内存泄漏。
解决方案:在Activity
销毁时 及时关闭 / 注销资源
2. 内存抖动
核心原因:频繁创建大量、临时的小对象。
排查方向:优先寻找循环或者频繁调用的地方进行排查。
优化方案:避免创建大量、临时的小对象。
3. 内存溢出
5、电池使用优化
从电脑拔出手机,操作一会我们的目标app,再接上电脑
adb kill-servers
adb devices
adb shell dumpsys batterystats --reset
然后再去https://github.com/google/battery-historian 下载zip文件
找到下载文件中的historian.py,拷贝到常用位置
接下把cmd转到刚刚存放文件的位置来运行
python historian.py battery.txt > battery.html
运行结束后就得到了一个html文件,可以在浏览器打开查看
顶部是电量情况
最左边是各种行为
底部是时间间隔(如下图,点击查看大图)
6、网络优化