Android 性能优化

2019-06-23  本文已影响0人  往事一块六毛八

1:布局优化

减少布局文件的层级,布局中的层级减少了,意味着Android绘制时的工作量少了,程序的性能自然就高了。

1.1 如何进行布局优化呢?

2 绘制优化

绘制优化是指View的onDraw方法要避免执行大量的操作,这主要体现在两个方面。

内存泄漏优化

3.1 内存泄漏的原因

3.2 内存泄漏的场景

3.2.1 静态变量导致的内存泄漏

public class MainActivity extends AppCompatActivity {
    private static Context sContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sContext=this;
    }
}

public class MainActivity extends AppCompatActivity {
    private static View mView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sContext=this;
        mView=new View(this);
    }
}
public class ClassName {
 // 定义1个静态变量
 private static Context mContext;
 //...
// 引用的是Activity的context
 mContext = context; 

// 当Activity需销毁时,由于mContext = 静态 & 生命周期 = 应用程序的生命周期,故 Activity无法被回收,从而出现内存泄露

}

以上三个都是Static成员变量持有了当前activity的引用,被 Static 关键字修饰的成员变量的生命周期 = 应用程序的生命周期

静态变量导致的内存泄漏解决方案

3.2.2 单利模式导致的内存泄漏

public class SingleInstanceClass {    
    private static SingleInstanceClass instance;    
    private Context mContext;    
    private SingleInstanceClass(Context context) {        
        this.mContext = context.getApplicationContext(); // 传递的是Application 的context
    }    

    public SingleInstanceClass getInstance(Context context) {        
        if (instance == null) {
            instance = new SingleInstanceClass(context);
        }        
        return instance;
    }
}

解决方式;
当单利模式要访问资源文件的时候,这个时候要获取Context对象,此时传入Application的Context对象即可

3.2.3资源对象为能即使释放导致的内存泄漏

3.2.4非静态内部类|匿名类

非静态内部类 / 匿名类 默认持有 外部类的引用;而静态内部类则不会

非静态内部类的实例 = 静态

若 非静态内部类所创建的实例 = 静态(其生命周期 = 应用的生命周期),会因 非静态内部类默认持有外部类的引用 而导致外部类无法释放,最终 造成内存泄露
即 外部类中 持有 非静态内部类的静态对象

   a. 在启动频繁的Activity中,为了避免重复创建相同的数据资源,会在Activity内部创建一个非静态内部类的单例
   b. 每次启动Activity时都会使用该单例的数据

public class TestActivity extends AppCompatActivity {  
    
    // 非静态内部类的实例的引用
    // 注:设置为静态  
    public static InnerClass innerClass = null; 
   
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);   

        // 保证非静态内部类的实例只有1个
        if (innerClass == null)
            innerClass = new InnerClass();
    }

    // 非静态内部类的定义    
    private class InnerClass {        
        //...
    }
}

// 造成内存泄露的原因:
    // a. 当TestActivity销毁时,因非静态内部类单例的引用(innerClass)的生命周期 = 应用App的生命周期、持有外部类TestActivity的引用
    // b. 故 TestActivity无法被GC回收,从而导致内存泄漏

解决方案:

多线程:AsyncTask、实现Runnable接口、继承Thread类

原因:
多线程的使用方法 = 非静态内部类 / 匿名类;即 线程类 属于 非静态内部类 / 匿名类

 /** 
     * 方式1:新建Thread子类(内部类)
     */  
        public class MainActivity extends AppCompatActivity {

        public static final String TAG = "carson:";
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            // 通过创建的内部类 实现多线程
            new MyThread().start();

        }
        // 自定义的Thread子类
        private class MyThread extends Thread{
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    Log.d(TAG, "执行了多线程");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

   /** 
     * 方式2:匿名Thread内部类
     */ 
     public class MainActivity extends AppCompatActivity {

    public static final String TAG = "carson:";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 通过匿名内部类 实现多线程
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    Log.d(TAG, "执行了多线程");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }.start();
    }
}


/** 
  * 分析:内存泄露原因
  */ 
  // 工作线程Thread类属于非静态内部类 / 匿名内部类,运行时默认持有外部类的引用
  // 当工作线程运行时,若外部类MainActivity需销毁
  // 由于此时工作线程类实例持有外部类的引用,将使得外部类无法被垃圾回收器(GC)回收,从而造成 内存泄露

产生内存泄漏的条件:

// 共有2个解决方案:静态内部类 & 当外部类结束生命周期时,强制结束线程
// 具体描述如下

   /** 
     * 解决方式1:静态内部类
     * 原理:静态内部类 不默认持有外部类的引用,从而使得 “工作线程实例 持有 外部类引用” 的引用关系 不复存在
     * 具体实现:将Thread的子类设置成 静态内部类
     */  
        public class MainActivity extends AppCompatActivity {

        public static final String TAG = "carson:";
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            // 通过创建的内部类 实现多线程
            new MyThread().start();

        }
        // 分析1:自定义Thread子类
        // 设置为:静态内部类
        private static class MyThread extends Thread{
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    Log.d(TAG, "执行了多线程");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

   /** 
     * 解决方案2:当外部类结束生命周期时,强制结束线程
     * 原理:使得 工作线程实例的生命周期 与 外部类的生命周期 同步
     * 具体实现:当 外部类(此处以Activity为例) 结束生命周期时(此时系统会调用onDestroy()),强制结束线程(调用stop())
     */ 
     @Override
    protected void onDestroy() {
        super.onDestroy();
        Thread.stop();
        // 外部类Activity生命周期结束时,强制结束线程
    }

消息传递机制:Handler

非静态内部类 & 匿名内部类都默认持有 外部类的引用

解决方案:

  • 同时,还可加上 使用WeakReference弱引用持有Activity实例
  • 原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
public class MainActivity extends AppCompatActivity {

    public static final String TAG = "carson:";
    private Handler showhandler;

    // 主线程创建时便自动创建Looper & 对应的MessageQueue
    // 之后执行Loop()进入消息循环
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //1. 实例化自定义的Handler类对象->>分析1
        //注:
            // a. 此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue;
            // b. 定义时需传入持有的Activity实例(弱引用)
        showhandler = new FHandler(this);

        // 2. 启动子线程1
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // a. 定义要发送的消息
                Message msg = Message.obtain();
                msg.what = 1;// 消息标识
                msg.obj = "AA";// 消息存放
                // b. 传入主线程的Handler & 向其MessageQueue发送消息
                showhandler.sendMessage(msg);
            }
        }.start();

        // 3. 启动子线程2
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // a. 定义要发送的消息
                Message msg = Message.obtain();
                msg.what = 2;// 消息标识
                msg.obj = "BB";// 消息存放
                // b. 传入主线程的Handler & 向其MessageQueue发送消息
                showhandler.sendMessage(msg);
            }
        }.start();

    }

    // 分析1:自定义Handler子类
    // 设置为:静态内部类
    private static class FHandler extends Handler{

        // 定义 弱引用实例
        private WeakReference<Activity> reference;

        // 在构造方法中传入需持有的Activity实例
        public FHandler(Activity activity) {
            // 使用WeakReference弱引用持有Activity实例
            reference = new WeakReference<Activity>(activity); }

        // 通过复写handlerMessage() 从而确定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.d(TAG, "收到线程1的消息");
                    break;
                case 2:
                    Log.d(TAG, " 收到线程2的消息");
                    break;


            }
        }
    }
}

解决方案2:当外部类结束生命周期时,清空Handler内消息队列

@Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
        // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
    }

集合类

原因:
集合类 添加元素后,仍引用着 集合元素对象,导致该集合元素对象不可被回收,从而 导致内存泄漏

// 通过 循环申请Object 对象 & 将申请的对象逐个放入到集合List
List<Object> objectList = new ArrayList<>();        
       for (int i = 0; i < 10; i++) {
            Object o = new Object();
            objectList.add(o);
            o = null;
        }
// 虽释放了集合元素引用的本身:o=null)
// 但集合List 仍然引用该对象,故垃圾回收器GC 依然不可回收该对象

解决方案:

集合类 添加集合元素对象 后,在使用后必须从集合中删除

 // 释放objectList
        objectList.clear();
        objectList=null;

4电量优化

在 Android5.0 以前,在应用中测试电量消耗比较麻烦,也不准确,5.0 之后专门引入了一个获取设备上电量消耗信息的 API:Battery Historian。Battery Historian 是一款由 Google 提供的 Android 系统电量分析工具,和Systrace 一样,是一款图形化数据分析工具,直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况,最后提供一些可供参考电量优化的方法。

除此之外,还有一些常用方案可提供

5 网络优化

6 APK体积的优化

https://www.jianshu.com/p/97476e095e1a

https://blog.csdn.net/huangxiaoguo1/article/details/80434456
https://www.jianshu.com/p/9745a9375191

上一篇 下一篇

猜你喜欢

热点阅读