您的位置:首页 > 移动开发 > Android开发

Android设计模式源码解析之单例模式

2016-01-10 11:58 531 查看

1. 模式介绍

1.1模式的定义:

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

1.2模式的使用场景:

  确保某个类有且只有一个对象的场景,例如创建一个对象需要消耗的资源过多,如要访问 IO 和数据库等资源。如:我们使用的第三方框架(com.nostra13.universalimageloader)

  

2.模式的简单实现

1.1简单实现的介绍:

  单例模式是设计模式中最简单的,只有一个单例类,没有其他的层次结构与抽象。该模式需要确保该类只能生成一个对象,通常是该类需要消耗太多的资源或者没有没有多个实例的理由。例如一个公司只有一个CEO、一台电脑通常只有一个显示器等。

1.2实现源码:

public class CEO{
private static CEO ceo = null;
// 构造私有化
private CEO(){
}

// 方式一饿汉式,这里不做介绍

/**
*  方式二懒汉式:避免并发时创建了多个实例, 该方式不能完全避免并发带来的破坏.
**/
public static CEO getInstance(){
if(ceo == null){
synchronized(CEO.class){
if(ceo == null){
ceo = new CEO();
}
}
}
return ceo;
}

/**
* 方式三 : 在第一次加载SingletonHolder时初始化一次mOnlyInstance对象, 保证唯一性,
* 也延迟了单例的实例化,如果该单例比较耗资源可以使用这种模式.
*/
public static CEO getInstanceFromHolder(){
return CEOHolder.mOnlyInstance;
}

/**
* 静态内部类
*
* @author mrsimple
*
*/
private static class CEOHolder {
private static final CEO mOnlyInstance = new CEO();
}

/**
* 方式四 : 注册到容器, 根据key获取对象.一般都会有多种相同属性类型的对象会注册到一个map中
* instance容器
*/
private static Map<String,Staff> objMap = new HashMap<String,Staff>();
/**
* 注册员工对象到map中
* @param key
* @param instance
*/
public static void registerService(String key, Staff staff) {
if (!objMap.containsKey(key) ) {
objMap.put(key, instance) ;
}
}

/**
* 根据key获取员工对象
* @param key
* @return
*/
public static Staff getStaff(String key) {
return objMap.get(key) ;
}
}


不管以哪种形式实现单例模式,它们的核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例,在这个获取的过程中你必须保证线程安全、反序列化导致重新生成实例对象等问题,该模式简单,但使用率较高。

3.Android源码中的模式实现

在Android系统中,我们经常会通过Context获取系统级别的服务,比如WindowsManagerService, ActivityManagerService等,更常用的是一个叫LayoutInflater的类。这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过Context的getSystemService(String name)获取。我们以LayoutInflater为例来说明, 平时我们使用LayoutInflater较为常见的地方是在ListView的getView方法中。

@Oveeride
public View getView(int position,View convertView,ViewGroup parent){
if(convertView == null){
convertView = LayoutInflater.from(mContext).inflate(R.layout.item,null);
// 其他代码
}
// 获取Holder
// 初始化每项的数据
return convertView;
}


通常我们使用LayoutInflater.from(Context)来获取LayoutInflater服务, 下面我们看看LayoutInflater.from(Context)的实现。

/**
* 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;
}


可以看到from(Context)函数内部调用的是Context类的getSystemService(String key)方法,我们跟踪到Context类看到, 该类是抽象类。

public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.view.LayoutInflater} for inflating layout resources in this
* context.
*
* @see #getSystemService
* @see android.view.LayoutInflater
*/
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";

public abstract Object getSystemService(@ServiceName @NonNull String name);
// 省略
}


使用的getView中使用的Context对象的具体实现类是什么呢 ?其实在Application,Activity, Service,中都会存在一个Context对象,即Context的总个数为 1 + Activity个数 + Service个数。而ListView通常都是显示在Activity中,那么我们就以Activity中的Context来分析。

我们知道,一个Activity的入口是ActivityThread的main函数。在该main函数中创建一个新的ActivityThread对象,并且启动消息循环(UI线程),创建新的Activity、新的Context对象,然后将该Context对象传递给Activity。下面我们看看ActivityThread源码。

public static void main(String[] args) {
SamplingProfilerIntegration.start();

// CloseGuard defaults to true and can be quite spammy.  We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("<pre-initialized>");
// 主线程消息循环
Looper.prepareMainLooper();
// 创建ActivityThread对象
ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

AsyncTask.init();

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}

private void attach(boolean system) {
sThreadLocal.set(this);
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
} else {
// 省略
}
}


在main方法中,我们创建一个ActivityThread对象后,调用了其attach函数,并且参数为false. 在attach函数中, 参数为false的情况下, 会通过Binder机制与ActivityManagerService通信,并且最终调用handleLaunchActivity函数 ( 具体分析请参考老罗的博客 : Activity的启动流程),我们看看该函数的实现 。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 代码省略
Activity a = performLaunchActivity(r, customIntent);
// 代码省略
}

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
// 代码省略
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(         // 1 : 创建Activity
cl, component.getClassName(), r.intent);
// 代码省略
} catch (Exception e) {
// 省略
}

try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);

if (activity != null) {
// 2 : 获取Context对象
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
// 3: 将appContext等对象attach到activity中
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config);

// 代码省略
// 4 : 调用Activity的onCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state);
// 代码省略
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
// 代码省略
}

return activity;
}

private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
// 5 : 创建Context对象, 可以看到实现类是ContextImpl
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);

appContext.setOuterContext(activity);

// 代码省略
return baseContext;
}


通过上面1~5的代码分析可以知道, Context的实现类为ComtextImpl类。我们继续跟踪到ContextImpl类。

class ContextImpl extends Context {

// 代码省略
/**
* Override this class when the system service constructor needs a
* ContextImpl.  Else, use StaticServiceFetcher below.
*/
static class ServiceFetcher {
int mContextCacheIndex = -1;

/**
* Main entrypoint; only override if you don't need caching.
*/
public Object getService(ContextImpl ctx) {
ArrayList<Object> cache = ctx.mServiceCache;
Object service;
synchronized (cache) {
if (cache.size() == 0) {
for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) {
cache.add(null);
}
} else {
service = cache.get(mContextCacheIndex);
if (service != null) {
return service;
}
}
service = createService(ctx);
cache.set(mContextCacheIndex, service);
return service;
}
}

/**
* Override this to create a new per-Context instance of the
* service.  getService() will handle locking and caching.
*/
public Object createService(ContextImpl ctx) {
throw new RuntimeException("Not implemented");
}
}

// 1 : service容器
private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
new HashMap<String, ServiceFetcher>();

private static int sNextPerContextServiceCacheIndex = 0;
// 2: 注册服务器
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}

// 3: 静态语句块, 第一次加载该类时执行 ( 只执行一次, 保证实例的唯一性. )
static {
//  代码省略
// 注册Activity Servicer
registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});

// 注册LayoutInflater service
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
// 代码省略
}

// 4: 根据key获取对应的服务,
@Override
public Object getSystemService(String name) {
// 根据name来获取服务
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}

// 代码省略
}


从ContextImpl类的部分代码中可以看到,在虚拟机第一次加载该类时会注册各种服务,其中就包含了LayoutInflater Service, 将这些服务以键值对的形式存储在一个HashMap中,用户使用时只需要根据key来获取到对应的服务,从而达到单例的效果。这种模式就是上文中提到的“单例模式的实现方式4”。系统核心服务以单例形式存在,减少了资源消耗。

4.杂谈

优点与缺点

优点

  由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。

  由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决;

  单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。

  单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

缺点

  单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: