Android源码装饰模式---ContextWrapper

来源:互联网 发布:仿淘宝商品详情轮播 编辑:程序博客网 时间:2024/05/17 09:30

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

装饰模式

意图

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

UML图

decorator

简单代码

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,但那是用来测试的,这个地方也可以看作是用了策略的思想,但正常运行的时候是主要是侧重于不同的装饰。这其实也是装饰的好处。


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

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 我生了爸爸孩子怎么办 三岁宝宝特别犟怎么办 孩子说老师打他怎么办 孩子在幼儿园不合群怎么办 孩与厌学不想学怎么办 1岁宝宝太活泼怎么办 3岁宝宝不爱看书怎么办 6岁不好好吃饭怎么办 二岁宝宝不吃饭怎么办 2岁半宝宝不爱吃饭怎么办 宝宝一岁了不爱吃饭怎么办 一岁多的宝宝不爱吃饭怎么办 小孩不吃饭还吐怎么办 一岁婴儿不吃饭怎么办 写字久了肩膀疼怎么办 6岁儿童不写字怎么办 孩子字写得丑怎么办 6岁儿童怕写字怎么办 高三学生上课困怎么办 高三学生压力大怎么办 数学会做的做错怎么办 脑子很笨反应慢怎么办 好学生考砸了怎么办 孩子写作业范愁怎么办 对粗心的孩子该怎么办 做计算题总出错怎么办 孩子字写得不好怎么办 3岁宝宝数学不好怎么办 十个月的宝宝拉肚子怎么办 刚生的宝宝拉肚子怎么办 我给兔子洗澡了怎么办 刚生的小狗脐带怎么办 宠物兔不想养了怎么办 兔子被打不动了怎么办 ai画板大小会变怎么办 和小孩生肖相冲怎么办 小孩和父母相冲怎么办 4岁宝宝抵抗力差怎么办 5岁儿童抵抗力差怎么办 格力小狗腿歪了怎么办 泰迪后腿骨折了怎么办