美文网首页
Android 性能优化

Android 性能优化

作者: 往事一块六毛八 | 来源:发表于2019-06-23 22:14 被阅读0次

1:布局优化

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

1.1 如何进行布局优化呢?

  • 删除布局无用的层级
  • 有选择行使用性能较低的ViewGroup.比如RelativeLayout。如果布局中能使用LinearLaout也可以使用Relativelaout,那么使用LinearLayout,因为RelativeLayotd 功能复杂些,布局过程使用的cup需要花费更多的时间。
  • 采用<include>标签,<merge>标签,Viewstub标签。<include>标签主要用于布局的重用,<merge>标签一般和<include>配合使用,减少布局的层级,ViewStub则提供了按需加载的功能,当需要是才会将ViewStub中的布局加载到内存,这提高了程序的初始化效率。

2 绘制优化

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

  • onDraw方法中不要创建新的局部对象,这是因为onDraw方法可能会被频繁调用,这样会在一瞬间产生大量的临时对象,这不仅占用了过多的内存而且还会导致系统更加频繁gc,降低了程序的执行效率。
  • onDraw方法中不要做好事的任务

内存泄漏优化

3.1 内存泄漏的原因

  • 当一个对象已经不需要在被使用,本该被GC回收时,而灵位一个正在使用的对象持有它的引用,从而导致它不能被程序回收,而停留在堆内存中。
  • 本质原因:持有引用者的生命周期>被引用者的生命周期

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 关键字修饰的成员变量的生命周期 = 应用程序的生命周期

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

  • 尽量避免 Static 成员变量引用资源耗费过多的实例(如 Context)
  • 如需引用Context,则尽量使用Applicatong的Context
  • 使用 弱引用(WeakReference) 代替 强引用 持有实例

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资源对象为能即使释放导致的内存泄漏

  • BroadcastReceiver 注销
  • 文件流File的close
  • 数据库游标Cursor(cursor.close())
  • 图片资源Bitmap未释放
  • 动画未被取消(因为动画持有了view对象,而View又持有了Activity,最终Activity无法释放)
    解决方案
    在Activity销毁时 及时关闭 / 注销资源

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)回收,从而造成 内存泄露

产生内存泄漏的条件:

  • 存在 ”工作线程实例 持有外部类引用“ 的引用关系
  • 工作线程实例的生命周期 > 外部类的生命周期,即工作线程仍在运行 而 外部类需销毁
    解决方案的思路 = 使得上述任1条件不成立 即可。
// 共有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

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

解决方案:

  • 解决方案1:静态内部类+弱引用
  • 同时,还可加上 使用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 一样,是一款图形化数据分析工具,直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况,最后提供一些可供参考电量优化的方法。

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

  • 计算优化,避开浮点运算等。
  • 避免 WaleLock 使用不当
  • 使用 Job Scheduler。

5 网络优化

6 APK体积的优化

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

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

相关文章

网友评论

      本文标题:Android 性能优化

      本文链接:https://www.haomeiwen.com/subject/claiqctx.html