Android 性能优化汇总:
目录:
一如何对Android应用进行性能分析
DDMS:
- 如果不用第三方,可以选用自带的ddms
traceView:
- 上次讲过的性能优化,用来检测视图层级的嵌套情况的工具
heap:
1.点击步骤:
检测是否会有会造成内存泄露的地方,按以下步骤:
- 1.点击想要检测的进程
- 2.点击Devices视图的”Update Heap”图标
- 3.点Heap视图的”Cause GC” 按钮,就可看到内存使用情况
2.说明:
1.点击一次”GC”相当向虚拟机请求一次gc操作
2.不必点击多次,Heap会自动定时更新,不断操作应用的过程就能看到内存的使用变化.
3.参数讲解:
- data object : 数据对象,即程序中大量存在的类类型的对象
- Total Size : 当前进程中所有Java对象的内存总量,一般该值的大小是判断是否内存泄露的依据.
4.判断方法:
- 1.不断操作应用,观察data object的total Size值;
- 2.正常情况下:Total Size都是在一个稳定的范围,即代码良好,对象正常被回收器回收,内存从而回落和保持稳定
- 3.非正常情况:data object的Total Size在每次GC后都不回落,反而Total Size越来越大,直达上限被kill掉;
allocation tracker(追踪)
- 1.点击进程,并并点击Allocation tracker标签里的StartTracing按钮”开始追踪”
- 2.操作应用程序,然后点击”Get Tracing”获取追踪按钮,就会得到一个列表
- 3.点击表格任何一项都会得到内存分配的栈跟踪信息:不仅直到分配了哪个类,还知道哪个线程,哪个类,哪个文件,哪一行.
演示图:
二.请介绍和区分内存溢出和内存泄露
1.内存溢出:
- “要求”分配的内存超出了系统能给的,系统不能满足需求,产生溢出
- 通俗的说: 你跟我要能装一斤半的水瓶,我能拿给你的只是能装一斤的,那就有半斤溢出来.
2.内存泄露:
- 没有及时的清理垃圾,导致系统无法再给你提供内存资源(内存就耗尽)
- 已经给程序中的某个代码分配的内存,但是用完后都不回收却占据着内存,导致内存慢慢变没有了,导致了泄露;
回收垃圾预防
以下是一个用完对象后回收的小段代码:
Vector v=new Vector(10);
for (int i=1;i<10000; i++)
{
Object o=new Object();
v.add(o);
o=null;
}
3.总结:
- 1.一个是要太多给不了-溢出
- 2.一个是借给你钱收不回-泄露
3.我想告诉大家我的观点:
- 内存泄露导致内存溢出,为什么?因为泄露使得内存慢慢变少,追踪导致小到无法提供对象的创建,就溢出;
- (好比一个水池底部有个水龙头,如果慢慢加水,你却不开水龙头用水,那么水就爆满了)
4.面试官这么问就是想引出你怎么处理内存问题而已从而问你OOM(说白了就是内存不足).
三.什么情况导致内存“泄露”,以及解决/避免的方法:
1.程序的堆内存大小:
- Android**每一个应用的堆内存**大小有限, 通常的情况为16M-48M,如果内存占用超过一定水平就会内存不足OOM
2.内存溢出的原因和解决方法:
1.资源释放问题
- Android开发最典型的就是Activity的生命周期中的onPause(),onStop(),onDestry()进行资源的释放
- 长期保持资源如:Context,Cursor(数据库游标不关闭),IO,因不释放导致泄露
2.对象占用内存过大问题
- 耗用内存过大的对象(Bitmap,XML),造成内存超出限制
图片过大OOM:
方法1:等比例缩小图片并回收:(点击访问专题)
//最后回收 bmp.recycle();
方法2::对图片采用软引用,及时地进行recyle()操作,释放内存
SoftReference<Bitmap> bitmap = new SoftReference<Bitmap>(pBitmap);
//在这里获取并使用图片操作;
//doSomething();
if(bitmap != null){
if(bitmap.get() != null && !bitmap.get().isRecycled()){
//回收并置空
bitmap.get().recycle();
bitmap = null;
}
}
界面的切换导致OOM
- 横竖切屏N次后OOM(没有固定的解决方法),以下是参考
- 1.布局中有无大图片改成用代码设置,换成比例显示(点击专题访问)
- 2.xml大背景图片,改在程序中设置
Drawable drawable = getResources().getDrawable(R.drawable.id);
ImageView imageView = new ImageView(this);
imageView.setBackgroundDrawable(drawable);
在Activity destory 时注意,drawable.setCallback(null); 防止Activity得不到及时的释放。
原因是: 我们如果背景图很大,然后我们打开了过个界面,那么就会加载很过的xml,而上面的drawable.setCallback能更快的释放.
- 3.平时的xml可以加载成view放容器里,然后再this.setContentView()方法避免重复加载
- 4.页面切换时应尽量少使用重复的”导致叠加”的代码
3.static关键字的使用问题
- 修饰成员变量的时候,该变量就属于该类,而不是实例,所以该变量的生命周期很长,如果用来修饰一些资源耗费过多的实例(Context最容易引发),就危险了.
- 如下代码危险
public class ClassName{
public static Context mContext;
}
- 原因: 将Activity赋值到mContext,即使Activity已经销毁,但是mContext是属于ClassName类,相当于还有对象保存Activity的引用,所以Activity不会得到释放;
- 详细代码如下:
//静态的成员变量属于类的实例
private static Drawable sBackground;
@Override protected void onCreate(Bundle state) {
super.onCreate(state);
//定义控件和上下文绑在以前
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
//静态变量赋值获取图片
sBackground = getDrawable(R.drawable.large_bitmap);
}
//控件设置背景图,和静态变量联系在一起了
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
- 所以以上的代码顺序可以简单看为: 控件绑定上下文然后设置静态成员变量
- 引用链就是: Drawable->TextView->Context,引发泄露
static解决方案:
- 避免static成员变量引用太多耗资源的实例:如Context
- Context尽量使用ApplicationContext,因为因为这个上下文生命周期比较长(是整个程序运行期间),引用他不会产生内存泄露
(全局上下文,并且是单例) - 使用弱引用代替强引用,比如使用WeakReference< Context> mContext;
4.线程使用问题
(产生的原因:线程生命周期不可控)
代码如下:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyThread().start();
}
private class MyThread extends Thread {
@Override
public void run() {
super.run();
// 这里进行耗时操作,且拥有Context,没执行完就切换屏幕
}
}
}
采用下面的代码,会发现销毁后还是打印:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyThread().start();
}
static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
System.out.println("onDestroy-----------------------------------");
}
我们常常这样写,按我们的理解,当我们切换横竖屏,Activity生命周期重新计算,然后就destroy了,对象对象垃圾了,但是因为线程是非静态内部类,持有外部类的强引用,所以即使我们销毁了界面,但是线程还在运行,若持有上下文引用,那么Activity销毁后,对象依然不会被释放,如果;
解决方法:
- 将线程的内部类由”非静态”改为”静态”(因为非静态内部类对外部类对象有强引用,静态类则不会;区分上面说static成员变量属于类不属于实例,这里不是成员变量是静态内部类)
- 线程内部用弱引用保存Context引用
5.没有使用缓存的convertView
- 在使用ListView 的时候通常会使用Adapter,那么我们应该尽可能的使用ConvertView.
四.Android如何”捕获-未捕获”的异常(重要)
(PS:我们平时都是用 try catch处理可能存在的异常,或者在方法上用throws抛出,但是有一些异常我们捕获不了比如UncatchException 比如空指针等等)
小例子:
int a = 0;
int c = 14/a;
我们不处理异常,为了捕获
运行结果:
把为捕获的捕获到了;
核心:类继承Application+实现UncatchExceptionHandler
- 1.新建MyApplicationn,继承Application,实现UncaughtExceptionHandler
- 2.复写UncaughtExceptionHandler的onCreate和uncaughtException 方法
- 3.详细方法如下:
public class MyApplication extends Application implements Thread.UncaughtExceptionHandler {
@Override
public void onCreate() {
super.onCreate();
// 在onCtreat里要设置默认的未获取异常的Handler
// 给Thread 类设置默认异常处理handler,如果这句代码不执行则一切都是白搭。
Thread.setDefaultUncaughtExceptionHandler(this);
}
// 在uncaughtException方法中我们必须新开辟个线程进行我们异常的收集工作,然后将系统给杀死。
@Override
public void uncaughtException(final Thread thread, final Throwable ex) {
// 开启线程获取异常
// 读取异常
// 然后再把系统杀死
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
// 获取线程的ID和异常信息
Toast.makeText(MyApplication.this, "thread id = " + thread.getId() + ", info = " + ex.toString(),
Toast.LENGTH_SHORT).show();
Looper.loop();
}
}).start();
}
}
此时MyApplication还是灰色的,我们要在清单文件里的< application里>注册:
<application
android:name=".MyApplication"
MyApplication就变成被使用了
所以当程序运行,在过程中如果碰到未捕获的异常,就能捕获到为捕获的异常;
五.Android性能优化博客参考
点击访问:
http://blog.csdn.net/yanbober/article/details/48394201
目录如下:
1.背景
2.应用UI性能问题分析
2-1 应用UI卡顿原理
2-2 应用UI卡顿常见原因
2-3 应用UI卡顿分析解决方法
2-3-1 使用HierarchyViewer分析UI性能
2-3-2 使用GPU 过度绘制分析UI性能
2-3-3 使用GPU 呈现模式图及FPS考核UI性能
2-3-4 使用Lint进行资源及冗余UI布局等优化
2-3-5 使用Memory 监测及GC打印与Allocation Tracker进行UI卡顿分析
2-3-6 使用Traceview和dmtracedump进行分析优化
2-3-7 使用Systrace进行分析优化
2-3-8 使用tracestxt文件进行ANR 分析优化
2-4 应用UI性能分析解决总结
3.应用开发Memory 内存性能分析优化
3-1 Android 内存管理原理
3-2 Android 内存泄露性能分析
3-2-1 Android应用内存泄露概念
3-2-2 Android应用内存泄露察觉手段
3-2-3 Android应用内存泄露leakcanary 工具定位分析
3-2-4 Android应用内存泄露MAT工具定位分析
3-2-5 Android应用开发规避内存泄露建议
3-3 Android 内存溢出OOM性能分析
3-3-1 Android应用内存溢出OOM概念
3-3-2 Android应用内存溢出OOM性能分析
3-3-3 Android应用规避内存溢出OOM建议
3-4 Android 内存性能优化总结
4.Android 应用API使用及代码逻辑性能分析
4-1 Android 应用StringStringBuilderStringBuffer优化建议
4-2 Android 应用OnTrimMemory 实现性能建议
4-3 Android 应用HashMap 与ArrayMap 及SparseArray优化建议
4-4 Android 应用ContentProviderOperation优化建议
4-5 Android 应用其他逻辑优化建议
5.Android 应用移动设备电池耗电性能分析
5-1 Android 应用耗电量概念
5-2 Android 应用耗电量优化建议
6.Android应用开发性能优化总结