Android 性能优化(一)

Android软件是否受用户欢迎不仅仅取决于这个软件的页面设计是否精美,提供的功能是否方便,一个很重要的因素是这个app本身的性能。接下来的几篇文章,我们来讨论一下android性能优化的那些事。

高效的代码:

1、不要做重复的工作;
2、尽量避免过多的对内存分配操作。

1、数据结构选择

因为android是基于java开发的,因此我们在很多时候习惯于用java自带的数据结构来对数据进行操作, 但是android是移动端,他的内存空间是有限的,因此对内存的要求也势必更加严格。在Java开发中,我们常用HashMap,但是在Android中往往效果就不是那么理想了。因为HashMap是基于数组和链表实现的,他初始空间是16,当数据量>容量*加载因子时,他就会以2倍进行容量的扩充,当数据量很大的时候,无疑会造成内存的极大浪费。
android中提供了SparseArray 和ArrayMap进行HashMap的替换。

SparseArray

SparseArray比HashMap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为Integer类型),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,为了优化性能,它内部对数据还采取了压缩的方式来表示稀疏数组的数据。实现了从Integer到Object的映射,在特定的场合可以被用来替换HashMap。SparseArray在存储和读取数据时候,使用的是二分查找法,他是线程不安全的。

使用场景:数据量不大,最好在千级以内,key必须为int类型,这中情况下的HashMap可以用SparseArray代替:

tips:在Android工程中运行Lint进行静态代码分析,会有一个AndroidLintUseSparseArrays的检查项,如果违规,他会提示用SparseArray。

ArrayMap

ArrayMap的用法和SparseArray的用法非常类似,他的内部也是维护了两个数组,其中一个存储键的hash值,另一个用来维护value,其增删改查也是基于二分查找的。
那么两者的使用区别是:
假设数据量都在千级以内的情况下:
1、如果key的类型已经确定为int类型,那么使用SparseArray,因为它避免了自动装箱的过程,如果key为long类型,它还提供了一个LongSparseArray来确保key为long类型时的使用
2、如果key类型为其它的类型,则使用ArrayMap。

2、Handler和内部类的使用(内存泄漏)

我们在Android中涉及线程之间的通信往往会使用Handler,下面的代码就可能会引起内存泄漏:

public class MyActivity extends Activity{
private final Handler hh = new Handler(){
@override
    public void handleMessage(Message msg){
    .....
    }
};
}

原因很简单,Handler是和Looper和MessageQuene一起工作的,当一个app启动时,系统默认创建一个为主线程服务的Looper对象,他用于处理主线程的所有Message对象,生命周期和app的生命周期一样,在主线程中创建一个Handler对象,他会关联到Looper和MessageQuene,当消息队列中有消息的时候,message就会有handler的引用,这样looper进行处理消息的时候,就会调用handler的handleMessage方法,因此只要message没有被处理完,那么handler就不会被垃圾回收。
上面的代码。将Handler的实例声明为MyActivity的内部类,注意:java中非静态匿名内部类会持有外部类的一个隐式的引用,这样只要handler没有被回收,那么外面的activity就不会被回收。产生内存泄漏。

解决办法:

使用外部Activity的弱引用,并且将handler声明为静态内部类。

public class HandlerActivity2 extends Activity {  
    private final Handler mHandler = new MyHandler(this);  

    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        mHandler.sendMessageDelayed(Message.obtain(), 60000);  

        // just finish this activity  
        finish();  
    }  
    private static class MyHandler extends Handler {  
        private final WeakReference<HandlerActivity2> mActivity;  
        public MyHandler(HandlerActivity2 activity) {  
            mActivity = new WeakReference<HandlerActivity2>(activity);  
        }  

        @Override  
        public void handleMessage(Message msg) {   
            if (mActivity.get() != null) {  
                ....
            } 
        }  
    }  

3、关于Context

单例模式导致的内存泄漏(内存泄漏)

public class SingleTon{
     private static SingleTon instance;
     private Context context;
     private SingleTon(Context context) {
           this.context = context;
     }
     public static SingleTon getInstance(Context context) {
          if (instance != null) {
                instance = new SingleTon(context);
          }
          return instance;
     }
}

如果传入的context是一个Activity或者Service的实例,那么在应用退出之前,由于单例一直存在,导致对应的Activity或者Service无法被垃圾回收,造成了内存泄漏。

解决办法:

传入context.getApplicationContext()

public class SingleTon{
     private static SingleTon instance;
     private Context context;
     private SingleTon(Context context) {
           this.context = context;
     }
     public static SingleTon getInstance(Context context) {
          if (instance != null) {
                instance = new SingleTon(context.getApplicationContext());
          }
          return instance;

4、代码的细微优化

1、最好是重用对象,而不是创建对象,尤其是不要在循环中重复创建相同的对象。
2、对基本数据类型和String类型的常量,使用static final修饰,因为final类型的常量会进入静态dex文件的域初始化部分,对这类常量的调用不会涉及类的初始化。
3、类的内部避免使用getters和setters方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值