《Android源码设计模式》--单例模式
No1:
懒汉单例模式优缺点分析
public class Singleton{ private static Singleton instance; private Singleton(){} public static synchronized Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
优点:单例只有在使用时才会被实例化,在一定程度上节约了资源
缺点:第一次加载时需要及时进行实例化,反应稍慢,最大的问题是每次调用getInstance都进行同步,造成不必要的同步开销。
所以这种模式一般不建议使用
No2:
Double Check Lock(DCL)双重检查锁定方式
public class Singleton{ private static Singleton sInstance = null; private Singleton(){} public void doSomething(){ System.out.println("do sth."); } public static Singleton getInstance(){ if(mInstance == null){ synchronized(Singleton.class){ if(mInstance == null){ sInstance = new Singleton(); } } } return sInstance; } }
优点:,第一次执行getInstance时单例对象才会被实例化,资源利用率高。第一层对instance判空主要是为了避免不必要的同步,第二层判断则是为了在null的情况下创建实例
缺点:第一次加载时反应稍慢,也由于java内存模型的原因偶尔会失败。再高并发环境下也有一定缺陷,虽然发生概率很小
No3:
静态内部类单例模式
public class Singleton{ private Singleton(){} public static Singleton getInstance(){ return SingletonHolder.sInstance; } /** *静态内部类 */ private static class SingletonHolder{ private static final Singleton sInstance = new Singleton(); } }
第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,所以推荐使用
No4:
枚举单例
public enum SingletonEnum{ INSTANCE; public void doSomething(){ System.out.println("do sth."); } }
优点:线程安全,即使反序列化也不会重新生成新的实例
No5:
如果想杜绝单例模式对象在被反序列化时重新生成对象,那么必须加入readResolve函数
public final class Singleton implements Serializable{ private static final long serialVersionUID = 0L; private static final Singleton INSTANCE = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return INSTANCE; } private Object readResolve() throws ObjectStreamException{ return INSTANCE; } }
也就是在readResolve方法中将单例对象返回,而不是重新生成一个新的对象。而对于枚举,并不存在这个问题
No6:
1)可序列化类中的字段类型不是Java的内置类型,那么该字段类型也需要实现Serializable接口
2)如果调整了可序列化类的内部结构,例如新增、去除某个字段,但没有修改serialVersionUID,会引发异常。最好的方案是将值设置为0L,只是那些新修改的字段会为0或者null。
No7:
使用容器实现单例模式
public class SingletonManager{ private static Map<String,Object> objMap = new HashMap<String,Object>(); private SingletonManager(){} public static void registerService(String key,Object instance){ if(!objMap.containsKey(key)){ objMap.put(key,instance); } } public static Object getService(String key){ return objMap.get(key); } }
优点:可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
No8:
LayoutInflater.from(Context)来获取LayoutInflater服务
Context是抽象类,在Application、Activity、Service中都会存在一个Context对象,即Context的总个数为Activity个数+Service个数+1。
一个Activity的入口是ActivityThread的main函数。
创建Context对象的实现类是ContextImpl
No9:
从ContextImpl类的部分代码中可以看到,在虚拟机第一次加载该类时会注册各种ServiceFetcher,其中就包含了LayoutInflater Service。将这些服务以键值对的形式存储在一个HashMap中,用户使用时只需要根据key来获取到对应的ServiceFetcher,然后通过ServiceFetcher对象的getService函数来获取具体的服务对象。当第一次获取时,会调用ServiceFetcher的createService函数创建服务对象,然后将该对象缓存到一个列表中,下次再取值时直接从缓存中获取,避免重复创建对象,从而达到单例的效果。系统核心服务以单例形式存在,减少了资源消耗。
No10:
LayoutInflater是一个抽象类,PolicyManager实际上是一个代理类,实现了IPolicy接口
真正LayoutInflater的实现类是PhoneLayoutInflater
No11:
Activity的setContentView方法实际上调用的是Window的setContentView,Window是一个抽象类,具体实现类是PhoneWindow
No12:
createView相对比较简单,如果有前缀,那么构造View的完整路径,并且将该类加载到虚拟机中,然后获取该类的构造函数并且缓存起来,再通过构造函数来创建该View的对象,最后将View对象返回,这就是解析单个View的过程。通过rInflate的解析之后,整棵视图树就构建完毕。
No13:
单例模式的缺点:
1)单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现
2)单例对象如果持有Context,那么很容易引发内存泄露,此时需要注意传递给单例对象的Context最好是Application Context
No14:
Window的View层级图