"Android 性能优化"-Android面试必问"精华技能点"汇总

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应用开发性能优化总结

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值