原文链接 https://plus.google.com/105051985738280261832/posts/FXCCYxepsDU
作者Dianne Hackborn就职于Google,android platform team,也就是安卓系统的设计团队。来看看安卓设计团队的人怎么看待四大组件的作用的。
"我应该如何设计我的app?我应该用什么MVC模式?我应该用什么来实现事件总线?"安卓开发者经常这样问我们,他们应该在app上用什么架构或者什么设计模式。出乎意料的是:这个问题我们没有很标准的答案。在这里,“我们”指的是google安卓platform team。尽管有一些安卓开发者(google内外的都有)给出了一些很好的建议,我没有忽视这些观点(言下之意...这些建议不能代表google安卓平台设计者)。应该用mvc?mvp?mvvm?我不知道。(我觉得原作者的想说的是,他们是安卓系统的设计者,他们自己对于app的架构设计都没有给出指南性质的建议,其他开发者给出的一些建议不应该视作是标准或是指南。)
这样的回答可能有点出人意料,因为人们很容易认为安卓app架构的设计是有官方准则可参照的。你看安卓sdk里面的这些java API,以及它的高层的设计,很容易让人认为它可以提供一个应用的框架。但是,事实上,并不是这样。
可能,对于安卓sdk里核心api(我目测,作者指的就是四大组件这样的高层设计)更加贴切的描述是:这是一个“系统框架”。安卓核心api的目的是指明一个应用应该通过什么方式和安卓系统之间的进行交互;至于应用内部怎么搞,安卓核心api并不是特别关心。
我们来看一个经典操作系统怎么来调用它的应用。一般是这样的,操作系统直接调用应用的main()方法。很明显,这个main()里面要干什么,和操作系统没啥关系;main()是空的,你想让应用干啥,你就在main()方法里写啥。安卓核心api的功能,实际上和main()方法类似,用来提供一个入口。但是因为安卓核心api和main()方法乍看之下很不一样,以至人们会误解安卓核心api的用途。
在安卓中,我们没有设置类似main()方法的入口,因为我们想让系统更好地控制安卓app。特别需要指出的是,我们希望用户使用中不要有启动,或者停止一个应用的概念,而是让安卓系统来停止/启动app。所以,(相较于main()方法为入口的模型)安卓系统需要每个应用的状态信息,以及在app没有启动的情况下,能以特定的启动方式启动app。为了实现这个目的,我们把app的主要入口根据app与安卓系统的不同交互形式分类。这样分类的结果就是大家熟悉的Activity, BroadcastReceiver, Service, and ContentProvider API。
乍看之下,这些类好像在指导你应该如何设计你的app内部架构,但是,并不是这样。这些类完全是为了完成app与安卓系统之间的交互(以及在安卓系统的调控下,完成与其他app之间的交互)。安卓系统并不是很关心你的app内部是怎么实现的。
为了更好的阐述这个观点,我们来看一下四大组件在安卓系统中到底是干什么的。
Activity
Acitivity是进入一个需要和用户交互的app的入口。从安卓系统的角度来看,安卓系统(通过activity)提供给一个app的最重要的和安卓系统的交互途径是下面这些
----系统记录当前用户对什么感兴趣(当前在屏幕上的是什么),并且确保维护当前屏幕内容的进程的运行。
----系统记录用户在之前干了什么,用户可能会回到之前的任务(被停止的activity),系统需要给维护以前任务的进程较高的优先级(以便用户想回去的时候可以回得去)。
----如果进程被杀死,Activity需要帮助应用回到被杀之前的状态(在用户想回去的时候)。
----在安卓系统的维护之下,给应用之间建立跳转的通道,最经典的例子就是分享(分享到朋友圈就是跳到微信app)
对于安卓系统来说,app内部的页面跳转,它是不关心的。无论你是在一个activity里面用自己编写的view,还是用fragment,还是用多个activity来实现跳转,安卓系统不怎么关心。(我的理解就是,在设计app内部的架构的时候,肯定会涉及到页面跳转,安卓平台的设计者没有规定怎么来实现跳转。你可以视自己的需求来使用自定义view,或者fragment,或者activity。activity存在的目的,不是用来在app内部跳转页面,当然它可以被用来做app内部的跳转。)
BroadcastReceiver
BroadcastReceiver在一般的用户跳转之外,提供了传递事件到应用的机制。最重要的是,这个应用入口可以在app没有启动的情况下给app传递事件。所以,举个例子,一个app可以预定时间弹出一个notification提醒用户,通过给app的BroadcastReceiver发送警报,我们不必在警报启动前让app一直运行。
app内部分发事件完全是另外一件事情,BroadcastReceiver对这些不关心。无论你是用一些事件总线框架也好,还是自定义回调机制也好,总之你不应该使用安卓系统的BroadcastReceiver。在app内部使用BroadcastReceiver不仅会有过大的开销,还会有安全问题。如果你要用的话,可以使用LocalBroadcastManager实现进程内的intent分发系统。
Service
Service:当app需要在系统的后台干点活的时候,Service就应该是app的入口。如果一个app有service的话,那么这个app和系统的关系可能有这样两种情况。
--->通过startService方法启动的service就是在告诉安卓系统,让这个service工作一直到它干完了。这个工作可以是后台数据同步,或者音乐播放。这个情况下,可以细分出两种不同的情况 1 类似音乐播放的service,它是使用者在关注的。这种情况下可以把service设置为前台service,前台service会有一个notification. 在这种情况下,安卓系统就知道,这个service如果挂了,使用者会不太高兴,它会努力让这个service所在的进程活下去。2一般的service,它不受到使用者的直接关注,所以安卓系统给它的优先级会低一些,并且在内存不足的时候杀掉这个service的进程(以后可能会重新启动它)。
--->通过bindService启动的service或者说绑定的service,表示其他的app或者是系统需要这个service提供的功能。这个service基本可以看做是给绑定对象的app提供api的。安卓系统于是知道:某个app会依赖于这个service。如果进程A中的app绑定了一个进程B的service,那么系统知道进程A需要进程B,如果进程A在前台,那么进程B也会被认为是高优先级的。
许多的系统的功能比如可变墙纸,屏保,输入法等,都被做成了service,app需要它们的时候就会绑定到它们。
在app内部,如果一个东西和app进程在安卓系统里的优先级无关的话,安卓系统是不关心的。所以,如果你要下载一些东西用来在UI显示的话,你就不用启动一个service来做这个事情。事实上,你应该确保,你不用service来做这件事情。 如果你只是简单启动一个下载线程的话(或者任何非service的方法),这样的场景是符合安卓设计初衷的:当用户正在使用某个界面的时候,系统会确保下载任务的优先级,而当用户离开这个界面的时候,这个进程(包括UIthread和下载thread)会被缓存,在使用者想回来的时候可以回来,同时在使用者需要内存去干其他事情的时候,会被杀掉。
contentProvider
最后contentProvider,它是用来传递数据的。(contentProvider我从来没用过,所以原文中有些内容理解不深,就不仔细翻了,挑点我看懂的写一写)人们通常认为它是用来封装数据库的,因为它有很多API用来干这个。但是从安卓系统设计的角度来说,不是这样。
content provider对于安卓系统来说是发布数据的app的入口,数据由uri来定位。如果你的app不打算发布数据给其他app使用,那么你无须去使用ContentProvider。