征服安卓面试官路漫漫:从源码深扒一下四大组件和Context


征服安卓面试官路漫漫:从源码深扒一下四大组件和Context文章插图
目录

  • 什么是 Context?
  • 四大组件和 Context
  • Application 和 Context
  • 为什么 Application 的 Context 不可以创建 Dialog ?
  • 未完待续...
文章开头 , 先来看一段代码:
public class ContextActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_context);Log.e("context", "getApplication in Activity: " + getApplication().getClass().getName());Log.e("context", "getApplicationContext in Activity: " + getApplicationContext().getClass().getName());Log.e("context", "getBaseContext in Activity: " + getBaseContext().getClass().getName());startService(new Intent(this,ContextService.class));}}你能准确的说出这三行打印语句的执行结果吗?如果不能 , 你需要认真阅读这篇文章 。
什么是 Context ?Context 是一个抽象类 。 既然是抽象类 , 那么它就代表了一类具体对象的通用特征 。 先来看一下 Context 的类图:
征服安卓面试官路漫漫:从源码深扒一下四大组件和Context文章插图
其中看到了我们很熟悉的 Activity、Service、Application , 这些都是 Context 的具体实现类 , 也就是说 Context 抽象了这些类的通用特征和功能:
  • 获取系统资源 , getResources() , getAssets() 等
  • 启动各种系统组件
  • 获取系统服务
  • ......
这些与系统环境息息相关的功能都是由 Context 提供的 , 所以一般将其称为上下文 , 它其实就是对当前运行环境的具体描述 , 为系统组件的正常运行提供必要的环境和资源 。
在上面的类图中 , 可能有两个读者比较陌生的类 , ContextWraaper 和ContextImpl 。
ContextImpl 很好理解 , 它就是 Context 的具体实现类 。 Context 类中的所有抽象方法都是在 ContextImpl 中实现的 。
class ContextImpl extends Context {......@Overridepublic AssetManager getAssets() {return getResources().getAssets();}@Overridepublic Resources getResources() {return mResources;}@Overridepublic PackageManager getPackageManager() {if (mPackageManager != null) {return mPackageManager;}IPackageManager pm = ActivityThread.getPackageManager();if (pm != null) {// Doesn't matter if we make more than one instance.return (mPackageManager = new ApplicationPackageManager(this, pm));}return null;}@Overridepublic ContentResolver getContentResolver() {return mContentResolver;}@Overridepublic Looper getMainLooper() {return mMainThread.getLooper();}......}ContextWraaper 其实也很简单 , 直接看它的实现代码:
public class ContextWrapper extends Context {@UnsupportedAppUsageContext mBase;public ContextWrapper(Context base) {mBase = base;}/*** 在这个方法中给 mBase 赋值*/protected void attachBaseContext(Context base) {if (mBase != null) {throw new IllegalStateException("Base context already set");}mBase = base;}public Context getBaseContext() {return mBase;}@Overridepublic AssetManager getAssets() {return mBase.getAssets();}@Overridepublic Resources getResources() {return mBase.getResources();}......}这是一个典型的 装饰者模式 , 也叫做 修饰模式 , 以下来自维基百科:
修饰模式 , 是面向对象编程领域中 , 一种动态地往一个类中添加新的行为的设计模式 。 就功能而言 , 修饰模式相比生成子类更为灵活 , 这样可以给某个对象而不是整个类添加一些功能 。
通过使用修饰模式 , 可以在运行时扩充一个类的功能 。 原理是:增加一个修饰类包裹原来的类 , 包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数 。 装饰类实现新的功能 , 但是 , 在不需要用到新功能的地方 , 它可以直接调用原来的类中的方法 。 修饰类必须和原来的类有相同的接口 。
修饰模式是类继承的另外一种选择 。 类继承在编译时候增加行为 , 而装饰模式是在运行时增加行为 。
当有几个相互独立的功能需要扩充时 , 这个区别就变得很重要 。 在有些面向对象的编程语言中 , 类不能在运行时被创建 , 通常在设计的时候也不能预测到有哪几种功能组合 。 这就意味着要为每一种组合创建一个新类 。 相反 , 修饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合 。 一个修饰模式的示例是JAVA里的Java I/O Streams的实现 。
Context 是基本的抽象类 , 无论是实现类 , 还是装饰类 , 都直接或间接的实现它 。 ContextImpl 是 Context 的直接实现类 , 但各个组件并不是直接继承 ContextImpl , 而是通过装饰类 ContextWrapper 来持有 ContextImpl 。 这是为什么呢?对于 Activity 和 Service 来说 , 它们都需要系统上下文运行环境 , 但它们又是不同的 。 Activity 需要显示到前台 , 它有页面 , 它需要主题 , 于是有了继承自 ContextWrapper 的 ContextThemeWrapper , 扩展了功能 , 给 Activity 提供了主题 。 同时 , Activity、Service、Application 这些具体组件本身又扩展出了不同的生命周期功能 。