假设说Android源代码中哪个地方装饰模式应用的最明显的话,那肯定是非ContextWrapper莫属了,ContextWrapper是一个透明的经典的装饰模式。本文将通过装饰器模式分析Context源代码结构。本文基于的Android源代码是(android 5.0.0)。

首先先介绍一下装饰模式。

装饰模式

意图

装饰模式动态地给对象加入额外的职责,就加入功能来说,它比子类的方式更加灵活。

UML图

简单代码

class Component{
public void operate1(){
//do operate1
System.out.println("operate1");
}
} class ComponentDecorator extends Component{
private Component mComponent;
public ComponentDecorator(Component component){
mComponent = component;
} public void operate1(){
// 加入相关的职责
System.out.println("print before");
mComponent.operate1();
}
} class ComponentChild{
public void operate1(){
//do operate1
System.out.println("child operate1");
}
} public static void main(String [] args){
ComponentChild component = new ComponentChild();
ComponentDecorator decorator = new ComponnentDecorator(component);
decorator.operate1();
}

上面的代码就简单实现了装饰模式,通过把被装饰的对象作为装饰器的成员变量,当调用装饰器的操作的时候,装饰器还是会调用被装饰对象的操作,可是装饰器能够给相应的操作加入相关功能。

装饰器透明的实现就是跟被装饰对象的接口一致。不加入新的接口,可是如今很多其它的实现方式是半透明的。装饰模式与子类实现方式比,它能够装饰被装饰对象的各种子类,甚至能够装饰装饰器对象(装饰器之间互相装饰),这样比子类化加入功能更加灵活。

假设用子类的方式。将会导致非常多的子类。

ContextWrapper

以下介绍一下Android源代码中装饰器的使用:ContextWrapper(装饰器有个别名Wrapper)。先看UML图。

Context

Context是一个全局应用环境接口,详细的实现由Android系统完毕。它能訪问应用资源,而且能够启动Activity,Service。接收broadcast。

它是一个抽象类,里面包括了各种方法的声明,应该将它作为一个接口类来看待。

ContextWrapper

这是一个Context的包装器,里面包括了一个mBase,ContextWrapper就是包装了mBase。

ContextThemeWrapper, Activity, Application, Service, ReceiverRestrictedContext

ContextThemeWrapper这个是包括主题的装饰器,而Activity是它的子类。

Application, Service, ReceiverRestrictedContext都是ContextWrapper的子类。每个应用都会有一个Application。Android源代码中已经有的Application有EmailApplication,LauncherApplication, Browser等等。

ContextImpl

这个是真正实现Context的类,Context是为应用环境类,它包括了跟环境相关的各种操作:getResource(资源管理。包括了getAssets,layout, string,drawable等等), getPackageManager(包管理器), getContentResolver(用于获取内容模型,比方訪问ContentProvider), startActivity(启动Activity), ***Service(包括一系列Service相关操作,start,bind,unbind,stop), registerBroadcast/unregisterBroadcast/sendBroadcast(Broadcast相关)

这里简介一下这几个关键方法的实现方式

getResource

这是获取资源的接口。得到Resource对象。ContextImpl里面有个mResource成员变量,android里面由mResource去读取资源。

mResource由ResourceManager生成。如今非常多Android资源动态载入是直接通过以下这样的方式实现的。

AssetManager assets = AssetManager.class.newInstance();
try{
Method method = AssetManager.class.getMethod("addAssetPath",String.class);
method.invoke(assets,dexPath); //dexPath 表示动态dex包路径
}catch(Exception e){
e.printStackTrace
}
r = new Resources(assets,getResources.getDisplayMetrics(),getResources.getConfiguration());

只是assets的addAssetPath是一个隐藏函数,须要通过反射去调用。

事实上Android的资源框架大致就是例如以下过程:

- 依据layout。values,drawable等生成相应的ID。保存在R.id文件以下,而Android自带的资源在com.android.R.id里面。assets不会赋予id。

- Android用不同的资源文件夹后缀来适配不同的语言环境。屏幕大小。比方说drawable-en-w360。Android有一个相应的,依照顺序匹配,对于drawable。本着不超过相应手机的參数的最大值来匹配。

Android在打包的时候会将依据这些參数建立一个索引。

- 打包到resources.arsc文件里

执行的时候应用读取过程

- 先去掉跟手机环境全然不匹配的资源文件夹,比方如今是中文环境,则去掉en文件夹

- assets由AssetsManager来訪问。其它相应的资源依据MMC表的顺序,依照索引表一个一个的维度来匹配,筛选。直到找到相应的资源。

假设没有找到则抛出异常。

上述过程假设想要详细了解可參见罗升阳的博客

getPackageManager

返回包管理器,包管理器能够管理安装在系统中的应用程序包相关的各种信息。

能够加入权限,能够查看相应包名的包信息,指定组件的组件信息。安装应用包等等。Android里面不同的应用程序报名绝对不同同样。包就相应了应用程序。所以从这里去理解PackageManager,他就是管理涉及到包管理(应用级相关的信息处理)的管理器。

getContentResolver

返回一个ContentResolver。ContentResolver是用于获取Content模型的,像ContentProvider就是通过ContentResolver来訪问。

Activity操作

Context包括了Activity的启动操作。Activity的启动过程简单来说就是经过以下几个步骤:

1. mMainThread.instrumentation.execStartActivity

mMainThread是ActivityThread类型。它是管理着应用程序主线程的执行,像调度Activity,Broadcast等,instrumentation是一个Instrumentation类型,他是Android測试框架,在这里是为了监控Activity的活动,里面会有一个ActivityMonitor去监控被调用的Activity,用ActivityMonitor也能够等待一个指定的Activity启动。也能够阻止一个Activity的启动。

  通过这样的方式启动Activity主要就是为了监控Activity的启动活动。在測试中将会大实用处。启动完后,也能够检查启动后的返回组件信息。

2. 在execStartActivity的函数中。进行完监控后,就是通过ActivityManagerNative来启动指定的Activity。

int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options); 看到Native事实上就能够想到它是通过一种IPC机制来訪问的了,详细是ActivityManagerProxy通过Binder进行IPC。
  1. 由Binder机制来訪问ActivityManagerService,ActivityManagerService是进行Activity,Service, Broadcast管理的核心服务。
  2. 由ActivityManagerService.startActivity 会经过mStackSupervisor 以及 ActivityStack 进行相关处理
  3. 然后经过ApplicationThread, 最后到主线程的Handler(H)启动Activity

Service

Service除了包括startService,stopService,bindService,unbindService。Service终于也是在ActivityManagerService中管理,可是Service不同的。在ActivityManagerService里面管理的是是ActiveServices。可是终于控制Service的流程还是会到达Handler中

BroadCastReceiver

Context中有sendBroadcast, registerReceiver, unregisterReceiver。相应的都会通过ActivityManagerNative调用ActivityManagerService的相关函数。

broadcastIntent是处理sendBroadcast的, ActivityManagerService中也有registerReceiver, unregisterReceiver。ActivityManagerService里面保存有一个Broadcast的ReceiverList,注冊的时候将Receiver保存起来。发送广播的时候,依据保存的IntentFilter来找到相应Receiver。另外BroadCastReceiver还有sticky,是注冊的时候假设符合已有的sticky广播,则立马发送给Receiver。

 

对于Activity,Service启动流程,能够去參考罗升阳的博客,他的博客是基于早期的Android版本号的,事实上非常多地方还是有非常多变化了。所以假设想了解最新的源代码,建议參考博客,自己去阅读源代码。

以下讨论一下Context这部分用装饰模式的优势

ContextWrapper装饰模式的优点

事实上看了Context的源代码结构。知道他是使用了装饰模式,可是为什么这个部分使用装饰模式呢?在这里装饰模式能够非常好地加入职责。比方遇到Receiver的时候。ReceiverRestrictedContext就能够限制Receiver调用startService。让应用程序的Receiver不能够启动Service。通过实现装饰器,能够提供各种各样的装饰,而且也为Android应用程序组件提供了应用环境。

与继承的方式加入功能比較

我们似乎能够考虑用继承去实现Context结构,可是假设用继承的方式给Context加入功能,则将Application, ThemeContext,Service等等直接集成自ContextImpl,假如我们须要一种新的Context实现方式,则会变得非常麻烦,新的ContextImpl实现了后。Application又得去继承新的ContextImpl。一方面麻烦,另外一方面导致子类树集成非常庞大。如今用装饰模式,直接将ContextWrapper装饰的对象替换掉就能够了。

策略模式

我们的Context是须要不断在外面加入新的职责装饰的。而策略模式是将系统的内部替换掉,假如说我们须要的是多种Context的实现,则能够採用策略模式。Context是Android应用环境,在Android中。Android的应用环境是确定了的,它在对不同的应用程序。Android的应用环境是不会变化的。

只是话说回来,装饰模式能够加入不同的实现,Android里面也有MockContext。但那是用来測试的,这个地方也能够看作是用了策略的思想。但正常执行的时候是主要是側重于不同的装饰。这事实上也是装饰的优点。


纵然伤心。也不要愁眉不展,由于你不知是谁会爱上你的笑容 –泰戈尔《飞鸟集》

Android源代码装饰模式---ContextWrapper的更多相关文章

  1. 《Android源代码设计模式解析》读书笔记——Android中你应该知道的设计模式

    断断续续的,<Android源代码设计模式解析>也看了一遍.书中提到了非常多的设计模式.可是有部分在开发中见到的几率非常小,所以掌握不了也没有太大影响. 我认为这本书的最大价值有两点,一个 ...

  2. 在线阅读android源代码

    这两天一直在寻找android系统的源代码,但是直到一个小时之前,一直未能如愿.但是,令人欣慰的是,现在找到了. 网上有不少帖子介绍如何下载android源代码,包括在linux系统,windows系 ...

  3. 1、android源代码下载及目录分析,和eclipser的跟踪

    1.在eclipse中跟踪源代码:假如对mainactivity.java里面的activity按Ctrl+鼠标左键(前提已经导入android源代码:方法1:在项目点击右键,然后找到properti ...

  4. Android源代码结构分析

    Google提供的Android包含了:Android源代码,工具链,基础C库,仿真环境,开发环境等,完整的一套.第一级别的目录和文件如下所示:----------------├── Makefile ...

  5. Android 源码获取-----在Windows环境下通过Git得到Android源代码

    在学习Android的过程中,深入其源代码研究对我们来说是非常重要的,这里将介绍如何通过在Windows环境下使用Git来得到我们的Android源代码. 1.首先确保你电脑上安装了Git,这个通过  ...

  6. MTK6577 Android源代码目录

    MTK6577 Android源代码目录 1.     MTKAndroid4.0 源代码目录 (1)  makeMtk 整个工程编译或是构建(make/build)的入口. (2)  abi 应用程 ...

  7. Android 源代码自动编译packages/apps

    /*************************************************************************** * Android 源代码自动编译packag ...

  8. 1、android源代码下载与跟踪

     学习Android源代码的目的 理解Android API查找API(Activity.Content Provider等) 高级应用开发(ROM定制)  在不同平台下载Android源代码 W ...

  9. Windows下载Android源代码

    下载msysgit,安装 官方下载:http://code.google.com/p/msysgit/downloads/list, 打开Git Bash,运行命令 cd D: git clone h ...

随机推荐

  1. 初见Python<5>:条件、循环和其他语句

    1.使用逗号输出 使用逗号隔开,可以打印多个表达式.打印后,各项之间自动以一个空格隔开. 也可以同时输出文本和变量值. 可以和字符串连接符“+”一起使用.   2.从模块中导入函数 从模块导入函数的方 ...

  2. 某5道CF水题

    1.PolandBall and Hypothesis 题面在这里! 大意就是让你找一个m使得n*m+1是一个合数. 首先对于1和2可以特判,是1输出3,是2输出4. 然后对于其他所有的n,我们都可以 ...

  3. ARC-100 D - Equal Cut

    题面在这里! 我们枚举一下第2和第3段的分界点,显然这种情况下 第1与第2  和  第3与第4  之间的分界点都只有两种情况可能最优,吧这四种情况讨论一下就好了. 两边的分界点可以单调扫过去... # ...

  4. 【差分约束系统】【最短路】【spfa】CDOJ1646 穷且益坚, 不坠青云之志。

    求一个有n个元素的数列,满足任意连续p个数的和不小于s, 任意连续q个数的和不大于t. 令sum[i]表示前i项的和(0<=i<=n,sum[0]=0) 那么题目的条件可转化为: sum[ ...

  5. Linux下KVM的图形界面管理工具(virt-manager)(桌面版)

    背景: virt-manager是用于管理KVM虚拟环境的主要工具,virt-manager默认设置下需要使用root用户才能够使用该工具.当你想在KVM hypervisor服务器上托管虚拟机,由最 ...

  6. mysql mapper 大于小于号 tag name expected

    xml文件的某些特殊字符是自动转义的, 我们不希望它被转义,可以用<![CDATA[]]>, xml解析器会忽视CDATA中的内容, 比如:<![CDATA[<]]> 或 ...

  7. Objective-C]入门 (xcode helloworld程序 创建类

    一:objective-c简介 Objective-C是进行iPhone软件开发的语言 Objective-C语言是C语言的一个扩展集 Objective-C是一种面向对象的语言 大小写敏感 程序语句 ...

  8. 粗览Activiti Modeler操作和源代码

    Activiti Model Editor组件 我的 了解ActivitiExplorer及其Vaadin实现方式博文里提到ActivitiExplorer使用的是Vaadin架构,但是Activit ...

  9. mongo 误操作恢复数据

    场景:我往同一个集合里面插入 三条数据  aa:aa  bb:bb  cc:cc .后来我后悔了,不想插入 bb:bb,通过oplog重放过滤好 bb:bb这条数据. 原理: 1.通过 oplog.r ...

  10. texture 资源 shader资源

    tex:create or load的时候 开显存 bindtex的时候把address送过去 shader 有两部分 compile之前和之后的  compile之前是比如hlsl 编完是二进制文件 ...