安卓资源收集程序员安卓自定义VIEW

Android性能优化 基础理论篇

2018-05-21  本文已影响14人  lonamessi

做什么都要往精益求精的方面去做,就拿写文章来说,写的越详细越好,一方面让读者可以读懂弄明白,再一方面自己在以后回头看也能清楚的想起来。

性能优化的重要性不再强调,新手一枚如果有不对的地方请多多指导!本文只是抛砖引玉,毕竟性能这块研究起来还是需要各方面综合起来的,那么开车了开车了!!!
472b43460482be61a4fe93749e832d51.gif

界面是直接反馈给用户的,其实说白了性能就是流畅度!

目录

一 Android渲染知识


1.1 绘制原理

Android需要每一帧在16ms内绘制完成,保证流畅的用户体验,如果没有在16ms内绘制完一帧的话就会出现丢帧的情况。那么一秒的帧率就是大约60帧。因为人眼无法感知到超过60帧的画面更新。我们都知道电影画面大部分都是24帧每秒(人眼能感知的连续线性的运动),为了更好的变现绚丽的画面内容,安卓采用了60帧来限定绘制!

1.2渲染性能问题
渲染问题其实就是丢帧现象

1.在UI主线程做大量的耗时操作,容易引起ANR;

2.布局过于复杂,导致无法在16ms内完成渲染;

3.在较短的时间内过于频繁的执行动画,导致CPU和GUP工作超负荷;

4.View的过度绘制,比如一些不能被用户看到,无用的背景,也被绘制;

5.频繁的去GC;

6.View频繁的触发measure、layout,导致measure、layout累计耗时过多 及整个View频繁的重新渲染;

7.冗余资源及逻辑等导致加载和执行缓慢;

二 界面绘制


这里主要讲解界面布局优化

三 内存问题


程序内存的管理是否合理高效对应用的性能有着很大的影响。

友情链接>>>>>>>>Android性能优化典范

3.1 内存浪费
3.1.1 ArrayMap(源码大量运用),SparseArray

安卓为我们提供了一些更佳高效的容器,那就是ArrayMap和SparseArray,为了解决HashMap的内存占量大的问题,HashMap有固定的的储存空间,如果超过会成倍增加,虽然在时间上HashMap更快,但是它也花费了更多的内存空间。由于HashMap存储的是非基本数据类型,因此自动装箱的存在意味着每次插入都会有额外的对象创建,这会影响到内存的利用。
SparseArray比HashMap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为Integer类型),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,为了优化性能,它内部对数据还采取了压缩的方式来表示稀疏数组的数据,从而节约内存空间
ArrayMap是一个<key,value>映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值,它和SparseArray一样。

3.1.2避免自动装箱

自动拆装箱的目的就是自动地将基础类型与它们的对象版本相互转化,这样你就不用操心你代码中的这些转化了,这个过程其实是这样的

Integer total = 0;
for (int i = 0; i < 100; i++) {
  total += i;
}

看似这样使我们写代码方便了许多,其实他真正的过程是这样的

Integer total = 0;
for (int i = 0; i < 100; i++) {
  //total += i;
  // create new Integer()
  // push in new value
  // add to total
}

自动装箱会创建大量的对象,然后会GC进行频繁回收。这样就导致会发生卡顿现象,经常发生在类似HashMap这样的容器里面,对HashMap的增删改查操作都会发生了大量的自动装箱的行为。当key是int类型的时候,HashMap和ArrayMap都有Autoboxing行为,为了避免Autoboxing行为Android提供了SparseArray,此容器使用于key为int类型。

链接直通车>>>>>>>>>>>>>>> 小心自动装箱

3.1.3 Enum(项目中尽量避免使用枚举)
Android官方强烈建议不要在Android程序里面使用到enum,使用enum运行时还会产生额外的内存占用.

3.2 内存泄漏

一些不用的对象长时间被持有,GC无法回收,导致内存无法被释放
可能发生内存泄漏的场景
public class MainActivity extends AppCompatActivity {

    public static Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this;
        CommUtil.getInstance(getApplicationContext());
    }

上面这种是最简单的形式,因为静态变量context引用了当前的Activity,这就导致当前的Activity无法被销毁。类似这种的静态变量都会导致内存泄漏

public class CommUtil {
    private static CommUtil commUtil;
    private Context context;

    public CommUtil(Context context) {
        this.context = context;
    }
    public static CommUtil getInstance(Context context){
        if (null==commUtil){
            commUtil = new CommUtil(context);
        }
        return commUtil;
    }
}

public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        CommUtil.getInstance(this);
    }
}

泄漏的原因是Activity的对象被单例模式的CommUtil 类所持有,而单利模式的特点是其生命周期和Application保持一致,因此Activity对象无法被及时释放。修改的方法就是把this换成getApplicationContext();

ublic class MainActivity extends AppCompatActivity {

    private Handler mHandler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            //...

        }

    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        loadData();

    }

    private void loadData(){

        //...request

        Message message = Message.obtain();

        mHandler.sendMessage(message);

    }

}

由于mHandler是非静态匿名内部类的实例,那么他就持有当前activity的引用,如果有大量消息还没有处理完的情况下关闭了activity,那样Handler会继续处理,又由于handler持有当前activity的引用,这就导致该Activity的内存资源无法及时回收,引发内存泄漏。
修改的方法就是创建一个静态的内部类Handler,对Handler对象使用弱引用,这样就避免了不能回收Handler持有的对象,也就避免了Activity的泄漏,然后我们在Activity的Destory时把消息移除就行了。具体做法如下:

public class MainActivity extends AppCompatActivity {

    private MyHandler mHandler = new MyHandler(this);

    private TextView mTextView ;

    private static class MyHandler extends Handler {

        private WeakReference<Context> reference;

        public MyHandler(Context context) {

            reference = new WeakReference<>(context);

        }

        @Override

        public void handleMessage(Message msg) {

            MainActivity activity = (MainActivity) reference.get();

            if(activity != null){

                activity.mTextView.setText("");

            }

        }

    }
    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mTextView = (TextView)findViewById(R.id.textview);

        loadData();

    }

    private void loadData() {

        //...request

        Message message = Message.obtain();

        mHandler.sendMessage(message);

    }

    @Override

    protected void onDestroy() {

        super.onDestroy();

        mHandler.removeCallbacksAndMessages(null);

    }

}

还有一种就是线程造成的内存泄漏,创建线程一般也是匿名内部类,这样也会持有当前activity的对象,如果线程任务未完成就关闭当前activity,就会导致activity无法被释放回收,引起内存泄漏。

内存泄漏的例子还很多,这里就不一一列举了。Android的内存优化涉及的知识面还有很多:内存管理的细节,垃圾回收的工作原理,如何查找内存泄漏等等都可以展开讲很多。OOM是内存优化当中比较突出的一点,尽量减少OOM的概率对内存优化有着很大的意义。这篇只是一个基础知识篇,这个系列还会继续更新。有不对的地方也望指出!

上一篇 下一篇

猜你喜欢

热点阅读