注:根据Android官方的建议,编写高效代码的三个基本准则以下:java
正确的选择合适的数据结构很重要,对java中常见的数据结构例如ArrayList和LinkedList、HashMap和HashSet等,须要作到对它们的联系与区别有教深刻的理解,这样在编写代码中面临选择时才能做出正确的选择,下面咱们以android开发中使用SparseArray代替HashMap为例进行说明。SparseArray是Android平台特有的稀疏数组的实现,它是Integer到Object的一个映射,在特定场合可用于代替HashMap<Integer,<E>>,提升性能。核心实现是二分法查找算法。android
————————————————————————————————————————程序员
SparseArray家族目前有如下四类:算法
————————————————————————————————————————编程
HashMap<Integer, Boolean> booleanHashMap = new HashMap<>();
SparseBooleanArray booleanArray = new SparseBooleanArray();
HashMap<Integer,Integer> integerHashMap = new HashMap<>();
SparseIntArray intArray = new SparseIntArray();
HashMap<Integer,Long> longHashMap = new HashMap<>();
SparseLongArray sparseLongArray = new SparseLongArray();
HashMap<Integer,String> stringHashMap = new HashMap<>();
SparseArray<String> sparseArray = new SparseArray<>();复制代码
————————————————————————————————————————设计模式
须要注意的几点以下:数组
在Android工程中运行Lint进行静态代码块分析,会有一个名为AndroidLintUseSparseArrays的检查项,若是违规,它会提示:缓存
————————————————————————————————————————安全
HashMap can be replaced with SparseArraybash
————————————————————————————————————————
这样能够很轻松地找到工程中优化的地方。
————————————————————————————————————————
Android代码中涉及线程间通讯的地方常常会使用Handler,典型的代码结构以下:
————————————————————————————————————————
public class HandlerActivity extends Activity {
private final Handler mLeakyHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}复制代码
————————————————————————————————————————
使用AndroidLint分析这段代码,会违反检查项AndroidLintHanderLeak,获得以下提示:
————————————————————————————————————————
This Handler class should be static or leaks might occur
————————————————————————————————————————
那么产生内存泄漏的缘由多是什么?Handler和Looper以及MessageQueue一块儿工做的,在Android中,一个应用启动后,系统默认会建立一个为主线程服务的Looper对象,该Looper对象用于处理主线程的全部Message对象,它的生命周期贯穿于整个应用的生命周期。在主线程中使用的Handler都会默认绑定到这个Looper对象。在主线程中建立Handler对象时,它会当即关联主线程Looper对象的MessageQueue,这时发送到MessageQueue中的Message对象都会持有这个Handler对象的引用,这样Looper处理消息时才能回调到Handler的handlerMessage方法。所以,若是Message尚未被处理完成,那么Handler对象也就不会被垃圾回收。
在上面的代码中,将Handler的实例声明为HandlerActivity类的内部类。而在Java语言中,非静态内部匿名类会持有外部类的一个隐式的引用,这样就可能会致使外部类没法被垃圾回收。所以,最终因为MessageQueue中的Message尚未处理完成,就会持有Handler对象的引用,而非静态的Handler对象会持有外部类HandlerActivity的引用,这样Activity没法被垃圾回收,从而致使内存泄漏。
一个明显的会引入内存泄漏的例子以下:
————————————————————————————————————————
/**
* ================================================================
* User:xijiufu
* Email:xjfsml@163.com
* Version:1.0
* Time:2017/4/20--2:07
* Function:
* ModifyHistory:
* ================================================================
*/
public class HandlerActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//延迟10分钟发送消息
mLeakyHandler.postAtTime(new Runnable() {
@Override
public void run() {
/***/
}
}, 1000 * 60 * 10);
}
}复制代码
————————————————————————————————————————
因为消息延迟10分钟发送,所以,当用户进入这个Activity并退出后,在消息发送并处理完成以前,这个Activity是不会被系统回收(系统内存确实不够使用的状况例外)
如何解决呢?两个方案:
————————————————————————————————————————
/**
* ================================================================
* User:xijiufu
* Email:xjfsml@163.com
* Version:1.0
* Time:2017/4/20--2:07
* Function:
* ModifyHistory:
* ================================================================
*/
public class HandlerActivity extends Activity {
/***
* 声明一个静态的Handler内部类,并持有外部类的弱引用
*/
private static class InnerHandler extends Handler {
private final WeakReference<HandlerActivity> mActivity;
public InnerHandler(HandlerActivity activity) {
this.mActivity = new WeakReference<HandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity = mActivity.get();
if (activity != null) {
//..
}
}
}
private final InnerHandler mHandler = new InnerHandler(this);
/**
* 静态的匿名内部类不会持有外部类的引用
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() {
/****/
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//延迟10分钟发送消息
mHandler.postAtTime(sRunnable, 1000 * 60 * 10);
}
}复制代码
————————————————————————————————————————
Context应该是每一个入门Android开发的程序员第一个接触到的概念,它表明当前的上下文环境,能够用来实现不少功能的调用,语句以下:
————————————————————————————————————————
//获取资源管理器对象,进而能够访问到例如string,color等资源
Resources resources = context.getResources();
//启动指定的Activity
context.startActivity(new Intent(this, MainActivity.class));
//获取各类系统服务
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
//获取系统文件目录
File internalDir = context.getCacheDir();
File externalDir = context.getExternalCacheDir();
//更多。。。复制代码
可见,正确理解Context的概念是很重要的,虽然应用开发中随处可见Context的使用,但并非全部的Context实例都具有相同的功能,在使用上须要区别对待,不然极可能会引入问题。咱们首先来总结下Context的种类。
根据Context依托的组件以及用途不一样,咱们能够将Context分为以下几种。
错误地使用Context可能会致使内存泄漏,典型的例子是在实现单例模式时使用Context,以下代码是可能会致使内存泄漏的单例实现。
————————————————————————————————————————
/**
* ================================================================
* User:xijiufu
* Email:xjfsml@163.com
* Version:1.0
* Time:2017/4/20--23:57
* Function:
* ModifyHistory:
* ================================================================
*/
public class SingleInstance {
private Context mContext;
private static SingleInstance sInstance;
private SingleInstance(Context context) {
mContext = context;
}
public static SingleInstance getInstance(Context context) {
if (sInstance == null) {
sInstance = new SingleInstance(context);
}
return sInstance;
}
}复制代码
————————————————————————————————————————
若是使用者调用getInstance时传入的Context是一个Activity或者Service的实例,那么在应用退出以前,因为单例一直存在,会致使对应的Activity或者Service被单例引用,从而不会被垃圾回收,Activity或者Service中关联的其余View或者数据结构对象也不会被释放,从而致使内存泄漏。正确的作法是使用Application Context,由于它是应用惟一的,并且声明周期是跟着应用一致的,正确的单例实现以下:
————————————————————————————————————————
/**
* ================================================================
* User:xijiufu
* Email:xjfsml@163.com
* Version:1.0
* Time:2017/4/20--23:57
* Function:
* ModifyHistory:
* ================================================================
*/
public class SingleInstance {
private Context mContext;
private static SingleInstance sInstance;
private SingleInstance(Context context) {
mContext = context;
}
public static SingleInstance getInstance(Context context) {
if (sInstance == null) {
sInstance = new SingleInstance(context.getApplicationContext());//这一句关键
}
return sInstance;
}
}复制代码
不一样组件中的Context能提供的功能不尽相同,总结起来,以下表:
功能 | Application | Activity | Service | BroadcastReceiver | ContentProvider |
显示Dialog | NO | YES | NO | NO | NO |
启动Activity | NO[1] | YES | NO[1] | NO[1] | NO[1] |
实现LayoutInflation | NO[2] | YES | NO[2] | NO[2] | NO[2] |
启动Service | YES | YES | YES | YES | YES |
绑定Service | YES | YES | YES | YES | NO |
发送Broadcast | YES | YES | YES | YES | YES |
注册Broadcast | YES | YES | YES | YES | NO[3] |
加载资源Resource | YES | YES | YES | YES | YES |
其中NO[1]标记表示对应的组件并非真的不能够启动Activity,而是建议不要这么作,由于这些组件会在新的Task中建立Activity,而不是在原来的Task中。
NO[2]标记也是表示不建议这么作,由于在非Activity中进行Layout Inflation,会使用系统默认的主题,而不是应用中设置的主题。
NO[3]标记表示在Android4.2及以上的系统上,若是注册的BroadcastReceiver是null时是能够的,用来获取sticky广播的当前值。
掌握java的四种引用类型对于写出内存使用良好的应用是很关键的。
强引用:Java里面最普遍使用的一种,也是对象默认的引用类型。若是一个对象具备强引用,那么垃圾回收期是不会对它进行回收操做的,当内存空间不足时,Java虚拟机将会抛出OutOfMemoryError错误,这时应用将会终止运行。一句话总结,只要引用存在,垃圾回收器永远不会回收。Object obj = new Object(); 能够直接经过obj取得对应的对象,只有obj这个引用被释放以后,对象才会被释放掉。
Object object = new Object();
SoftReference<Object> sf = new SoftReference<Object>(object);复制代码
Object object = new Object();
WeakReference<Object> reference = new WeakReference<Object>(object);复制代码
————————————————————————————————————————
对象的建立须要内存分配,对象的销毁须要垃圾回收,这些都会必定程度上影响到应用的性能。所以通常来水,最好是重用对象而不是在每次须要的时候去建立一个功能相同的新对象,特别是注意不要在循环中重复建立相同的对象。
————————————————————————————————————————
对于基本数据类型和String类型的常量,建议使用static final 修饰,由于final类型的常量会在会进入静态dex文件的域初始化部分,这是对基本数据类型和String类型常量的调用不会涉及类的初始化,而是直接调用字面量。
————————————————————————————————————————
在面向对象编程中,Getters/Setters的做用主要是对外屏蔽具体的变量定义,从而达到更好的封装性。但若是是在类内部还使用Getters/Setters函数访问变量的话,会下降访问的速度。根据Android官方文档,在没有JIT(Just In Time)编译器时,直接访问变量的速度是调用Getter方法的3倍;在JIT编译时,直接访问变量的速度是调用Getters方法的7倍。固然,若是你的应用中使用了ProGuard(混淆代码)的话,那么ProGuard会对Getters/Setters进行内联操做,从而达到直接访问的效果。
代码的重构是一项长期的锲而不舍的工做,须要依靠团队中每个成员来维护代码库的高质量,要会去享受高质量代码带来的快感,如何有效的进行代码重构,除了须要对你所在项目有较深刻的理解以外,你还须要必定的方法指导。重构代码可使用不一样的设计模式来达到高质量的代码,这儿能够关注个人设计模式系列博客:Android设计模式之——单例模式(一) Android设计模式之——Builder模式(二)