Android四大组件:Activity、Service、Broadcast receiver、Content provider
在Android中,一个应用程序可以使用其它应用程序的组件,这是Android系统一个非常重要的特性。例如,你编写的应用程序需要显示一个可以滚动的图片列表,如果其它某个应用程序已经开发了具有此功能的组件并对外发布了此组件以使其它应用程序能够使用此组件,因此你可以直接调用这个组件来显示图片,而不需要重新开发一个具有此功能的组件。在需要的时候Android会启动另外一个程序的部分代码,这部分代码实现了你请求执行的动作的功能,而不是在你的应用程序中直接包含另外一个应用程序的代码。
为了实现这样的功能,Android系统必须能够在其它应用程序有请求的时候启动一个应用程序进程并实例化部分java对象。因此与其它大部分系统不同,运行在Android系统上的应用程序并没有一个应用程序入口点(类似java程序中的main方法)。相反,Android应用程序包含一些运行应用程序所必须的能够被Android系统实例化的组件。这些组件主要包括以下四种类型。
一、Activity
一个Activity通常展现为一个可视化的用户界面。例如,一个activity可能展现为一个用户可以选择的菜单项列表或者展现一些图片以及图片的标题。一个消息服务应用程序可能包含一个显示联系人列表的activity,一个编写信息的activity,以及其它一些查看信息和修改应用程序设置的activity。虽然这些activity一起工作,共同组成了一个应用程序,但每一个activity都是相对独立的。每一个activity都是Activity(android.app.Activity)的子类。
一个应用程序可能只包含一个activity,或者像上面提到的消息服务程序一样有多个activity。一个应用程序包含几个activity以及各个activity完成什么样的功能完全取决于应用程序以及它的设计。通常每个应用程序都包含一个在应用程序启动后第一个展现给用户的activity。在当前展现给用户的activity中启动一个新的activity,可以实现从一个activity转换到另外一个activity。
每个activity都会有一个用于绘制用户界面的窗口。通常这样一个窗口会填充整个屏幕,当然这个窗口也可以比屏幕小并漂浮在其他窗口之上。activity还可以使用一些额外的窗口,例如一个要求用户响应的弹出式对话框,或者是当用户在屏幕上选择一个条目后向用户展现一些重要信息的窗口。
展示activity窗口的可视化内容区域是一些具有层次关系(很像数据结构中的树)的视图,而视图则是由类View的子类表示的。每个视图控制窗口中的一个矩形区域。父视图包含一些子视图并管理子视图的布局。位于叶节点的视图直接控制并响应用户的动作。因此视图就是activity与用户交互的接口。例如,一个显示图片的视图,当用户单击的时候它可能会启动一个动作。Android有许多开发人员可以直接使用的视图,包括按钮,文本域,滚动条,菜单,复选框等。
通过调用Activity.setContentView()方法来设置展现activity的窗口的视图。内容视图则是视图层次结构中的根节点视图。
1. Activity的4种状态
Activity的生命周期指Activity从启动到销毁的过程,Activity有4种状态 :
(1)活动状态:Activity在用户界面处于最上层,完全能被用户看到,能够与用户进程进行交互;
(2)暂停状态:Activity在界面上被部分挡住,该Activity不再处于用户界面的最上层,且不能够与用户进行交互,或者屏幕被锁定;
(3)停止状态:Activity在界面上完全不能被用户看到,也就是说这个Activity被其他Activity全部遮挡;
(4)非活动状态:不在以上3种状态中的Activity则处于非活动状态。
2. Activity的生命周期
(1)全生命周期:
全生命周期是从Activity建立到销毁的全部过程,始于onCreate(),结束于onDestroy()。开发者通常在onCreate()中初始化用户界面,分配引用类变量,绑定数据控件,并创建服务和线程等Activity所能使用的全局资源和状态,并在onDestroy()中释放这些资源,并确保所有外部连接被关闭,如网络或数据库的连接等;在一些极端情况下,Android系统会直接终止进程,而不会先调用onDestroy()。
为了避免创造短期对象和增加垃圾收集的时间,以致对用户体验产生直接影响。如果你的Activity需要创建一些对象的话,最好在onCreate方法中创建,因为它在一个Activity的完整生命周期中只被调用一次。
(2)可视生命周期:
可视生命周期是Activity在界面上从可见到不可见的过程,开始于onStart(),结束于onStop()。在极端情况下,系统会直接销毁一个Activity,而不先调用onStop,即使它处于可见状态。
(3)活动生命周期
活动生命周期是Activity在屏幕最上层,并能够与用户交互的阶段,开始于onResume(),结束于onPause()。在Activity的状态变换过程中onResume()和onPause()经常被调用,因此这两个方法中的代码应该写得简单、高效些,以提高性能。
3. Activity的启动模式
Activity有4种启动模式,类似于C语言中的局部变量、全局变量及静态变量等。
(1)standard:标准模式,调用startActivity()方法就会产生一个新的实例。
(2)singleTop:检查是否已经存在一个实例位于Activity Stack的顶部,如果存在就不产生新的实例,否则调用Activity的newInstance()方法产生一个新实例。
(3)singleTask:在一个新的Task中产生这个实例,以后每次调用都会使用此实例,而避免产生新的实例。
(4)singleInstance:这个基本上跟singleTask一样,只有一点不同,那就是在这个模式下的Activity实例所处的Task中,只能有一个Activity实例,而不能有其他的实例。
二、Service
service没有用户界面,但它会在后台一直运行。例如,service可能在用户处理其它事情的时候播放背景音乐,或者从网络上获取数据,或者执行一些运算,并把运算结构提供给activity展示给用户。每个service都扩展自类Serivce。
多媒体播放器播放音乐是应用service的一个非常好的例子。多媒体播放器程序可能含有一个或多个activity,用户通过这些activity选择并播放音乐。然而,音乐回放并不需要一个activity来处理,因为用户可能会希望音乐一直播放下去,即使退出了播放器去执行其它程序。为了让音乐一直播放,多媒体播放器activity可能会启动一个service在后台播放音乐。Android系统会使音乐回放service一直运行,即使在启动这个service的activity退出之后。
应用程序可以连接到一个正在运行中的service。当连接到一个service后,可以使用这个service向外暴露的接口与这个service进行通信。对于上面提到的播放音乐的service,这个接口可能允许用户暂停,停止或重新播放音乐。
与activity以及其它组件一样,service同样运行在应用程序进程的主线程中。所以它们不能阻塞其它组件或用户界面,通常需要为这些service派生一个线程执行耗时的任务。
下面让我们看下该类的原文档:
Class Overview
A Service is an application component representing either anapplication's desire to perform a longer-running operation while notinteracting with the user or to supply functionality for other applications touse. Each service class must have a corresponding <service>declaration inits package's AndroidManifest.xml. Services can be started withContext.startService() and Context.bindService().
Note that services, like other application objects, run in the mainthread of their hosting process. This means that, if your service is going todo any CPU intensive (such as MP3 playback) or blocking (such as networking)operations, it should spawn its own thread in which to do that work. Moreinformation on this can be found in Processes and Threads. The IntentServiceclass is available as a standard implementation of Service that has its ownthread where it schedules its work to be done.
Service和其他组件一样,都是运行在主线程中的,因此不能用它来做耗时的操作。如果需要耗时的操作,那么你就应该在服务中开启另一个线程,在另外的线程中操作。
Service可分为两种:
1. 本地服务:Local Service用于应用程序内部。在Service可以调用Context.startService()启动,调用Context.stopService()结束。在内部可以调用Service.stopSelf()或Service.stopSelfResult()来自己停止。无论调用了多少次startService(),都只需调用一次stopService()来停止。
2. 远程服务:Remote Service用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
Service Lifecycle
There are two reasons that a service can be run by the system. Ifsomeone calls Context.startService() then the system will retrieve the service(creating it and calling its onCreate() method if needed) and then call itsonStartCommand(Intent, int, int) method with the arguments supplied by theclient. The service will at this point continue running untilContext.stopService() or stopSelf() is called. Note that multiple calls toContext.startService() do not nest (though they do result in multiplecorresponding calls to onStartCommand()), so no matter how many times it isstarted a service will be stopped once Context.stopService() or stopSelf() iscalled; however, services can use their stopSelf(int) method to ensure theservice is not stopped until started intents have been processed.
For started services, there are two additional major modes ofoperation they can decide to run in, depending on the value they return fromonStartCommand(): START_STICKY is used for services that are explicitly startedand stopped as needed, while START_NOT_STICKY orSTART_REDELIVER_INTENT are usedfor services that should only remain running while processing any commands sentto them. See the linked documentation for more detail on the semantics.
Clients can also use Context.bindService() to obtain a persistentconnection to a service. This likewise creates the service if it is not alreadyrunning (calling onCreate() while doing so), but does not callonStartCommand(). The client will receive the IBinder object that the servicereturns from its onBind(Intent) method, allowing the client to then make callsback to the service. The service will remain running as long as the connectionis established (whether or not the client retains a reference on the service'sIBinder). Usually the IBinder returned is for a complex interface that has beenwritten in aidl.
A service can be both started and have connections bound to it. Insuch a case, the system will keep the service running as long as either it isstarted or there are one or more connections to it with theContext.BIND_AUTO_CREATE flag. Once neither of these situations hold, theservice's onDestroy() method is called and the service is effectivelyterminated. All cleanup (stopping threads, unregistering receivers) should becomplete upon returning from onDestroy().
三、Broadcast receiver
broadcase receiver不执行任何任务,仅仅是接受并响应广播通知的一类组件。大部分广播通知是由系统产生的,例如改变时区,电池电量低,用户选择了一幅图片或者用户改变了语言首选项。应用程序同样也可以发送广播通知,例如通知其他应用程序某些数据已经被下载到设备上可以使用。
一个应用程序可以包含任意数量的boradcase reveiver来响应它认为很重要的通知。所有的broadcast receiver都扩展自类BroadcastReceiver。
broadcast receiver不包含任何用户界面。然而它们可以启动一个activity以响应接受到的信息,或者通过NotificationManager通知用户。可以通过多种方式使用户知道有新的通知产生:闪动背景灯、震动设备、发出声音等等。通常程序会在状态栏上放置一个持久的图标,用户可以打开这个图标并读取通知信息。
下面让我们看看BroadcastReceiver类的原文档说明:
ClassOverview
Base class for code that will receive intents sent bysendBroadcast().
If you don't need to send broadcasts across applications, considerusing this class with LocalBroadcastManager instead of the more generalfacilities described below. This will give you a much more efficient implementation(no cross-process communication needed) and allow you to avoid thinking aboutany security issues related to other applications being able to receive or sendyour broadcasts.
You can either dynamically register an instance of this class withContext.registerReceiver() or statically publish an implementation through the<receiver> tag in your AndroidManifest.xml.
Note: If registering a receiverin your Activity.onResume() implementation, you should unregister it in Activity.onPause().(You won't receive intents when paused, and this will cut down on unnecessarysystem overhead). Do not unregister in Activity.onSaveInstanceState(), becausethis won't be called if the user moves back in the history stack.
Thereare two major classes of broadcasts that can be received:
Normal broadcasts (sent with Context.sendBroadcast) are completely asynchronous. Allreceivers of the broadcast are run in an undefined order, often at the sametime. This is more efficient, but means that receivers cannot use the result orabort APIs included here.
Ordered broadcasts (sent with Context.sendOrderedBroadcast) are delivered to one receiverat a time. As each receiver executes in turn, it can propagate a result to thenext receiver, or it can completely abort the broadcast so that it won't bepassed to other receivers. The order receivers run in can be controlled withthe android:priority attribute of the matching intent-filter; receivers withthe same priority will be run in an arbitrary order.
四、Content provider
应用程序可以通过contentprovider访问其它应用程序的一些私有数据,这是Android提供的一种标准的共享数据的机制。共享的数据可以是存储在文件系统中、sqlite数据库中或其它的一些媒体中。content provider扩展自ContentProvider类,通过实现此类的一组标准的接口可以使其它应用程序存取由它控制的数据。然而应用程序并不会直接调用ContentProvider中的方法,而是通过类ContentResolver。ContentResolver能够与任何一个ContentProvider通信,它与ContentProvider合作管理进程间的通信。
任何时候当Android系统收到一个需要某个组件进行处理的请求的时候,Android会确保处理此请求的组件的宿主进程是否已经在运行,如果没有,则立即启动这个进程,当请求的组件的宿主进程已经在运行,它会继续查看请求的组件是否可以使用,如果不能立即使用,它会创建一个请求的组件的实例来响应请求。
ContentProvider是在应用程序间共享数据的一种接口机制,它使其它的应用程序保存或读取此ContentProvider的各种数据类型。
ContentProvider提供了更高级的数据共享方法,应用程序可以指定需要共享的数据,而其它应用程序则可以在不知数据来源、路径的情况下,提供了查询、添加、删除和更新共享数据等操作的接口。许多Android系统的内置数据也通过ContentProvider提供给用户使用,如通讯录、音视频文件和图像文件等。
ContentProvider的数据模式类似于数据库的数据表,每行是一条数据,每列具有相同的数据类型。每条记录都有一个long型的字段_ID,用来唯一标识每一条记录。ContentProvider可以提供多个数据集,调用者使用URI对不同的数据集的数据进行操作。
ContentProvider完全屏蔽了数据提供组件的数据存储方法。在创建ContentProvider时,需要先实现底层的存储功能,可以是数据库、文件系统或是网络存储等,然后在继承ContentProvider的类中实现基本数据操作的接口方法,包括添加、删除、查找和更新等功能。接口主要有以下几个:
1. query(Uri uri, String[] projection, Stringselection, String[] selectionArgs, String sortOrder):通过Uri进行查询,返回一个Cursor。
2. insert(Url url, ContentValues values):将一组数据插入Uri指定的地方。
3. update(Uri uri, ContentValues values,String where, String[] selectionArgs):更新Uri指定位置的数据。
4. delete(Uri uri, String where, String[]selectionArgs):删除指定Uri并且符合一定条件的数据。
调用者不能直接调用ContentProvider的接口方法,而需要使用ContentResolver对象,通过Uri间接调用ContentProvider。
ContentResolver接口是外界的程序访问ContentProvider提供的数据的桥梁,ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个:
1. query(Uri uri, String[] projection, Stringselection, String[] selectionArgs, String sortOrder):通过Uri进行查询,返回一个Cursor。
2. insert(Url url, ContentValues values):将一组数据插入Uri指定的地方。
3. update(Uri uri, ContentValues values,String where, String[] selectionArgs):更新Uri指定位置的数据。
4. delete(Uri uri, String where, String[]selectionArgs):删除指定Uri并且符合一定条件的数据。
开发者在Activity中通过getContentResolver()可以得到当前应用的ContentResolver对象,然后使用该ContentResolver对象与ContentProvider进行交互,而ContentResolver则通过Uri确定需要访问的ContentProvider数据集。在发起一个请求的过程中,Android首先根据Uri确定处理这个查询的ContentResolver,然后初始化ContentResolver所有需要的资源,这个初始化是由Android系统完成的。一般情况下只有一个ContentProvider对象,但却可以同时与多个ContentResolver在不同的应用程序和不同的进程中进行交互。Uri是通用资源标志符(Uniform
Resource Identifier),用来定位任何远程或本地的可用资源。
ContentProvider使用的Uri的语法结构:
content://<authority>/<data_path>/<id>