Androd性能优化笔记

UI优化:


  1. 设计布局时不要嵌套太多层,会造成过度绘制
  2. 自定义View中onDraw()避免耗时操作,因为Android系统每16ms执行一次界面渲染
  3. 尽量少使用background,也会造成过度绘制,如果父视图中已有同色背景,可移除子视图中不必要的背景

    优化方案
    1.可使用ConstraintLayout解决布局多重嵌套的问题
    2.可打开手机中开发者模式中的调试GPU过度渲染功能,红色区域表示为过度渲染
    3.可使用include标签和ViewStub标签来引入布局,
    ViewStub常常用来引入那些默认不显示,只在特定情况下才出现的布局,例如:进度条,网络连接失败显示的提示布局等,ViewStub的好处在于默认不显示也不会占用视图位置,即不会参与layout的解析,只有在手动调用其inflate()时才会解析其引入的布局:
    ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout);
    View layout_error = stub.inflate();
    4.当某布局顶接点是FrameLayout并且不需要设置background或者padding等属性,可使用merge代替,然后使用include引入顶结点会自动被忽略,而其自己点全部合并到主布局中,从而减少一层视图嵌套

内存泄露:


Android内存优化主要是针对堆(Heap)而言的,当堆中对象的作用域结束的时候,这部分内存也不会立刻被回收,而是等待系统GC进行回收。
Java中造成内存泄漏的根本原因是:堆内存中长生命周期的对象持有短生命周期对象的强/软引用,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收。

常见内存泄露

1.单例模式引用:

public class SingleTon{
    private Context context;
    private static SingleTon singleTon;
    public static final SingleTon getInstance(Context context){
        this.context = context;//此处应改为 context.getApplicationContext;
        return SingleHolder.INSTANCE;
    }
    private static class SingleHolder{
        private static final SingleTon INSTANCE = new SingleTon();
    }
}

2.Handler造成的内存泄露:
由于Handler属于TLS(Thread Local Storage)变量,生命周期和Activity是不一致的,如果在Activity中使用创建一个非静态的内部类Handler,该Handler对象就会持有Activity的引用导致Activity对象无法即时回收,正确做法应该是将MyHandler 改为静态内部类,静态内部类不会持有外部类的引用,并用弱引用的方式持有Acivity的引用:

    /** 
     * 声明一个静态的Handler内部类,并持有外部类的弱引用 
     */  
    private static class MyHandler extends Handler{  

        private final WeakReference<HandlerActivity> mActivtyReference;  

        private MyHandler(HandlerActivity mActivty) {  
            this.mActivty = new WeakReference<HandlerActivity>(mActivty);  
        }  

        @Override  
        public void handleMessage(Message msg) {  
            super.handleMessage(msg);  
            HandlerActivity activity = mActivtyReference.get();  
            if (activity != null){  
                // ....  
            }  
        }  
    }  

    //并且在Activity销毁时移除消息队列中的消息
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //传入null,就表示移除所有Message和Runnable
        myHandler.removeCallbacksAndMessages(null);
    }

3.匿名内部类在异步线程中的使用,原因同Handler类似,因为该匿名的内部类持有了Activity的引用,而在异步线程中使用该对象,无法保证异步线程的生命周期是否长于Activity的生命周期而造成内存泄露,正确的做法同Handler的处理方法类似,将使用静态内部类+弱引用的方式 或者在Activity销毁的时候手动结束该异步任务

4.static成员变量导致的内存泄露
我们知道static修饰的变量位于内存的静态存储区,此变量与App的生命周期一致 ,但如果某个static修饰的成员变量指向的 对象持有Activity的引用,就会造成Activtiy无法被GC回收,比如static修饰的成员变量指向Activity的内部类实例、或者static修饰的成员变量为View对象时(Activity页面中的View对象都会持有Activity的引用)都会在成内存泄露,错误代码如下:

public class SomeActivity{
    private static InnerClass mInnerClass;
    private static View mStaticView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //因为mInnerClass粗存在静态区,这里new InnerClass()只会初始化一次,但会一直持有当前Activity的引用,造成内存泄露,mStaticView也一样
        if(mInnerClass==null){ 
            mInnerClass = new InnerClass();
        }
        mStaticView = findViewById(R.id.rcv);

    }

    class InnerClass{
        //...
    }
 }

5.其他内存泄漏的正确处理
在Activity销毁时调用WebView.destroy()释放内存。
Bitmap在不使用的时候注意调用Bitmap.recycle()释放内存。
自定义View中自定义属性获取完毕后注意调用TypedArray.recycle()释放内存。
读取资源文件时Cursor对象或输入输出流对象注意手动关闭释放内存。
避免在代码设计中使用循环引用,比如A持有B,B持有C,C持有A,这样4个对象均无法释放

6.关于内存抖动
内存抖动是由于短时间内创建了大量生命周期较短的对象,触发GC回收造成的,而频繁的GC操作会引起系统卡顿
应避免在高频执行的函数中重复创建对象,或短时间内创建大量对象,应使用缓存服用或线程池寻求解决方案

猜你喜欢

转载自blog.csdn.net/zhangbo328/article/details/80923983