??xml version="1.0" encoding="utf-8" standalone="yes"?> 介:(x) Spring
作ؓ(f)现在最优秀的框架之一Q已被广泛的使用Qƈ且有很多对其分析的文章。本文将从另外一个视角试囑։析出 Spring 框架的作者设?Spring
框架的骨骼架构的设计理念Q有那几个核?j)组ӞZ么需要这些组Ӟ它们又是如何l合在一h?Spring 的骨骼架构? Spring ?
AOP Ҏ(gu)又是如何利用这些基的骨骼架构来工作的? Spring
中又使用?jin)那些设计模式来完成它的q种设计的?它的q种设计理念对对我们以后的Y件设计有何启C?本文详l解{这些问题?/p>
Spring d有十几个lgQ但是真正核?j)的lg只有几个Q下面是 Spring 框架的M架构图:(x) 从上图中可以看出 Spring 框架中的核心(j)lg只有三个QCore、Context ?Beans。它们构v?jin)整?Spring 的骨骼架构。没有它们就不可能有 AOP、Web {上层的Ҏ(gu)功能。下面也主要从q三个组件入手分?Spring?/p>
前面介绍?Spring 的三个核?j)组Ӟ如果再在它们三个中选出核心(j)的话Q那非 Beans lg莫属?jin),Zq样_(d)其实 Spring
是面向 Bean 的编E(BOP,Bean Oriented ProgrammingQ,Bean ?Spring 中才是真正的主角?/p>
Bean ?Spring 中作用就?Object ?OOP 的意义一P没有对象的概念就像没有面向对象编E,Spring 中没?
Bean 也就没有 Spring 存在的意义。就像一ơ演?gu)台都准备好?jin)但是却没有演员一栗ؓ(f)什么要 Bean q种角色 Bean 或者ؓ(f)何在
Spring 如此重要Q这?Spring 框架的设计目标决定,Spring Z如此行Q我们用 Spring 的原因是什么,x你会(x)发现原来
Spring 解决?jin)一个非常关键的问题他可以让你把对象之间的依赖关p{而用配置文g来管理,也就是他的依赖注入机制。而这个注入关pd一个叫
Ioc 容器中管理,?Ioc 容器中有又是什么就是被 Bean 包裹的对象。Spring 正是通过把对象包装在 Bean
中而达到对q些对象理以及(qing)一些列额外操作的目的?/p>
它这U设计策略完全类g Java 实现 OOP 的设计理念,当然?Java 本n的设计要?Spring
复杂太多太多Q但是都是构Z个数据结构,然后Ҏ(gu)q个数据l构设计他的生存环境Qƈ让它在这个环境中按照一定的规律在不停的q动Q在它们的不停运动中?
计一pd与环境或者与其他个体完成信息交换。这h来回q头x我们用到的其他框枉是大慨类似的设计理念?/p>
前面?Bean ?Spring 中关键因素,?Context ?Core 又有何作用呢Q前面吧 Bean
比作一场演Z的演员的话,?Context 是q场演出的舞台背景,?Core
应该是演出的道具了(jin)。只有他们在一h能具备能演出一场好戏的最基本的条件。当然有最基本的条件还不能使这场演?gu)颖而出Q还要他表演的节目够的_?
彩,q些节目是 Spring 能提供的特色功能?jin)?/p>
我们知道 Bean 包装的是 ObjectQ?Object 必然有数据,如何l这些数据提供生存环境就?Context 要解决的问题Q对
Context 来说他就是要发现每个 Bean 之间的关p,为它们徏立这U关pdƈ且要l护好这U关pR所?Context 是一?Bean
关系的集合,q个关系集合又叫 Ioc 容器Q一旦徏立vq个 Ioc 容器?Spring 可以ؓ(f)你工作了(jin)。那 Core
lg又有什么用武之地呢Q其?Core 是发现、徏立和l护每个 Bean 之间的关pL需要的一些列的工P从这个角度看来,Core q个lg?
Util 更能让你理解?/p>
它们之间可以用下图来表示Q?/p>
q里详l介l每个组件内部类的层ơ关p,以及(qing)它们在运行时的时序顺序。我们在使用 Spring 是应该注意的地方?/p>
Bean lg 前面已经说明?Bean lg?Spring 的重要性,下面看看 Bean q个lg式怎么设计的。Bean lg?Spring ?
org.springframework.beans 包下。这个包下的所有类主要解决?jin)三件事QBean 的定义、Bean 的创Z?qing)?Bean
的解析。对 Spring 的用者来说唯一需要关?j)的?Bean 的创建,其他两个?Spring 在内部帮你完成了(jin)Q对你来说是透明的?/p>
Spring Bean 的创建时典型的工厂模式,他的接口?BeanFactoryQ下图是q个工厂的承层ơ关p:(x) BeanFactory 有三个子c:(x)ListableBeanFactory、HierarchicalBeanFactory ?
AutowireCapableBeanFactory。但是从上图中我们可以发现最l的默认实现cL
DefaultListableBeanFactoryQ他实现?jin)所有的接口。那Z要定义这么多层次的接口呢Q查阅这些接口的源码和说明发玎ͼ每个接口
都有他用的场合Q它主要是ؓ(f)?jin)区分?Spring 内部在操作过E中对象的传递和转化q程中,对对象的数据讉K所做的限制。例?
ListableBeanFactory 接口表示q些 Bean 是可列表的,?HierarchicalBeanFactory 表示的是q些
Bean 是有l承关系的,也就是每?Bean 有可能有?Bean。AutowireCapableBeanFactory 接口定义 Bean
的自动装配规则。这四个接口共同定义?Bean 的集合、Bean 之间的关pR以?Bean 行ؓ(f)?/p>
Bean 的定义主要有 BeanDefinition 描述Q如下图说明?jin)这些类的层ơ关p:(x) Bean 的定义就是完整的描述?jin)?Spring 的配|文件中你定义的 <bean/> 节点中所有的信息Q包括各U子节点。当
Spring 成功解析你定义的一?<bean/> 节点后,?Spring 的内部他p转化?BeanDefinition
对象。以后所有的操作都是对这个对象完成的?/p>
Bean 的解析过E非常复杂,功能被分的很l,因ؓ(f)q里需要被扩展的地方很多,必须保证有够的灉|性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文g的解析。这个解析过E主要通过下图中的cd成:(x) 当然q有具体?tag 的解析这里ƈ没有列出?/p>
Context lg Context ?Spring ?org.springframework.context 包下Q前面已l讲解了(jin) Context lg?
Spring 中的作用Q他实际上就是给 Spring 提供一个运行时的环境,用以保存各个对象的状态。下面看一下这个环境是如何构徏的?/p>
ApplicationContext ?Context 的顶U父c,他除?jin)能标识一个应用环境的基本信息外,他还l承?jin)五个接口,q五个接口主要是扩展?Context 的功能。下面是 Context 的类l构图:(x) Q查??7 的清晰版?/font>。)(j) 从上图中可以看出 ApplicationContext l承?BeanFactoryQ这也说明了(jin) Spring 容器中运行的M对象?
BeanQ另?ApplicationContext l承?ResourceLoader 接口Q?ApplicationContext
可以讉KCQ何外部资源,q将?Core 中详l说明?/p>
ApplicationContext 的子cM要包含两个方面:(x) 再往下分是按照构徏 Context 的文件类型,接着是讉K Context 的方式。这样一U一U构成了(jin)完整?Context {层次?/p>
M来说 ApplicationContext 必须要完成以下几件事Q?/p>
Context 作ؓ(f) Spring ?Ioc 容器Q基本上整合?Spring 的大部分功能Q或者说是大部分功能的基?/p>
Core lg Core lg作ؓ(f) Spring 的核?j)组Ӟ他其中包含?jin)很多的关键类Q其中一个重要组成部分就是定义了(jin)资源的访问方式。这U把所有资源都抽象成一个接口的方式很值得在以后的设计中拿来学?fn)。下面就重要看一下这个部分在 Spring 的作用?/p>
下图?Resource 相关的类l构图:(x) Q查??8 的清晰版?/font>。)(j) 从上囑֏以看?Resource
接口装?jin)各U可能的资源cdQ也是对用者来说屏蔽了(jin)文gcd的不同。对资源的提供者来_(d)如何把资源包装v来交l其他h用这也是一个问题,我们看到
Resource 接口l承?InputStreamSource 接口Q这个接口中有个 getInputStream Ҏ(gu)Q返回的?
InputStream cR这h有的资源都被可以通过 InputStream
q个cL获取Q所以也屏蔽?jin)资源的提供者。另外还有一个问题就是加载资源的问题Q也是资源的加载者要l一Q从上图中可以看?gu)个Q务是?
ResourceLoader 接口完成Q他屏蔽?jin)所有的资源加蝲者的差异Q只需要实现这个接口就可以加蝲所有的资源Q他的默认实现是
DefaultResourceLoader?/p>
下面看一?Context ?Resource 是如何徏立关pȝQ首先看一下他们的cdpdQ?/p>
从上囑֏以看出,Context 是把资源的加载、解析和描述工作委托l了(jin) ResourcePatternResolver cL完成Q他相当于一个接头hQ他把资源的加蝲、解析和资源的定义整合在一起便于其他组件用。Core lg中还有很多类似的方式?/p>
Ioc 容器如何工作 前面介绍?Core lg、Bean lg?Context lg的结构与怺关系Q下面这里从使用者角度看一下他们是如何q行的,以及(qing)我们如何?Spring 完成各种功能QSpring 到底能有那些功能Q这些功能是如何得来的,下面介绍?/p>
如何创徏 BeanFactory 工厂 正如?2 描述的那PIoc 容器实际上就?Context lgl合其他两个lg共同构徏?jin)一?Bean
关系|,如何构徏q个关系|?构徏的入口就?AbstractApplicationContext cȝ refresh
Ҏ(gu)中。这个方法的代码如下Q?/p>
q个Ҏ(gu)是构徏整个 Ioc 容器q程的完整的代码Q了(jin)解了(jin)里面的每一行代码基本上׃(jin)解大部分 Spring 的原理和功能?jin)?/p>
q段代码主要包含q样几个步骤Q?/p>
下面q合代码分析这几个q程?/p>
W二三句是在创建和配置 BeanFactory。这里是 refresh 也就是刷新配|,前面介绍?Context
有可更新的子c,q里正是实现q个功能Q当 BeanFactory 已存在是更斎ͼ如果没有新创徏。下面是更新 BeanFactory
的方法代码:(x) q个Ҏ(gu)实现?AbstractApplicationContext 的抽象方?
refreshBeanFactoryQ这D代码清楚的说明?BeanFactory 的创E。注?BeanFactory
对象的类型的变化Q前面介l了(jin)他有很多子类Q在什么情况下使用不同的子c这非常关键。BeanFactory 的原始对象是
DefaultListableBeanFactoryQ这个非常关键,因ؓ(f)他设计到后面对这个对象的多种操作Q下面看一下这个类的承层ơ类图:(x) Q查??10 的清晰版?/font>。)(j) 从这个图中发现除?BeanFactory 相关的类外,q发C(jin)?Bean ?register 相关。这?
refreshBeanFactory Ҏ(gu)中有一?loadBeanDefinitions(beanFactory)
找到答案,q个Ҏ(gu)开始加载、解?Bean 的定义,也就是把用户定义的数据结构{化ؓ(f) Ioc 容器中的特定数据l构?/p>
q个q程可以用下面时序图解释Q?/p>
Q查??11 的清晰版?/font>。)(j) Bean 的解析和登记程时序囑֦下:(x) Q查??12 的清晰版?/font>。)(j) 创徏?BeanFactory 后,接下L加一?Spring 本n需要的一些工L(fng)Q这个操作在 AbstractApplicationContext ?prepareBeanFactory Ҏ(gu)完成?/p>
AbstractApplicationContext 中接下来的三行代码对 Spring
的功能扩展性v?jin)至关重要的作用。前两行主要是让你现在可以对已经构徏?BeanFactory 的配|做修改Q后面一行就是让你可以对以后再创?
Bean 的实例对象时d一些自定义的操作。所以他们都是扩展了(jin) Spring 的功能,所以我们要学习(fn)使用 Spring 必须对这一部分搞清楚?/p>
其中?invokeBeanFactoryPostProcessors Ҏ(gu)中主要是获取实现 BeanFactoryPostProcessor 接口的子cRƈ执行它的 postProcessBeanFactory Ҏ(gu)Q这个方法的声明如下Q?/p>
它的参数?beanFactoryQ说明可以对 beanFactory 做修改,q里注意q个 beanFactory ?
ConfigurableListableBeanFactory cd的,q也印证?jin)前面介l的不同 BeanFactory
所使用的场合不同,q里只能是可配置?BeanFactoryQ防止一些数据被用户随意修改?/p>
registerBeanPostProcessors Ҏ(gu)也是可以获取用户定义的实C(jin) BeanPostProcessor
接口的子c,q执行把它们注册?BeanFactory 对象中的 beanPostProcessors
变量中。BeanPostProcessor
中声明了(jin)两个Ҏ(gu)QpostProcessBeforeInitialization、postProcessAfterInitialization
分别用于?Bean 对象初始化时执行。可以执行用戯定义的操作?/p>
后面的几行代码是初始化监听事件和对系l的其他监听者的注册Q监听者必L ApplicationListener 的子cR?/p>
如何创徏 Bean 实例q构?Bean 的关pȝ 下面是 Bean 的实例化代码Q是?finishBeanFactoryInitialization Ҏ(gu)开始的?/p>
从上面代码中可以发现 Bean 的实例化是在 BeanFactory 中发生的。preInstantiateSingletons Ҏ(gu)的代码如下:(x) q里出现?jin)一个非帔R要的 Bean —?FactoryBeanQ可以说 Spring 一大半的扩展的功能都与q个 Bean
有关Q这是个Ҏ(gu)?Bean 他是个工?BeanQ可以?Bean ?BeanQ这里的产生 Bean 是指 Bean
的实例,如果一个类l承 FactoryBean 用户可以自己定义产生实例对象的方法只要实C?getObject Ҏ(gu)。然而在 Spring
内部q个 Bean 的实例对象是 FactoryBeanQ通过调用q个对象?getObject Ҏ(gu)p获取用户自定义生的对象Q从而ؓ(f)
Spring 提供?jin)很好的扩展性。Spring 获取 FactoryBean 本n的对象是在前面加?& 来完成的?/p>
如何创徏 Bean 的实例对象以?qing)如何构?Bean 实例对象之间的关联关pd Spring 中的一个核?j)关键,下面是这个过E的程图?/p>
Q查??13 的清晰版?/font>。)(j) 如果是普通的 Bean q接创Z的实例,是通过调用 getBean Ҏ(gu)。下面是创徏 Bean 实例的时序图Q?/p>
Q查??14 的清晰版?/font>。)(j) q有一个非帔R要的部分是建立 Bean 对象实例之间的关p,q也?Spring 框架的核?j)竞争力Q何时、如何徏立他们之间的关系L(fng)下面的时序图Q?/p>
Q查??15 的清晰版?/font>。)(j) Ioc 容器的扩展点 现在q有一个问题就是如何让q些 Bean 对象有一定的扩展性,是可以加入用户的一些操作。那么有哪些扩展点呢Q?Spring 又是如何调用到这些扩展点的? ?Spring ?Ioc 容器来说Q主要有q么几个。BeanFactoryPostProcessorQ?
BeanPostProcessor。他们分别是在构?BeanFactory 和构?Bean 对象时调用。还有就?
InitializingBean ?DisposableBean 他们分别是在 Bean
实例创徏和销毁时被调用。用户可以实现这些接口中定义的方法,Spring ׃(x)在适当的时候调用他们。还有一个是 FactoryBean
他是个特D的 BeanQ这?Bean 可以被用h多的控制?/p>
q些扩展炚w常也是我们使用 Spring 来完成我们特定Q务的地方Q如何精?Spring q你有没有掌握?Spring 有哪些扩展点Qƈ且如何用他们,要知道如何用他们就必须?jin)解他们内在的机理。可以用下面一个比L解释?/p>
我们?Ioc
容器比作一个箱子,q个子里有若干个球的模子,可以用这些模子来造很多种不同的球Q还有一个造这些球模的机器Q这个机器可以生球模。那么他们的对应?
pd?BeanFactory 是那个造球模的机器Q球模就?BeanQ而球模造出来的球就?Bean
的实例。那前面所说的几个扩展点又在什么地方呢Q?BeanFactoryPostProcessor
对应到当造球模被造出来时Q你有Z(x)可以对其做出讑ֽ的修正,也就是他可以帮你修改球模。?InitializingBean ?
DisposableBean 是在球模造球的开始和l束阶段Q你可以完成一些预备和扫尾工作。BeanPostProcessor
可以让你对球模造出来的球做出适当的修正。最后还有一?
FactoryBeanQ它可是一个神奇的球模。这个球模不是预先就定型?jin),而是׃来给他确定它的ŞӞ既然你可以确定这个球模型的ŞӞ当然他造出?
的球肯定是你想要的球了(jin)Q这样在q个子里尼可以发现所有你惌的球 Ioc 容器如何为我所?/strong> 前面的介l了(jin) Spring 容器的构E,?Spring 能ؓ(f)我们做什么,Spring ?Ioc 容器又能做什么呢Q我们?
Spring 必须要首先构?Ioc 容器Q没有它 Spring 无法工作QApplicatonContext.xml 是 Ioc
容器的默认配|文ӞSpring 的所有特性功能都是基于这?Ioc 容器工作的,比如后面要介l的 AOP?/p>
Ioc 它实际上是Z构徏?jin)一个魔方,Spring
Z搭好?jin)骨骼架构,q个方到底能变Z么好的东西出来,q必要有你的参与。那我们怎么参与Q这是前面说的要了(jin)?Spring
中那有些扩展点,我们通过实现那些扩展Ҏ(gu)改变 Spring 的通用行ؓ(f)。至于如何实现扩展点来得到我们想要的个性结果,Spring
中有很多例子Q其?AOP 的实现就?Spring 本n实现?jin)其扩展?gu)辑ֈ?jin)它惌的特性功能,可以拿来参考?/p>
要了(jin)?Spring ?AOP 必d?jin)解的动态代理的原理Q因?AOP 是Z动态代理实现的。动态代理还要从 JDK 本n说v?/p>
?Jdk ?java.lang.reflect 包下有个 Proxy c,它正是构造代理类的入口。这个类的结构入下:(x) 从上囑֏现最后面四个是公有方法。而最后一个方?newProxyInstance 是创徏代理对象的方法。这个方法的源码如下Q?/p>
q个Ҏ(gu)需要三个参敎ͼ(x)ClassLoaderQ用于加载代理类?Loader c,通常q个 Loader 和被代理的类是同一?
Loader
cRInterfacesQ是要被代理的那些那些接口。InvocationHandlerQ就是用于执行除?jin)被代理接口中方法之外的用户自定义的操作Q?
他也是用户需要代理的最l目的。用戯用目标方法都被代理到 InvocationHandler cM定义的唯一Ҏ(gu) invoke
中。这在后面再详解?/p>
下面q是看看 Proxy 如何产生代理cȝq程Q他构造出来的代理cd底是什么样子?下面揭晓啦?/p>
其实从上图中可以发现正在构造代理类的是?ProxyGenerator ?generateProxyClass 的方法中。ProxyGenerator cd sun.misc 包下Q感兴趣的话可以看看他的源码?/p>
假如有这样一个接口,如下Q?/p>
代理来生成的cȝ构如下:(x) q个cM的方法里面将?x)是调?InvocationHandler ?invoke Ҏ(gu)Q而每个方法也对应一个属性变量,q个属性变?m 也将传给 invoke Ҏ(gu)中的 Method 参数。整个代理就是这样实现的?/p>
从前面代理的原理我们知道Q代理的目的是调用目标方法时我们可以转而执?InvocationHandler cȝ invoke Ҏ(gu)Q所以如何在 InvocationHandler 上做文章是 Spring 实现 Aop 的关键所在?/p>
Spring ?Aop 实现是遵?Aop 联盟的约定。同?Spring 又扩展了(jin)它,增加?jin)?Pointcut、Advisor {一些接口得更加灵zR?/p>
下面?Jdk 动态代理的cdQ?/p>
上图清楚的显CZ(jin) Spring 引用?Aop Alliance 定义的接口。姑且不讨论 Spring 如何扩展 Aop
AllianceQ先看看 Spring 如何实现代理cȝQ要实现代理cd Spring 的配|文件中通常是这样定一?Bean 的,如下Q?/p>
配置上看到要讄被代理的接口Q和接口的实现类也就是目标类Q以?qing)拦截器也就在执行目标方法之前被调用Q这?Spring 中定义的各种各样的拦截器Q可以选择使用?/p>
下面看看 Spring 如何完成?jin)代理以及(qing)是如何调用拦截器的?/p>
前面提到 Spring Aop 也是实现其自w的扩展Ҏ(gu)完成q个Ҏ(gu)的Q从q个代理cd以看出它正是l承?FactoryBean ?
ProxyFactoryBeanQFactoryBean 之所以特别就在它可以让你自定义对象的创徏Ҏ(gu)。当然代理对象要通过 Proxy
cL动态生成?/p>
下面?Spring 创徏的代理对象的时序图:(x) Spring 创徏?jin)代理对象后Q当你调用目标对象上的方法时Q将都会(x)被代理到 InvocationHandler cȝ invoke
Ҏ(gu)中执行,q在前面已经解释。在q里 JdkDynamicAopProxy cdC(jin) InvocationHandler 接口?/p>
下面再看?Spring 是如何调用拦截器的,下面是这个过E的时序图:(x) 以上所说的都是 Jdk 动态代理,Spring q支持一U?CGLIB cM理,感兴自q吧?/p>
Spring 中用的设计模式也很多,比如工厂模式、单例模式、模版模式等Q在?Webx 框架的系l架构与设计模式》、?Tomcat 的系l架构与模式设计分析》已l有介绍Q这里就不赘qC(jin)。这里主要介l代理模式和{略模式?/p>
代理模式原理 代理模式是l某一个对象创Z个代理对象,而由q个代理对象控制对原对象的引用,而创个代理对象就是可以在调用原对象是可以增加一些额外的操作。下面是代理模式的结构:(x) Spring 中如何实C理模?/strong> Spring Aop ?Jdk 动态代理就是利用代理模式技术实现的。在 Spring 中除?jin)实现被代理对象的接口外Q还?x)?
org.springframework.aop.SpringProxy ?
org.springframework.aop.framework.Advised 两个接口。Spring 中用代理模式的l构囑֦下:(x) $Proxy 是创徏的代理对象,?Subject 是抽象主题,代理对象是通过 InvocationHandler 来持有对目标对象的引用的?/p>
Spring 中一个真实的代理对象l构如下Q?/p>
{略模式原理 {略模式思义是做某事的{略Q这在编E上通常是指完成某个操作可能有多U方法,q些Ҏ(gu)各有千秋Q可能有不同的适应的场合,然而这些操作方法都有可能用到。各一个操作方法都当作一个实现策略,使用者可能根据需要选择合适的{略?/p>
下面是策略模式的l构Q?/p>
Spring 中策略模式的实现 Spring 中策略模式用有多个地方Q如 Bean 定义对象的创Z?qing)代理对象的创徏{。这里主要看一下代理对象创建的{略模式的实现?/p>
前面已经?jin)?Spring 的代理方式有两个 Jdk 动态代理和 CGLIB 代理。这两个代理方式的用正是用了(jin){略模式。它的结构图如下所C:(x) 在上面结构图中与标准的策略模式结构稍微有点不同,q里抽象{略?AopProxy 接口QCglib2AopProxy ?
JdkDynamicAopProxy 分别代表两种{略的实现方式,ProxyFactoryBean 是代表 Context
角色Q它Ҏ(gu)条g选择使用 Jdk 代理方式q是 CGLIB 方式Q而另外三个类主要是来负责创徏具体{略对象QProxyFactoryBean
是通过依赖的方法来兌具体{略对象的,它是通过调用{略对象?getProxy(ClassLoader classLoader)
Ҏ(gu)来完成操作?/p>
本文通过?Spring 的几个核?j)组件入手,试图扑և构?Spring 框架的骨骼架构,q而分?Spring
在设计的一些设计理念,是否从中扑և一些好的设计思想Q对我们以后E序设计能提供一些思\。接着再详l分析了(jin) Spring
中是如何实现q些理念的,以及(qing)在设计模式上是如何用的?/p>
通过分析 Spring l我一个很大的启示是其这套设计理念其实对我们有很强的借鉴意义Q它通过抽象复杂多变的对象,q一步做规范Q然后根据它定义的这套规范设计出一个容器,容器中构建它们的复杂关系Q其实现在有很多情况都可以用q种cM的处理方法?/p>
虽然我很x我对 Spring 的想法完全阐q清楚,但是所?#8220;书不言Q言不尽意?#8221;Q有什么不Ҏ(gu)者不清楚的地方大家还是看看其源码吧?/p>
在网上看?jin)很多有兛_列化的文
章,我自׃写了(jin)两篇Q现在感觉这些文章都没有很好的把序列化说清楚(包括我自己在?Q所以在此我ȝ前h以及(qing)自己的经验,用更显易懂的语a来描q?
该机Ӟ当然Q仍然会(x)有不好的地方Q希望你看后可以指出Q作Z名程序员应该h不断探烦(ch)的精和强烈的求知欲望! 序列化概qͼ(x)
单来说序列化是一U用来处理对象流的机Ӟ所谓对象流也就是将对象的内容进行流化,的概念q里不用多说(是I/O)Q我们可以对化后的对象q行
d操作Q也可将化后的对象传输于网l之?注:(x)要想对象传输于|络必须q行化)Q在对对象流q行d操作时会(x)引发一些问题,而序列化机制正是用来
解决q些问题的! 问题的引出:(x)
如上所qͼd对象?x)有什么问题呢Q比如:(x)我要对象写入一个磁盘文件而后再将其读出来?x)有什么问题吗Q别急,其中一个最大的问题是对象引用QD个例?
来说Q假如我有两个类Q分别是A和BQBcM含有一个指向Acd象的引用Q现在我们对两个c进行实例化{ A a = new A(); B b =
new B();
}Q这时在内存中实际上分配?jin)两个空_(d)一个存储对象aQ一个存储对象bQ接下来我们惛_它们写入到磁盘的一个文件中去,在写入文g时出C(jin)问题Q因?
对象b包含对对象a的引用,所以系l会(x)自动的将a的数据复制一份到b中,q样的话当我们从文g中恢复对象时(也就是重新加载到内存?Ӟ内存分配?jin)三?
I间Q而对象a同时在内存中存在两䆾Q想一惛_果吧Q如果我想修改对象a的数据的话,那不是还要搜索它的每一份拷贝来辑ֈ对象数据的一致性,q不是我们所
希望的! 以下序列化机制的解决Ҏ(gu)Q?/p>
1.保存到磁盘的所有对象都获得一个序列号(1, 2, 3{等) 2.当要保存一个对象时Q先(g)查该对象是否被保存了(jin)?/p>
3.如果以前保存q,只需写入"与已l保存的h序列号x的对象相?的标讎ͼ否则Q保存该对象 通过以上的步骤序列化机制解决?jin)对象引用的问题Q?/p>
序列化的实现Q?/p>
需要被序列化的cd现Serializable接口Q该接口没有需要实现的Ҏ(gu)Qimplements
Serializable只是Z(jin)标注该对象是可被序列化的Q然后用一个输出流(如:(x)FileOutputStream)来构造一?
ObjectOutputStream(对象?对象Q接着Q用ObjectOutputStream对象的writeObject(Object
obj)Ҏ(gu)可以将参数为obj的对象写?即保存其状?Q要恢复的话则用输入?/p>
例子Q?/p>
import java.io.*; public class Test class Employee implements Serializable class Manager extends Employee 修改默认的序列化机制Q?nbsp; ?
序列化的q程中,有些数据字段我们不想其序列化,对于此类字段我们只需要在定义时给它加上transient关键字即可,对于transient字段?
列化机制?x)蟩q不?x)将其写入文Ӟ当然也不可被恢复。但有时我们惛_某一字段序列化,但它在SDK中的定义却是不可序列化的cdQ这L(fng)话我们也必须把他
标注为transientQ可是不能写入又怎么恢复呢?好在序列化机制ؓ(f)包含q种Ҏ(gu)问题的类提供?jin)如下的?gu)定义Q?/p>
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException; private void writeObject(ObjectOutputStream out) throws IOException; (注:(x)q些Ҏ(gu)定义时必LU有的,因ؓ(f)不需要你昄调用Q序列化机制?x)自动调用? 使用以上Ҏ(gu)我们可以手动寚w些你又想序列化又不可以被序列化的数据字段q行写出和读入操作?/p>
下面是一个典型的例子Qjava.awt.geom包中的Point2D.Doublecd是不可序列化的,因ؓ(f)该类没有实现Serializable接口Q在我的例子中将把它当作LabeledPointcM的一个数据字D,q演C如何将其序列化Q?/p>
import java.io.*; public class TransientTest class LabeledPoint implements Serializable 该demo是第一ơ基于android开发?/p>
主要功能有:(x) d联系人姓名、号码,qlisetview 昄Q获取listview数据Qƈ发短信、或者拨?/p>
package com.android.hello; import android.app.Activity; import java.util.ArrayList; @SuppressWarnings("deprecation") Log.d(TAG,"testNum="+ emailAddress + "type:"+emailType); String number = mText.getText().toString(); public void onItemClick(AdapterView<?> arg0, View arg1, String number = mText.getText().toString();
import java.io.File;
import java.util.HashMap;
import java.util.Vector;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class PraseXML extends DefaultHandler
{
private Vector<String> tagName;
private Vector<String> tagValue;
private int step;
// 开始解析XML文g
public void startDocument() throws SAXException
{
tagName = new Vector<String>();
tagValue = new Vector<String>();
step = 0;
}
// l束解析XML文g
public void endDocument() throws SAXException
{
for (int i = 0; i < tagName.size(); i++)
{
if (!tagName.get(i).equals("") || tagName.get(i) != null)
{
System.out.println("节点名称Q? + tagName.get(i));
System.out.println("节点|(x)" + tagValue.get(i));
}
}
}
/**
* 在解释到一个开始元素时?x)调用此?gu).但是当元素有重复时可以自己写法来区?
* q些重复的元?qName是什? <name:page ll=""></name:page>q样写就?x)抛出SAXException错误
* 通常情况下qName{于localName
*/
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException
{
// 节点名称
tagName.add(qName);
// 循环输出属?
for (int i = 0; i < attributes.getLength(); i++)
{
// 获取属性名U?
System.out.println("属性名Uͼ(x)" + attributes.getQName(i));
// 获取属性?
System.out.println("属性|(x)"
+ attributes.getValue(attributes.getQName(i)));
}
}
/**
* 在遇到结束标{时调用此方?
*/
public void endElement(String uri, String localName, String qName)
throws SAXException
{
step = step + 1;
}
/**
* d标签里的?ch用来存放某行的xml的字W数?包括标签,初始大小?048,
* 每解释到新的字符?x)把它添加到char[]里?nbsp; * 注意,q个char字符?x)自q理存储的字符,
* q不是每一行就?x)刷Cơchar,start,length是由xml的元素数据确定的,
* 暂时找不到规?以后看源代码.
*
* q里一个正标签Q反标签都会(x)被执行一ơcharactersQ所以在反标{时不用获得其中的?
*/
public void characters(char ch[], int start, int length)
throws SAXException
{
// 只要当前的标{的长度一臻I值就不赋Q则反标{不被计划在?
if (tagName.size() - 1 == tagValue.size())
{
tagValue.add(new String(ch, start, length));
}
}
public static void main(String[] args)
{
String filename = "MyXml.xml";
SAXParserFactory spf = SAXParserFactory.newInstance();
try
{
SAXParser saxParser = spf.newSAXParser();
saxParser.parse(new File(filename), new PraseXML());
}
catch (Exception e)
{
e.printStackTrace();
}
}
public Vector getTagName()
{
return tagName;
}
public void setTagName(Vector tagName)
{
this.tagName = tagName;
}
public Vector getTagValue()
{
return tagValue;
}
public void setTagValue(Vector tagValue)
{
this.tagValue = tagValue;
}
}
属性|(x)e01
属性名Uͼ(x)enable
属性|(x)true
属性名Uͼ(x)personid
属性|(x)e02
属性名Uͼ(x)enable
属性|(x)false
属性名Uͼ(x)personid
属性|(x)e03
属性名Uͼ(x)enable
属性|(x)true
节点名称Qpeople
节点|(x)
节点名称Qperson
节点|(x)
节点名称Qname
节点|(x)张三
节点名称Qtel
节点|(x)5128
节点名称Qemail
节点|(x)txq512@sina.com
节点名称Qperson
节点|(x)
节点名称Qname
节点|(x)meixin
节点名称Qtel
节点|(x)5252525
节点名称Qemail
节点|(x)wnight88@sina.com
节点名称Qperson
节点|(x)
节点名称Qname
节点|(x)yu
节点名称Qtel
节点|(x)5389654
节点名称Qemail
节点|(x)yu@188.net
<people>
<person personid="e01" enable="true">
<name>张三</name>
<tel>5128</tel>
<email>txq512@sina.com</email>
</person>
<person personid="e02" enable="false">
<name>meixin</name>
<tel>5252525</tel>
<email>wnight88@sina.com</email>
</person>
<person personid="e03" enable="true">
<name>yu</name>
<tel>5389654</tel>
<email>yu@188.net</email>
</person>
</people>
]]>
?1 .Spring 框架的M架构?/font>
?2. 三个lg关系
?4. Bean 工厂的承关p?/font>
?5. Bean 定义的类层次关系?/font>
?6. Bean 的解析类
?7. Context 相关的类l构?/font>
?8. Resource 相关的类l构?/font>
?9. Context ?Resource 的类关系?/font>
清单 1. AbstractApplicationContext.refresh
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
清单 2. AbstractRefreshableApplicationContext. refreshBeanFactory
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException(
"I/O error parsing bean definition source for "
+ getDisplayName(), ex);
}
}
?10. DefaultListableBeanFactory cȝ承关pd
?11. 创徏 BeanFactory 时序?/font>
?12. 解析和登?Bean 对象时序?/font>
清单 3. BeanFactoryPostProcessor.postProcessBeanFactory
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException;
清单 4. AbstractApplicationContext.finishBeanFactoryInitialization
protected void finishBeanFactoryInitialization(
ConfigurableListableBeanFactory beanFactory) {
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
清单 5. DefaultListableBeanFactory.preInstantiateSingletons
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isInfoEnabled()) {
this.logger.info("Pre-instantiating singletons in " + this);
}
synchronized (this.beanDefinitionMap) {
for (String beanName : this.beanDefinitionNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton()
&& !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean factory =
(FactoryBean) getBean(FACTORY_BEAN_PREFIX+ beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null
&& factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
new PrivilegedAction<Boolean>() {
public Boolean run() {
return ((SmartFactoryBean) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = factory instanceof SmartFactoryBean
&& ((SmartFactoryBean) factory).isEagerInit();
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
}
}
?13.Bean 实例创徏程?/font>
?14.Bean 实例创徏时序?/font>
?15.Bean 对象关系建立
?16. Proxy cȝ?/font>
清单 6. Proxy. newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
if (h == null) {
throw new NullPointerException();
}
Class cl = getProxyClass(loader, interfaces);
try {
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
?17. 创徏代理对象时序?/font>
清单 7. SimpleProxy c?/font>
public interface SimpleProxy {
public void simpleMethod1();
public void simpleMethod2();
}
清单 8. $Proxy2 c?/font>
public class $Proxy2 extends java.lang.reflect.Proxy implements SimpleProxy{
java.lang.reflect.Method m0;
java.lang.reflect.Method m1;
java.lang.reflect.Method m2;
java.lang.reflect.Method m3;
java.lang.reflect.Method m4;
int hashCode();
boolean equals(java.lang.Object);
java.lang.String toString();
void simpleMethod1();
void simpleMethod2();
}
?18. Jdk 动态代理的cd
清单 9. 配置代理c?Bean
<bean id="testBeanSingleton"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>
org.springframework.aop.framework.PrototypeTargetTests$TestBean
</value>
</property>
<property name="target"><ref local="testBeanTarget"></ref> </property>
<property name="singleton"><value>true</value></property>
<property name="interceptorNames">
<list>
<value>testInterceptor</value>
<value>testInterceptor2</value>
</list>
</property>
</bean>
?19.Spring 代理对象的?/font>
?20.Spring 调用拦截?/font>
?21. 代理模式的结?/font>
?22. Spring 中用代理模式的l构?/font>
清单 10 代理对象 $Proxy4
public class $Proxy4 extends java.lang.reflect.Proxy implements
org.springframework.aop.framework.PrototypeTargetTests$TestBean
org.springframework.aop.SpringProxy
org.springframework.aop.framework.Advised
{
java.lang.reflect.Method m16;
java.lang.reflect.Method m9;
java.lang.reflect.Method m25;
java.lang.reflect.Method m5;
java.lang.reflect.Method m2;
java.lang.reflect.Method m23;
java.lang.reflect.Method m18;
java.lang.reflect.Method m26;
java.lang.reflect.Method m6;
java.lang.reflect.Method m28;
java.lang.reflect.Method m14;
java.lang.reflect.Method m12;
java.lang.reflect.Method m27;
java.lang.reflect.Method m11;
java.lang.reflect.Method m22;
java.lang.reflect.Method m3;
java.lang.reflect.Method m8;
java.lang.reflect.Method m4;
java.lang.reflect.Method m19;
java.lang.reflect.Method m7;
java.lang.reflect.Method m15;
java.lang.reflect.Method m20;
java.lang.reflect.Method m10;
java.lang.reflect.Method m1;
java.lang.reflect.Method m17;
java.lang.reflect.Method m21;
java.lang.reflect.Method m0;
java.lang.reflect.Method m13;
java.lang.reflect.Method m24;
int hashCode();
int indexOf(org.springframework.aop.Advisor);
int indexOf(org.aopalliance.aop.Advice);
boolean equals(java.lang.Object);
java.lang.String toString();
void sayhello();
void doSomething();
void doSomething2();
java.lang.Class getProxiedInterfaces();
java.lang.Class getTargetClass();
boolean isProxyTargetClass();
org.springframework.aop.Advisor; getAdvisors();
void addAdvisor(int, org.springframework.aop.Advisor)
throws org.springframework.aop.framework.AopConfigException;
void addAdvisor(org.springframework.aop.Advisor)
throws org.springframework.aop.framework.AopConfigException;
void setTargetSource(org.springframework.aop.TargetSource);
org.springframework.aop.TargetSource getTargetSource();
void setPreFiltered(boolean);
boolean isPreFiltered();
boolean isInterfaceProxied(java.lang.Class);
boolean removeAdvisor(org.springframework.aop.Advisor);
void removeAdvisor(int)throws org.springframework.aop.framework.AopConfigException;
boolean replaceAdvisor(org.springframework.aop.Advisor,
org.springframework.aop.Advisor)
throws org.springframework.aop.framework.AopConfigException;
void addAdvice(org.aopalliance.aop.Advice)
throws org.springframework.aop.framework.AopConfigException;
void addAdvice(int, org.aopalliance.aop.Advice)
throws org.springframework.aop.framework.AopConfigException;
boolean removeAdvice(org.aopalliance.aop.Advice);
java.lang.String toProxyConfigString();
boolean isFrozen();
void setExposeProxy(boolean);
boolean isExposeProxy();
}
?23. {略模式的结?/font>
?24. Spring 中策略模式结构图
]]>
]]>
{
public static void main(String[] args)
{
Employee harry = new Employee("Harry Hacker", 50000);
Manager manager1 = new Manager("Tony Tester", 80000);
manager1.setSecretary(harry);
Employee[] staff = new Employee[2];
staff[0] = harry;
staff[1] = manager1;
try
{
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("employee.dat"));
out.writeObject(staff);
out.close();
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("employee.dat"));
Employee[] newStaff = (Employee[])in.readObject();
in.close();
/**
*通过harry对象来加?br />
*在secretary上反映出?br />
*/
newStaff[0].raiseSalary(10);
for (int i = 0; i < newStaff.length; i++)
System.out.println(newStaff[i]);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
/**
*加薪?br />
*/
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return getClass().getName()
+ "[name = "+ name
+ ",salary = "+ salary
+ "]";
}
private String name;
private double salary;
}
{
public Manager(String n, double s)
{
super(n, s);
secretary = null;
}
/**
*讄U书
*/
public void setSecretary(Employee s)
{
secretary = s;
}
public String toString()
{
return super.toString()
+ "[secretary = "+ secretary
+ "]";
}
//secretary代表U书
private Employee secretary;
}
import java.awt.geom.*;
{
public static void main(String[] args)
{
LabeledPoint label = new LabeledPoint("Book", 5.00, 5.00);
try
{
System.out.println(label);//写入?br />
ObjectOutputStream out = new ObjectOutputStream(new
FileOutputStream("Label.txt"));
out.writeObject(label);
out.close();
System.out.println(label);//写入?br />
ObjectInputStream in = new ObjectInputStream(new
FileInputStream("Label.txt"));
LabeledPoint label1 = (LabeledPoint)in.readObject();
in.close();
System.out.println(label1);//dq加1.0?br />
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
{
public LabeledPoint(String str, double x, double y)
{
label = str;
point = new Point2D.Double(x, y);
}
private void writeObject(ObjectOutputStream out) throws IOException
{
/**
*必须通过调用defaultWriteObject()Ҏ(gu)来写?br />
*对象的描qC?qing)那些可以被序列化的字?br />
*/
out.defaultWriteObject();
out.writeDouble(point.getX());
out.writeDouble(point.getY());
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
/**
*必须调用defaultReadObject()Ҏ(gu)
*/
in.defaultReadObject();
double x = in.readDouble() + 1.0;
double y = in.readDouble() + 1.0;
point = new Point2D.Double(x, y);
}
public String toString()
{
return getClass().getName()
+ "[label = "+ label
+ ", point.getX() = "+ point.getX()
+ ", point.getY() = "+ point.getY()
+ "]";
}
private String label;
transient private Point2D.Double point;
}
]]>
]]> android 2.2 获取联系人,?sh)话Qƈ拨号 收藏
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.provider.ContactsContract;
import java.util.HashMap;
import android.widget.SimpleAdapter;
public class hello extends Activity {
/** Called when the activity is first created. */
// @SuppressWarnings("deprecation")
// @Override
//
private static final String TAG="App";
ListView listView;
ListAdapter adapter;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
LinearLayout linearLayout=new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.setBackgroundColor(Color.BLACK);
LinearLayout.LayoutParams param=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
listView=new ListView(this);
listView.setBackgroundColor(Color.BLACK);
linearLayout.addView(listView,param);
this.setContentView(linearLayout);
//生成动态数l,加入数据
ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();
ArrayList<HashMap<String, Object>> listItemRead = new ArrayList<HashMap<String, Object>>();
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null, null, null, null);
while (cursor.moveToNext())
{
HashMap<String, Object> map = new HashMap<String, Object>();
String phoneName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
map.put("ItemTitle", phoneName);//?sh)话姓?br />
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String hasPhone = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
if (hasPhone.compareTo("1") == 0)
{
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,null, null);
while (phones.moveToNext())
{
String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String phoneTpye = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
map.put("ItemText", phoneNumber); // 多个L(fng)如何处理
Log.d(TAG,"testNum="+ phoneNumber + "type:"+phoneTpye);
}
phones.close();
}
Cursor emails = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,null,ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId,null, null);
while (emails.moveToNext())
{
String emailAddress = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
String emailType = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
}
emails.close();
listItem.add(map);
}
//生成适配器的Item和动态数l对应的元素
SimpleAdapter listItemAdapter = new SimpleAdapter(this,listItem,//数据?nbsp;
android.R.layout.simple_list_item_2,//ListItem的XML实现
//动态数l与ImageItem对应的子?nbsp;
new String[] {"ItemTitle", "ItemText"},
//ImageItem的XML文g里面的一个ImageView,两个TextView ID
new int[] {android.R.id.text1,android.R.id.text2}
);
listView.setAdapter(listItemAdapter);
cursor.close();
//listView.setEmptyView(findViewById(R.id.empty));
listView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
// TODO Auto-generated method stub
//openToast("滚动刎ͼ(x)"+arg0.getSelectedItemId());
//短信发?nbsp;
setTitle("选择"+arg2+"目");
openToast("选择"+arg0.getSelectedItemId()+"目");
RelativeLayout lr = (RelativeLayout) arg1;
TextView mText = (TextView) lr.getChildAt(1);
openToast(mText.getText().toString());
Log.d(TAG, "number=" + number);
// 判断?sh)话L(fng)的有效?br />
if (PhoneNumberUtils.isGlobalPhoneNumber(number)) {
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri
.parse("smsto://" + number));
intent.putExtra("sms_body", "The SMS text");
startActivity(intent);
}
}
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
int position, long arg3) {
// TODO Auto-generated method stub
// openToast("Click"+Integer.toString(position+1)+"目");
RelativeLayout lr = (RelativeLayout) arg1;
TextView mText = (TextView) lr.getChildAt(1);
openToast(mText.getText().toString());
Log.d(TAG, "number=" + number);
// 判断?sh)话L(fng)的有效?br />
if (PhoneNumberUtils.isGlobalPhoneNumber(number)) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri
.parse("tel://" + number));
startActivity(intent);
}
}
});
}
private void openToast(String str){
Toast.makeText(this,str,Toast.LENGTH_SHORT).show();
}
}
]]>
Comparator cmp = Collator.getInstance(java.util.Locale.CHINA);
TreeMap tree=new TreeMap(cmp);
String[] arr = {"张三", "李四", "王五"};
// 使根据指定比较器产生的顺序对指定对象数组q行排序?nbsp;
Arrays.sort(arr, cmp);
for (int i = 0; i < arr.length; i++)
System.out.println(arr[i]);
<script>
names = ["张三", "李四", "王五", "刘六"];
names.sort(function(a,b){return a.localeCompare(b)});//a,b 为数la的某两个|自动传入
alert(names);
</script>
?
String [] test = new String[] {
"作业",
"试",
"test",
"我们",
"。空",
"镂空",
"[",
"?,
"?
};
1.5.6
Eclipse 3.1
我们在应用程序中可能?x)经帔R到对中文排序的问题,例如姓名列表Q词汇表{等。对中文排序Q我们用比较多的是Ҏ(gu)汉语拼音发音来确定顺序?br />
我们可能?x)经怋?nbsp;
接口Q?
{类或方法对含有中文字符的对象进行排序,但是q些在默认情况下都是调用
Ҏ(gu)Q这个方法是比较2个字W的 codepoint valueQ如果第一个字W的值小于第二个Q则在排序结果中W一个会(x)在前面,反之亦然?/p>
其实 java 中提供了(jin)和语a相关的类Q即 Collator 接口?qing)其实现cR?
是一个具体类Q它实现?nbsp;Comparator 接口中的 compare(Object, Object) Ҏ(gu)? RuleBasedCollator Ҏ(gu)Ҏ(gu)特定语言的默认规则比较字W,也可以按照指定的规则来比较,请参?nbsp;java API 获取此类的详l信 息?br /> 如果我们需要对一个有中文的数l进行排序,则可以用这个类。请看如下示例代码:(x)
以上代码的输出结果ؓ(f)Q?br />
============
[
test
。空
试
我们
作业
?br />
镂空
?br />
大家可能?x)发现只有一部分汉字是按照汉语拼x序了(jin)Q还有几个没有?/p>
GB2312:
在简体中文中我们使用比较多的字符集是 GB2312-80Q简UCؓ(f) GB2312Q这个字W集包含?jin)目前最常用的汉字共?nbsp;6736 个。其中的汉字分ؓ(f)两大c:(x)
常用汉字按照汉语拼音来排序,而次常用汉字按照W画部首q行排序?br />
体汉字在 Unicode 中一般是按照 gb2312 的码点值的序来放|的Q所以如果是常用汉字 java p够很准确的进行排序,但如果是ơ常用汉字,则就?x)出现问题。在以上CZ中,"?Q?? 属于ơ常用字?/p>
RuleBasedCollator c?nbsp;getRules() Ҏ(gu)可以q回对应语言的规则设|。简体中文对应的规则?nbsp;gb2312 所对应的字W?/p>
在以下的代码中,我把排过序的汉字直接作ؓ(f) String 对象攑֜c里面了(jin)Q如果要让代码变得简z一些,则可以把完整的规则(Ҏ(gu)字符Q排序汉字)(j)存ؓ(f)文g?/p>
例子工程源码下蝲地址Q?使用览器自带的下蝲工具下蝲)http://cid-cb78b387364ae9a7.skydrive.live.com/browse.aspx/.Public/%e8%bd%af%e4%bb%b6/Andoroid
当然Q我也参考了(jin)一些网上的资料Q主要有Q?/p>
AdMobQ在android应用中嵌入广告的Ҏ(gu)
如何?font style="background-color: #00ffff;">Android Market赚钱 part 2 - 免费app附带q告
Publisher Starter Kit
面向开发?Wiki ?AdMob
好了(jin)Q现在让我从头开始说?#8230;…在这之前Q你不需要有M的帐P唯一需要的是有一个有效的email邮箱。只要按照下面的步骤一步步来,你就能通过Admob的广告插到自qE序中赚钱啦Q?/p>
首先Q当然是需要注册一个Admob的帐受Admob的主|Q?a >http://www.admob.com/ ?当然Q如果你对于览英文|页q有些障的话,可以d中文|站Q?a >http://zhcn.admob.com/ 。如果网站的文字q是英文Q你可以在网站主늚右下角的“Language”处,选择“中文Q简体)(j)”。点击进入注册页面后Q有一些栏目需要填写,不要? q疑虑,像你注册一个论坛一P随便填下好?jin)。最关键的是保证填写的email地址有效Q另外就是填上姓名,选择语言。帐L(fng)型我选择?#8220;不确?#8221;Q? 语言“中文Q简体)(j)”?提交注册甌之后Q不久你׃(x)收到用于认q激zdL(fng)?sh)子邮gQ点?yn)Lz链接,可以了(jin)ȀzM的Admob帐号?jin)?/p>
W二步就是设|你?font style="background-color: #00ffff;">Android? 用程序信息,q获得Admob的插入代码。登录你的Admob帐号后,在主늚左上方(Logo上面Q点?#8220;MarketplaceQ手机广告市(jng)场)(j)”Q? q入面后,?#8220;Sites&AppsQ站点和应用E序Q?#8221;标签下,点击“Add Site/App”。选择我们熟?zhn)的图标—? Android App ” 。这时会(x)出现需要你填写一?#8220;详细信息”Q随便填上一些信息。(不要太过在意现在填写的东西,因ؓ(f)q些以后都是可以修改的)(j)。比?#8220;Android Package URL” 我到现在都还没有填写Q描qCcȝQ想写就写点吧。填好详l信息后Q点?#8220;l箋”Q就可以到AdMob Android SDK 的下载页面了(jin)。下载这个SDKQ当?dng)q个很重要)(j)?/p>
The AdMob Android SDK includes:
README: Get started with AdMob Android ads!
AdMob Jar file: Required for publishing ads. Follow the documentation in
javadoc/index.html and drop the AdMob Jar file into your project.
Sample Projects: Examples of AdMob Android ads shown in the LunarLander application.
W三步获取你的应用程序对应的Publisher ID。在下蝲面点击"Go to Sites/Apps"可以到你应用程序的理界面?jin)。这时你?x)发现在q个面醒目的位|会(x)有一个叫你填写详l信息的提示Q?/p>
在我们发送Q何有待收入之前,(zhn)需要填写技术联p详l信息和付款首选项?/p>
我们暂时可以不用它Q因为钱是会(x)存在我们的Admob的̎户上的,{我们需要提现的时候,或者你惛_的时候再填就可以?jin)。在下面的列表中Q选择? 的应用程序ƈq入。这个界面就是你的应用程序广告的理界面?jin),里面有比较多的功能,以后可以慢慢了(jin)解Q现在我们只需要知道两个东西,一个是发布? IDQPublisher IDQ,一个是你程序的状态。Publisher ID是一?5个字W的字符Ԍ而你E序的状态现在应该还是不zdQInactiveQ。我们下面要做的是怎么让它变ؓ(f)Active?/p>
W四步代码编写——在你的应用E序中插入Admobq告。经q上面的步骤Q我们在|站上的讄告一个段落了(jin)Q现在我们终于要q入主题?jin),如何在自qAndroid应用E序中插入Admobq告。如果你不健忘的话,一定还记得我们之前下蝲的那个AdMob Android SDK 。解压它Q看看里面有些什么东ѝ这里面最重要的就是那个名?#8220;admob-sdk-android.jar”的包啦,Admob如何把q告加蝲?font style="background-color: #00ffff;">Android应用E序中的代码集成在这个包里,我们~写E序的时候就需要将q个包导入到我们的工E里面去。另外,解压出来的文件夹中还有一个名?#8220;javadoc”的文件夹Q打开它里面的index.html。它是关于Admob Android SDK的帮助文档,在Package 下的Setup下,有详l完整的在自q应用E序中插入广告的Ҏ(gu)介绍Q在q里我就hQ引用一下~
Including the Jar
Add the Jar file included with the SDK to your Android
project as an external library. In your project's root directory create
a subdirectory libs (this will already be done for you if you used Android's activitycreator). Copy the AdMob Jar file into that directory. For Eclipse projects:
Go to the Properties of your project (right-click on your project from the Package Explorer tab and select Properties)
Select "Java Build Path" from left panel
Select "Libraries" tab from the main window
Click on "Add JARs..."
Select the JAR copied to the libs directory
Click "OK" to add the SDK to your android project
注意Q需要首先在你工E的根目录下新徏一个叫?#8220;libs”的文件夹Qƈ把之前所说的最重要的东?#8220;admob-sdk-android.jar”复制到里面?/p>
AndroidManifest.xml
Your AdMob publisher ID was given to you when creating your publisher account on www.admob.com before downloading this code. It is a 15-character code like a1496ced2842262. Just before the closing </application> tag add a line to set your publisher ID:
<!-- The application's publisher ID assigned by AdMob -->
<meta-data android:value="YOUR_ID_HERE" android:name="ADMOB_PUBLISHER_ID" />
</application>
Set any permissions not already included just before the closing </manifest> tag:
<!-- AdMob SDK permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>
Only the INTERNET permission is required. Setting ACCESS_COARSE_LOCATION (and/or ACCESS_FINE_LOCATION) allows narrowly geo-targeted ads be shown.
q里需要注意的是,<meta-data android:value="YOUR_ID_HERE" android:name="ADMOB_PUBLISHER_ID" />中,我们只需要改的是"YOUR_ID_HERE"。这里需要你填上的ID是我们之前在Admob|站我们的应用程序管理页面上看到? Publisher IDQ而name="ADMOB_PUBLISHER_ID"是不应该改的。程序需要这个Key来查扑֯应的Value?/p>
attrs.xml
The attrs.xml file specifies custom AdView attributes in XML layout
files. If your application does not already have an
/res/values/attrs.xml file then create one and copy-and-paste the
following into it. If you do have that file then just add the
declare-styleable element:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="com.admob.android.ads.AdView">
<attr name="testing" format="boolean" />
<attr name="backgroundColor" format="color" />
<attr name="textColor" format="color" />
<attr name="keywords" format="string" />
<attr name="refreshInterval" format="integer" />
<attr name="isGoneWithoutAd" format="boolean" />
</declare-styleable>
</resources>
q个Q没什么说的?/p>
Placing an AdView in a Layout
AdView widgets can be put into any XML layout now. The first step is to
reference attrs.xml in your layout element by adding an xmlns line that
includes your package name specified in AndroidManifest.xml:
xmlns:yourapp=http://schemas.android.com/apk/res/yourpackage
For example a simple screen with only an ad on it would look like:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:admobsdk="http://schemas.android.com/apk/res/com.admob.android.example"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.admob.android.ads.AdView
android:id="@+id/ad"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
admobsdk:backgroundColor="#000000"
admobsdk:textColor="#FFFFFF"
admobsdk:keywords="Android application"
/>
</LinearLayout>
q里好像也没什么需要特别注意的Q注意加上xmlnsQ另外知道这里可以设|一个keywords
Test Mode
When you start integrating AdMob ads into your application it is
recommended to use test mode. This always returns the same ad. Normal ad
requests are not deterministic making it harder to be sure the ad view
looks like you want (e.g. ad requests can timeout or may not fill).
Once the ad shows up as you expect be sure to turn test mode off to get real ads. Never put your application into the Android Market with test mode enabled.
Test mode can be enabled either by calling AdManager.setInTestMode(true) or by adding a "admobsdk:testing="true"" property to the ad in your XML layout (where "admobsdk" is the XML namespace for your application).
讄Test Modeq个很关键,千万别将处于Test Mode的程序发布出M(jin)Q那样可赚不?jin)钱啊~Q如果在AdView的属性中不加上admobsdk:testing="false"Q似乎程序也是不处于Test Mode的,不过最好还是加上吧?/p>
W五步编译运行,q激zȝ序。编译运行你的程序,在模拟器上就可以看到效果啦~Q当然你的模拟器需要能上网Q关于怎么让模拟器上网呢?用\q应该 可以直接上,如果不是用\由,那么可能需要设|下Q具体方法大家自q上搜吧,具体忘了(jin)Q。如果你的应用程序能昄出广告,那么恭喜你,你的应用E序很快 ׃(x)在Admob上被ȀzM(jin)Q需要一定的旉Q我的好像花?jin)一个小时不刎ͼ(j)Q?/p>
W六步在Admob|站上查看应用程序赚?jin)多钱?#8220;手机q告?jng)?#8221;?gt;“报告”?gt;“站点和应用程序报?#8221;。选择你的应用E序Q然后点击页面最下面?#8220;创徏报告”?OKQ赚钱啦?/p>
最后,我把我自己写的一个例子工E上传上来,大家可以下蝲来参考下。另外,我的工程广告作为移动的Qƈ且改变了(jin)它默认的宽度和背景,希望对如何在应用E序中摆攑ֹ告,起到一个抛砖引玉的作用。效果图如下Q哈哈,在模拟器跑的~)(j)Q?/p>
dAdd Mobile Site/appQ输入相关信息后Q提交完成,
下蝲Androidq_使用的JARQ查看发布?ID?/p>
Properties->Java Build Path->Libraries->Add JARs…->Select the JAR->OK
application节点中添?/p>
<!– The application’s publisher ID assigned by AdMob –>
<meta-data android:value=”a14ae1ce0357305″ android:name=”ADMOB_PUBLISHER_ID” />
manifest节点d权限甌
<!– AdMob SDK permissions –>
<uses-permission android:name=”android.permission.INTERNET” />
/res/values/attrs.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<resources>
<declare-styleable name=”com.admob.android.ads.AdView”>
<attr name=”testing” format=”boolean” />
<attr name=”backgroundColor” format=”color” />
<attr name=”textColor” format=”color” />
<attr name=”keywords” format=”string” />
<attr name=”refreshInterval” format=”integer” />
<attr name=”isGoneWithoutAd” format=”boolean” />
</declare-styleable>
</resources>
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:admobsdk=”http://schemas.android.com/apk/res/com.moandroid.livesports”
android:orientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
>
<TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/hello”
/>
<com.admob.android.ads.AdView
android:id=”@+id/ad”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
admobsdk:backgroundColor=”#000000″
admobsdk:textColor=”#FFFFFF”
admobsdk:keywords=”Android application”
admobsdk:refreshInterval=”60″
/>
</LinearLayout>
为免费app嵌入Admobq告Q进而获得广告收入?/p>
在多Activity开发中Q有可能是自己应用之间的Activity跌{Q或者夹带其他应用的可复用Activity。可能会(x)希望跌{到原来某个Activity实例Q而不是生大量重复的Activity?/p>
q需要ؓ(f)Activity配置特定的加载模式,而不是用默认的加蝲模式?/p>
Activity有四U加载模式:(x)
讄的位|在AndroidManifest.xml文g中activity元素的android:launchMode属性:(x)
<activity android:name="ActB" android:launchMode="singleTask"></activity>
也可以在Eclipse ADT中图形界面中~辑Q?/p>
区分Activity的加载模式,通过CZ一目了(jin)然。这里编写了(jin)一个Activity AQActAQ和Activity BQActBQ@环蟩转的例子。对加蝲模式修改和代码做E微改动Q就可以说明四种模式的区别?/p>
首先说standard模式Q也是默认模式Q不需要配|launchMode。先只写一个名为ActA的ActivityQ?/p>
package com.easymorse.activities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;public class ActA extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView textView = new TextView(this);
textView.setText(this + "");
Button button = new Button(this);
button.setText("go actA");
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(ActA.this, ActA.class);
startActivity(intent);
}
});
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.addView(textView);
layout.addView(button);
this.setContentView(layout);
}
}
例子中都没有用layoutQ免得看着|嗦。可见是ActA –> ActA的例子。在界面中打印出对象的toString值可以根据hash code识别是否创徏新ActA实例?/p>
W一个界面:(x)
点击按钮后:(x)
可以多点几次。发现每ơ都创徏?jin)该Activity的新实例。standard的加载模式就是这L(fng)Qintent发送给新的实例?/p>
现在点Android讑֤的回退键,可以看到是按照刚才创建Activity实例的倒序依次出现Q类似退栈的操作Q而刚才操作蟩转按钮的q程是压栈的操作。如下图Q?/p>
singleTop和standard模式Q都?x)将intent发送新的实例(后两U模式不发送到新的实例Q如果已l有?jin)的话?j)。不 q,singleTop要求如果创徏intent的时候栈已l有要创建的Activity的实例,则将intent发送给该实例,而不发送给新的实例?/p>
q是用刚才的CZQ只需launchMode改ؓ(f)singleTopQ就能看到区别?/p>
q行的时候会(x)发现Q按多少遍按钮,都是相同的ActiA实例Q因实例在栈Ӟ因此不会(x)创徏新的实例。如果回退Q将退出应用?/p>
singleTop模式Q可用来解决栈顶多个重复相同的Activity的问题?/p>
如果是A Activity跌{到B ActivityQ再跌{到A ActivityQ行为就和standard一样了(jin)Q会(x)在B Activity跌{到A Activity的时候创建A Activity的新实例Q因为当时的栈顶不是A Activity实例?/p>
ActAcȝ作改动:(x)
package com.easymorse.activities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;public class ActA extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView textView = new TextView(this);
textView.setText(this + "");
Button button = new Button(this);
button.setText("go actB");
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(ActA.this, ActB.class);
startActivity(intent);
}
});
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.addView(textView);
layout.addView(button);
this.setContentView(layout);
}
}
ActBc:(x)
package com.easymorse.activities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;public class ActB extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button=new Button(this);
button.setText("go actA");
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.setClass(ActB.this, ActA.class);
startActivity(intent);
}
});
LinearLayout layout=new LinearLayout(this);
layout.addView(button);
this.setContentView(layout);
}
}
ActBcM用默认(standardQ加载,ActA使用singleTop加蝲。结果类g图:(x)
如果把ActA的加载模式改为standardQ情况一栗?/p>
singleTask模式和后面的singleInstance模式都是只创Z个实例的?/p>
当intent到来Q需要创建singleTask模式Activity的时候,pȝ?x)检查栈里面是否已经有该Activity的实例。如果有直接intent发送给它?/p>
把上面singleTop的实例中的ActA的launchMode改ؓ(f)singleTaskQActB的改为standard。那么会(x)发现在ActA界面中按一ơ按钮:(x)
然后在ActB1界面中按按钮Q因为ActA是singleTaskQ会(x)使用原来的ActA1实例。这时候栈内的情况:
如果多次按按钮蟩转,?x)发现始l只有ActA1q一个ActAcȝ实例?
解释singleInstance模式比较ȝ(ch)?/p>
首先要说一下TaskQQ务)(j)的概c(din)?/p>
如果是Swing或者WindowsE序Q可能有多个H口可以切换Q但是你无法在自q序中复用人家的窗口。注意是直接复用人家的二q制代码Q不是你拿到人家api后的源代码调用?/p>
Android可以做到Q让别h的程序直接复用你的ActivityQ类似桌面程序的H口Q?/p>
Android为提供这U机Ӟ引入了(jin)Task的概c(din)Task可以认ؓ(f)是一个栈Q可攑օ多个Activity。比如启动一个应用,那么
Android创Z(jin)一个TaskQ然后启动这个应用的入口ActivityQ就是intent-filter中配|ؓ(f)main和launch的那?
Q见
?x)发玎ͼ无论切换ActivityQtaskId是相同的?/p>
当然也可以在q个单一的Task栈中Q放入别人的ActivityQ比如google地图Q这L(fng)L(fng)q地图按回退键的时候,?x)退栈回到调用地囄Activity。对用户来说Qƈ不觉得在操作多个应用。这是Task的作用?/p>
但是Q有q样的需求,多个Task׃n一个ActivityQsingleTask是在一个task中共享一个ActivityQ?/p>
现成的例子是google地图。比如我有一个应用是导游斚w的,其中调用的google地图Activity。那么现在我比如按home键,然后到应用列表中打开google地图Q你?x)发现显C的是刚才的地图,实际上是同一个Activity?/p>
如果使用上面三种模式Q是无法实现q个需求的。google地图应用中有多个上下文ActivityQ比如\U查询等的,导游应用也有一些上下文Activity。在各自应用中回退要回退到各自的上下文Activity中?/p>
singleInstance模式解决?jin)这个问题(l了(jin)q么半天才说到正题)(j)。让q个模式下的Activity单独在一个task栈中。这个栈只有一个Activity。导游应用和google地图应用发送的intent都由q个Activity接收和展C?/p>
q里又有两个问题Q?/p>
如果q是拿刚才的ActA和ActB的示例,可以把ActB的模式改为singleInstanceQActA为standardQ如果按一ơ按钮切换到ActBQ看到现象用C意囄DP(x) 如果是第一ơ按钮切换到ActBQ在ActB在按按钮切换到ActAQ然后再回退Q示意图是:(x) 另外Q可以看C个Activity的taskId是不同的?/p>