首页 > 系统 > Android > 正文

Android 性能优化(一)

2019-11-06 09:54:49
字体:
来源:转载
供稿:网友

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方法。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表