开发者社区> 异步社区> 正文

《Android 源码设计模式解析与实战》——第2章,第2.7节Android源码中的单例模式

简介:
+关注继续查看

本节书摘来自异步社区《Android 源码设计模式解析与实战》一书中的第2章,第2.7节Android源码中的单例模式,作者 何红辉 , 关爱民,更多章节内容可以访问云栖社区“异步社区”公众号查看

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

@Override
public View getView(int position, View convertView, ViewGroup parent)
  View itemView = null;
  if (convertView == null) {
    itemView = LayoutInflater.from(mContext).inflate(mLayoutId, null);
    // 代码省略
  } else {
    // 代码省略
  }
  // 代码省略
  return itemView;
}
通常我们使用LayoutInflater.from(Context)来获取LayoutInflater服务,下面看看LayoutInflater.from (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 {
  // 省略
}

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

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

public static void main(String[] args) {
// 代码省略
    Process.setArgV0("<pre-initialized>");
    // 主线程消息循环
    Looper.prepareMainLooper();
    // 创建ActivityThread对象
    ActivityThread thread = new ActivityThread();
    thread.attach(false);

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

    AsyncTask.init();
    // 代码省略
    Looper.loop();
  }

  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 {
        // 关联mAppThread
        mgr.attachApplication(mAppThread);
      } catch (RemoteException ex) {
        // 省略
      }
    } else {
        // 省略
    }
}  

在main方法中,我们创建一个ActivityThread对象后,调用了其attach函数,并且参数为false。在attach函数中,参数为false的情况下(即非系统应用),会通过Binder机制与ActivityManager Service通信,并且最终调用handleLaunchActivity函数,我们看看该函数的实现:

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

   private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // 代码省略
    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对象
      Application app = r.packageInfo.makeApplication(false, mInstrumentation);
      if (activity != null) {
        Context appContext = createBaseContextForActivity(r, activity);  
        // 2. 获取Context对象
        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 = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
    appContext.setOuterContext(activity);
    Context baseContext = appContext;
    // 代码省略
    return baseContext;
  }

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

class ContextImpl extends Context {

// 代码省略
// ServiceFetcher通过getService获取服务对象
   static class ServiceFetcher {
    int mContextCacheIndex = -1;

    // 获取系统服务
    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); // 从缓存中获取Service对象
          if (service != null) {
            return service;
          }
        }
        service = createService(ctx);
        cache.set(mContextCacheIndex, service);
        return service;
      }
    }

    /**
     * 子类覆写该方法用以创建服务对象
     */
    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 {
    // 代码省略
    // 注册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类的部分代码中可以看到,在虚拟机第一次加载该类时会注册各种ServiceFatcher,其中就包含了LayoutInflater Service。将这些服务以键值对的形式存储在一个HashMap中,用户使用时只需要根据key来获取到对应的ServiceFetcher,然后通过ServiceFetcher对象的getService函数来获取具体的服务对象。当第一次获取时,会调用ServiceFetcher的createService函数创建服务对象,然后将该对象缓存到一个列表中,下次再取时直接从缓存中获取,避免重复创建对象,从而达到单例的效果。这种模式就是小节中通过容器的单例模式实现方式,系统核心服务以单例形式存在,减少了资源消耗。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
常见设计模式解析和实现(C++)FlyWeight模式
作用:运用共享技术有效地支持大量细粒度的对象   UML结构图:   解析: Flyweight模式在大量使用一些可以被共享的对象的时候使用。
823 0
OKHTTP3源码和设计模式(上篇)
本文来探究一下 OkHttp3 的源码和其中的设计思想。 关于 OkHttp3 的源码分析的文章挺多,不过大多还是在为了源码而源码。
1848 0
《Android程序设计》一1.4 保持版本最新
本节书摘来自华章出版社《Android程序设计》一 书中的第1章,第1.4节,作者:G. Blake Meike, Masumi Nakamura,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
882 0
《设计模式解析(第2版•修订版)》—第1章 1.8节特殊对象方法
所有面向对象语言都会在对象删除时查找并执行相应的析构函数。与构造函数一样,析构函数的使用也是对象“自己负责自己”所要求的。
1213 0
常见设计模式解析和实现(C++)Adapt模式
作用:将一个类的接口转换成客户希望的另一个接口。Adapt模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。     UML示意图 1)      采用继承原有接口类的方式   2)采用组合原有接口类的方式   解析: Adapt模式其实就是把完成同样一个功能但是接口不能兼容的类桥接在一起使之可以在一起工作,这个模式使得复用旧的接口成为可能。
799 0
《设计模式解析(第2版•修订版)》—第1章 1.5节应对需求变更
为了找出解决需求变更问题的办法,弄清功能分解有没有其他替代方法,我们先来看看日常生活中人们是如何做事的。假设你是要在一个会议1上担任讲师,听课的人在课后还要去听其他课,但他们不知道下一堂课的听课地点。你的责任之一,就是确保大家都知道下一堂课去哪里上。
1368 0
《Android程序设计》一1.2 小试牛刀:确认安装是否能够正常工作
本节书摘来自华章出版社《Android程序设计》一 书中的第1章,第1.2节,作者:G. Blake Meike, Masumi Nakamura,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1097 0
Android的UI设计与后台线程交互
本文将讨论Android应用程序的线程模型以及如何使用线程来处理耗时较长的操作,而不是在主线程中执行,保证用户界面(UI)的流畅运行。本文还将阐述一些用户界面(UI)中与线程交互的API。UI用户界面线程 当应用程序启动时,系统会为应用程序创建一个主线程(main)或者叫UI线程,它负责分发事件到不同的组件,包括绘画事件。
704 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
12049
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载