Android性能优化


界面卡顿优化

Android的界面为每秒60帧,即必须在16ms内完成1帧的绘制,如果某个方法耗时过程,导致16ms内无法完成绘制,会导致丢帧,丢帧的多了,直观上感受就是界面卡顿。

60帧是人眼观看动画比较合适的频率,如果每秒的帧数过少,即频繁的出现丢帧,就会感觉界面的卡顿。

1:通过Traceview找出卡主主线程的地方
卡住主线程说明函数在主线程被调用的时长比较长,包括:

1)单个函数调用的时长长

2)函数被调用的次数比较多

2:使用方法:

1)Terminal打开DDMS,输入指令:Monitor

2)点击start method profiling

 3)操作APP可能有问题的界面

4)再次点击stop method profiling,生成表格:

3:具体案例分析:recyclerView的onBindViewHolder,当复用屏幕外的数据是,是脏数据,会进行重新绑定,调用onBindViewHolder

@Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
        {
            Log.d(TAG, "onBindViewHolder--->" + position);
            。。。。。。
       。。。。。。。
SystemClock.sleep(7); }

SystemClock.sleep(10)模拟RecyclerView滚动过程中的耗时操作,操作Recyclerview,得到以下表格:

 Real Time/Call表示一个函数被调用的时长, Calls+Recursion + call totals表示一个函数被调用和被递归调用的次数

内存优化:

1:Memory Monitor:

Memory Monitor只能看个大概,可以查看内存抖动,或者内存增长的趋势,具体的小的泄漏,还得通过Heap Viewer查看

内存抖动:短时间内发生了多次内存的涨跌,意味着很有可能发生率内存抖动。

内存抖动带来的问题:短时间内的内存飙升,系统需要频繁的进行GC,而GC是需要主线程停下来,并且占用CPU资源的,会导致界面卡顿。

例子1:

 public void click(View view) {
        for (int i = 0; i < 100; i++) {
            byte[] b = new byte[2000];
        }
    }

例子2:

private int nums[][] = new int[250][250];
//内存抖动案例:
//短时间内创建大量的临时变量
private void init() { Random random = new Random(); for (int i = 0; i < nums.length; i++) { for(int j=0; j){ nums[i][j] = random.nextInt(1000); } } } private void printNums(){ String totalNums = ""; for (int i = 0; i < nums.length; i++) { for(int j=0; j){ totalNums += nums[i][j]; } } }

//优化:
    private void printNums2(){
        StringBuffer totalNums = new StringBuffer();
        for (int i = 0; i < nums.length; i++) {
            for(int j=0; j){
                totalNums.append(nums[i][j]);
            }
        }
    }
 

使用Monitor监控:

避免内存抖动的方法:

1)尽量避免在循环体内创建对象,应该把对象创建移到循环体外

2)自定义view的onDraw会被频繁调用,避免在这个函数里面new一个新的对象

2:Heap Viewer:

监控:能够实时观测内存的变动(短时间内通过Memory Monitor是看不出来的,曲线坡度太小,内存变动值很小,定位不到具体的代码)

Heap Viewer具体定位到哪个位置内存泄漏。

内存泄漏例子:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //test1();
        init();
        handler.sendEmptyMessageDelayed(0, 100000);
    }
//模拟当MainActivity跳转到MainActivity2的时候,
    // 延迟发送消息导致的内存泄漏问题
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

    public void click(){
        Intent intent = new Intent(this, MainActivity2.class);
        startActivity(intent);
        finish();
    }

 首先运行应用,处在MainActivity,点击Dump Java Heap,获取当前内存的快照:

 得到以下表格:

 修改为根据包名查看更方便快速定位到我们自己的代码:

Leaks为0说明当前没有发生内存泄漏

MainActivity的depth为3,说明MainActivity有被引用

点击MainActivity的click,跳转到MainActivity2,MainActivity.finish(),模拟MainActivty发生内存泄漏的场景(Handler引用MainActivity导致内存泄漏),然后点击GC,再截取内存快照

Dump Java Heap:

 黄色的Leaks为2说明发生了内存泄漏,点击Leaks,通过以下References可以找到MainActivity被引用的路径。

 3:LackCanary

在一个Activity执行完onDestroy后,将它放入到WeakReference中,然后将这个WeakReference类型的Activity的对象与ReferenceQueue关联,注意: 如果一个对象要被GC回收了,会把它引用的对象放入到ReferenceQueue中。这时候只需要在ReferenceQueue中去查找是否存在该对象,如果没有就执行一个GC,再次查找,如果还是没有,则说明该对象可能无法被回收,也就可能发生了内存泄漏,最后使用HAHA这个开源库取分析dump之后的heap内存
来源: 极客熊猫
作者: Mikyou
链接: http://www.youkmi.cn/2020/01/01/android-xing-neng-you-hua-zhi-leakcanary-nei-cun-yuan-li-fen-xi/
本文章著作权归作者所有,任何形式的转载都请注明出处。 在一个Activity执行完onDestroy后,将它放入到WeakReference中,然后将这个WeakReference类型的Activity的对象与ReferenceQueue关联,注意: 如果一个对象要被GC回收了,会把它引用的对象放入到ReferenceQueue中。这时候只需要在ReferenceQueue中去查找是否存在该对象,如果没有就执行一个GC,再次查找,如果还是没有,则说明该对象可能无法被回收,也就可能发生了内存泄漏,最后使用HAHA这个开源库取分析dump之后的heap内存
来源: 极客熊猫
作者: Mikyou
链接: http://www.youkmi.cn/2020/01/01/android-xing-neng-you-hua-zhi-leakcanary-nei-cun-yuan-li-fen-xi/
本文章著作权归作者所有,任何形式的转载都请注明出处。 在一个Activity执行完onDestroy后,将它放入到WeakReference中,然后将这个WeakReference类型的Activity的对象与ReferenceQueue关联,注意: 如果一个对象要被GC回收了,会把它引用的对象放入到ReferenceQueue中。这时候只需要在ReferenceQueue中去查找是否存在该对象,如果没有就执行一个GC,再次查找,如果还是没有,则说明该对象可能无法被回收,也就可能发生了内存泄漏,最后使用HAHA这个开源库取分析dump之后的heap内存
来源: 极客熊猫
作者: Mikyou
链接: http://www.youkmi.cn/2020/01/01/android-xing-neng-you-hua-zhi-leakcanary-nei-cun-yuan-li-fen-xi/
本文章著作权归作者所有,任何形式的转载都请注明出处。 在一个Activity执行完onDestroy后,将它放入到WeakReference中,然后将这个WeakReference类型的Activity的对象与ReferenceQueue关联,注意: 如果一个对象要被GC回收了,会把它引用的对象放入到ReferenceQueue中。这时候只需要在ReferenceQueue中去查找是否存在该对象,如果没有就执行一个GC,再次查找,如果还是没有,则说明该对象可能无法被回收,也就可能发生了内存泄漏,最后使用HAHA这个开源库取分析dump之后的heap内存
来源: 极客熊猫
作者: Mikyou
链接: http://www.youkmi.cn/2020/01/01/android-xing-neng-you-hua-zhi-leakcanary-nei-cun-yuan-li-fen-xi/
本文章著作权归作者所有,任何形式的转载都请注明出处。

 分为两个步骤:

1)通过虚引用的ReferenceQueue,判断一个对象是否被回收:

虚引用:对于对象来说,是无感的,如果只存在虚引用,GC的时候会直接被回收。虚引用的目的是为了追踪一个对象被回收的时机。如果一个定义了虚引用的对象GC之后被回收了,这个对象会被放入RefereceQueue中,LeakCanary就是在GC之后去检测该队列中是否有该对象判断该对象是否已经被回收。

2)初步判定有内存泄漏之后,通过开源库Haha分析dump之后的heap内存,从而定位到具体的内存泄露的对象的引用链条。