Android源码设计模式解析与实战之二单例模式

in Android with 0 comment

定义

确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如:访问IOS和数据库等资源就要考虑使用单例模式。

单例的几种写法

第一种(懒汉, 线程不安全)


public class Singleton {

    private static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}

多线程下不能正常工作

第二种(懒汉线程安全)

public class Singleton {

    private static Singleton instance;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}

懒汉单例模式优点在于单例只有在使用时才会被实例化,再定程度上节约了资源;缺点是第一次加载时需要及时的进行初始化,反应稍慢,还有就是每次调用getInstance()都进行同步,造成不必要的同步开销

第三种(双重校验)

public class Singleton {

    private static Singleton mInstance = null;

    private Singleton() {
    }


    public static Singleton getInstance() {
        if (mInstance == null) {
            synchronized (Singleton.class) {
                if (mInstance == null)
                    mInstance = new Singleton();
            }
        }
        return mInstance;
    }
}

双重校验的优点在于:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。缺点:第一次加载时反应慢,

第四种(静态内部类单例模式)

public class Singleton {

    /**
     * 内部类
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

静态内部类方式不仅能够确保线程安全,对象的唯一性,同时也延迟了单例的实例化。

第五种(枚举单例)

public enum Singleton {
    INSTANCE;

    public void doSomething() {
    }

}

这种方式是Effective Java作者提倡的方式,它不仅能保证多线程同步问题,而且还能防止反序列化重现创建新的对象问题。以上几个单例写法中都会出现反序列化重新创建对象的情形。

第五种(使用容器实现单例模式)

public class Singleton {


    private static Map<String, Object> objMap = new HashMap<>();

    private Singleton() {
    }

    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);
    }

}

在程序初始, 将多种单例类型注入到一个统一的管理类中,在使用是根据key获取对象。这种方式使得我们可以管理多种类型的单例,并且通过统一的接口进行获取操作, 降低了用户的使用成本,也隐藏了具体的实现,降低了耦合度。

LayoutInflater中的单例应用

LayoutInflater 算是经常见的,ListView 中getView()方法中:


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View view = null;
        if (convertView == null) {
            view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
        } else {

        }
        return view;
    }


但我们进入from()方法可看到源码

    /**
     * Obtains the LayoutInflater from the given context.
     */
    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

这时我们看到代码调用getSystemService()方法,LayoutInflater,看getSystemService()实现

public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);

从中我们看到此方法为抽象方法,Context当然也是个抽象类,so我们找到Context的实现类ContextCompl

    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

这时观察看到此方法又通过SystemServiceRegistry去调用,我们继续看getSystemService源码

    /**
     * Gets a system service from a given context.
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }

这时我们来看SYSTEM_SERVICE_FETCHERS定义:

    private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new HashMap<String, ServiceFetcher<?>>();

其实这只是个HashMap用于缓存ServiceFetcher,ServiceFetcher则保存各种Service,通过getService()方法获取系统服务,接着我们看怎么进行注册系统服务的

static {
   //省略

    registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
            new CachedServiceFetcher<ActivityManager>() {
                @Override
                public ActivityManager createService(ContextImpl ctx) {
                    return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
                }});

    registerService(Context.ALARM_SERVICE, AlarmManager.class,
            new CachedServiceFetcher<AlarmManager>() {
                @Override
                public AlarmManager createService(ContextImpl ctx) {
                    IBinder b = ServiceManager.getService(Context.ALARM_SERVICE);
                    IAlarmManager service = IAlarmManager.Stub.asInterface(b);
                    return new AlarmManager(service, ctx);
                }});
    ......
    //注册LayoutInlfater
    registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
            new CachedServiceFetcher<LayoutInflater>() {
                @Override
                public LayoutInflater createService(ContextImpl ctx) {
                    return new PhoneLayoutInflater(ctx.getOuterContext());
                }});

   //省略
}

此类就是通过静态语句块进行注册系统服务,此句在第一次加载时调用且只执行一次,保证实例的唯一性。在看注册服务方法

    /**
     * Statically registers a system service with the context.
     * This method must be called during static initialization only.
     */
    private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }

这里则是通过静态方法注册系统服务缓存到HashMap中,这也就对接上通过getService()获取系统服务。以上就是获取LayoutInflater源码...

Responses