此篇博客主要是参考《Android高级进阶》,关于Android开发过程性能优化中的代码优化和图片优化做一个记录。

代码优化

1.SparseArray的核心实现的是二分查找算法,使用SparseArray代替HanshMap,提高性能:
SparseArray的特点:
(1)SparseArray不是线程安全的。
(2)由于要进行二分查找,SparseArray会对插入的数据按照Key值大小顺序插入。
(3)SparseArray对删除操作做了优化,不会立即删除这个元素,而是通过设置标识位(DELETED)的方式,后面尝试重用。

HashMap<Integer, Boolean> ->SparseBooleanArray
HashMap<Integer, Integer> ->SparseIntArray
HashMap<Integer, Long> ->SparseLongArray
HashMap<Integer, String> ->SparseArray<String>

2.Handler和内部类的正确用法:

public class TestActivity extends Activity{
    private final Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    ...
}

上面Handler用法可能会引起内存泄漏的原因:
在Android中,一个应用启动后,系统默认会创建一个为主线程服务的Looper对象,用用处理主线程的所有Message对象,生命周期贯穿于整个应用的生命周期。
在主线程中创建Handler对象,会关联主线程Looper对象的MessageQueue,发送到MessageQueue中的Message对象会持有整个Handler对象的引用,如果Message还没有被处理完成,Handler对象不会被垃圾回收。
上面代码中,Handler声明为内部类,非静态内部匿名类会持有外部类的隐式的引用,可能导致外部类无法被垃圾回收。
所以由于Message没有处理完成,会持有handler对象的引用,而非静态的Handler对象持有外部类TestActivity的引用,这个Activity无法被垃圾回收,导致内存泄漏。
解决方法:
将handler声明为静态的内部类,静态内部类不会持有外部类的引用,不会引起内存泄漏。代码修改如下:

private static class InnerHandler extends Handler{
        private final WeakReference<TestActivity>  myActivity;
        public InnerHandler(TestActivity activity){
            myActivity = new WeakReference<TestActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            TestActivity activity = myActivity.get();
            if(activity != null){

            }
        }
    }
private InnerHandler handler = new InnerHandler(this);

3.正确地使用Context
Context的分类:
(1)Application:Android应用中的默认实例类,在Activity或者Service中通过getApplication()获取,通过context.getApplicationContext()获取到应用全局唯一的Context实例。
(2)Activity/Service:这两个类都是ContextWrapper的子类,通过getBaseContext()获取到Context实例,不同的Activity或者Service实例,他们的Context都是独立的,不会复用。
(3)BroadcastReceiver:和Activity以及Service不同,BroadcastReceiver本身不是Context的子类,而是在回调函数onReceive()中由Android框架传入一个Context的实例。系统传入的Context实例是经过功能裁剪的,不能调用registerReceiver()以及bindService()这两个函数。
(4)ContentProvider:ContentProvider也不是Context的子类,但在创建时系统会传入一个Context实例,在ContentProvider中通过调用getContext()函数获取。如果ContentProvider和调用者处于相同的应用进程中,getContext()会返回应用全局唯一的Context实例,如果是其他进程调用的ContentProvider,那么ContentProvider将持有自身所在进程的Context实例。
例子:
单例模式中传参Context,如果传入的Context是一个Activity或者Service的实例,在应用退出志气,单例一直存在,会导致对应的Activity或者Service被单例引用,不会被垃圾回收,Activity或者Service中关联的其他View或者数据结构对象也不会被释放,从而导致内存泄漏。
4.避免创建非必要的对象:
对象的创建需要内存分配,对象的销毁需要垃圾回收,这些都会在一定程度上影响到应用的性能,因此一般来说,最好是重用对象而不是在每次需要的时候去创建一个功能相同的新对象,特别是注意不要再循环中重复创建相同的对象。
5.对常量使用static final修饰:
对于基本数据类型和String类型的常量,建议使用static final 修饰,因为final类型的常量会在会进入静态dex文件的域初始化部分,这时对基本数据类型和String类型常量的调用不会设计类的初始化,而是直接调用字面量。
6.避免内部的Getters/Setters:
在面向对象编程中,Getters/Setters的作用主要是对外屏蔽具体的变量定义,从而达到更好的封装性。如果是在类内部还使用Getters/Setters函数访问变量的话,会降低访问的速度。

图片优化

图片格式:
JPEG:有损压缩图像标准格式,不支持透明和多帧动画。
PNG:无损压缩图片格式,支持完整的透明通道。
GIF:支持多帧动画。
WebP:WebP支持有损和无损压缩,完整的透明通道和多帧动画,是一种比较理想的图片格式。
1.图片压缩:
通过ImageOptim,ImageAlpha,TinyPNG等工具对图片进行压缩,对App就行瘦身。
PNG/JPEG转换为WebP,减少文件占用的大小。
2.尽量使用NinePatch格式的PNG图:
NinePatch图体积小,拉伸不变形,能够很好的适配Android各种机型。
3.图片缓存:
根据需求选择相应的图片缓存框架,Glide,Picasso,Android-Universal-Image-Loader等等。