cheney23reg

技术博客

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Android程序是利用Java语言来开发的。编译完成的java代码、数据和资源文件是通过一个叫做aapt的工具进行打包,打包之后会生成一个.apk文件。最终用户可以将.apk文件安装在Android手机上; 一般情况下,一个.apk文件就被称为一个应用程序。


每一个Android应用程序都是运行在一个独立环境中的,这体现在很多方面:
* 默认情况下,每一个Android应用程序都是运行在它自己的linux进程中。当应用程序的任何部分代码需要被执行时,Android会启动这个进程来运行它;当不再需要这个应用(即进程)时,并且其它应用请求系统资源时,Android就会关闭这个进程。
* 每一个进程都拥有一个独立的虚拟机(VM)。所以每一个应用程序相对于其它的应用程序是运行在一个孤岛环境中的。
* 默认情况下,每一个Android应用程序都被分配了一个linux用户id,并且进行了相关的权限设置,所以应用程序的文件只是对本应用程序是可见的(当然,也是一些方式方法来将这些文件导出给其它的应用程序使用)。

多个应用程序是可以分配一个相同的用户id的,这样的话,它们就能够访问彼此的文件了。多个拥有相同用户id的应用程序也可以运行在同一个linux进程中,共用一个虚拟机,以节约系统资源。

1. Application Components

Android的一个主要特性就是一个应用程序可以使用其它应用程序的元素(如果其它应用程序许可)。
比如你的应用程序需要显示一个滚动列表的图像,并且另外的一个应用程序已经开发出了一个适合的滚动列表,而且允许这个滚动列表被其它的应用程序使用。那么你就可以调用那个滚动列表来完成工作,而不需要再去开发新的滚动列表。你的应用并不需要包含那个应用程序的代码,也不需要链接它。你只需在你的程序中需要显示滚动列表的时候,启动那个应用程序的滚动列表的代码片段即可。

为了保证上述机制正常工作,当一个应用程序的某部分被请求和需要时,系统必须能够启动一个应用程序进程,并且初始化代表那一部分的Java对象。所以,和大多数其它的系统不同,Android应用程序没有一个唯一的入口(比如说 main 函数),而是拥有一些系统能够初始化和运行的基本组件。以下是4种基本组件的说明:

1) Activities

一个 activity 代表一个可视化的用户接口(An activity presents a visual user interface for one focused endeavor the user can undertake)。比如说,一个activity可能代表菜单项的列表,用户可以从中选择,或者这个列表显示一些带有标题的照片。一个文本短信的应用程序可能有一个显示通讯录列表的activity,还有一个给指定联系人编写短信的activity,并且可能还有查看以前的短信或是设置的一些activity。虽然它们共同组成了一个紧密结合的用户界面,但是每一个activity都不依赖其它activity。每一个activity都继承(extends)android.app.Activity类。

一个应用程序可能只是由一个activity构成,或者由多个activity构成,就像刚刚提到的文本短消息应用程序。具体一个activity是什么,以及有多少个activity,是由应用程序的设计决定的。典型情况下,有一个activity被标记为主界面(即当系统启动程序后第一个展现在用户面前的界面)。从一个activity跳转到另一个activity是由当前activity发起的。

每一个activity都被赋予一个默认的窗口来进行绘画。典型情况下,窗口会占满整个屏幕,但是也可以比屏幕小和漂浮在其它窗口之上。一个activity也可以使用副窗口(additional windows),比如一个要求用户响应的弹出对话框出现在activity的正中间,或者当用户选择了一个条目之后,出现在用户面前显示重要信息的窗口。

窗口中可视的内容都是android.view.View类的子类(如TextView)的对象每一个view控制一个位于窗口中的指定矩形区域。父view包含和组织它的子view的布局。叶子view(即在View继承树的最末端)在它自身的控制的矩形区域内进行绘制,并且对用户在这个矩形区域内的动作做出直接响应。所以,view就是activity与用户进行交互的场所。比如一个view显示一张小图片,并且当用户点击这张图片的时候触发一个行为(事件)。Android已经内置了一些view,包括按钮,输入框,滚动条,菜单项和多选框 等等。

通过调用android.app.Activity.setContentView(View)方法,我们可以给一个activity窗口设置一个完整的View层次树。其参数是这个View层次树的最高层的祖先。(想了解更多关于view及其继承关系的信息,请阅读 用户界面 部分)

2) Services
一个Services不拥有可视化的用户界面,它是在一段不确定的时间周期内在后台运行。比如某个service在后台播放音乐,或者是在后台获取网络数据,又或者是进行某个耗时的运算并且将运算结果提供给需要的activity。每一个具体的service是从android.app.Service基类继承的。

最经典的例子就是一个音乐播放器播放播放列表中的歌曲。这个播放器可能拥有一个或多个activity,比如让用户选择歌曲的activity和播放时的activity等等。然而,真正的播放功能可能并不是由某一个activity来负责的,因为用户可能期望就算跳转到任何一个不同的activity都保持歌曲持续不断地播放。为了达到这个目的,这个播放器的activity需要启动一个负责播放的service。系统会使这个service在后台持续不断地运行,哪怕切换到一个新的activity。

我们也可以连接上(绑定)一个正在运行的service(或者是启动它,前提是这个service还没有运行)。当连接上之后,你就可以通过这个service导出的接口来与这个service进行通信。针对于播放歌曲的service来说,导出接口可能包括暂停播放、倒退、停止播放和重新播放。

和activity以及其它组件类似,service也是运行在应用程序进程的主线程中的。所以为了不阻塞其它组件的运行和用户界面的响应,service经常会创建一个新的线程来进行耗时操作(比如像播放音乐)。具体信息请参考"进程和线程"部分。

3) Broadcast receivers
broadcast receiver是一个只能对广播通告进行接收和响应的组件。有很多的broadcast是由系统发起的,比如说当手机设置的时区发生改变的时候,手机电力不足,某一张图片被移除,又或者是用户切换了手机显示语言。应用程序也可以发起broadcast,比如当一个应用程序成功下载完某些数据后,通知其它的应用程序数据已经准备好。

一个应用程序可以利用任意多的Broadcast receiver来对它感兴趣的广播事件来进行响应。所有的Broadcast receiver都是从 android.content.BroadcastReceiver 类继承下来的。

broadcast receiver并不会显示用户界面。然而,它们可以启动一个activity来将关于接收的广播信息提示给用户,或者它们也可以使用 通知管理器(android.app.NotificationManager) 来提示用户。一个通知有很多途径来引起用户注意,比如点亮手机的背光灯,使手机振动,播放一个提示音等等。经典情况是在状态栏上显示一个小图标,用户可以点击这个小图标以获取详细信息。

4) Content providers
Content provider 的作用是将应用程序指定的数据共享给其它的应用程序。这些数据可以是存放在文件系统中的,或是存放在SQLite数据库文件中,或是以其它任何有意义的方式存放的。一个具体的 Content provider 是继承于 android.content.ContentProvider 类的,并且实现一系列可以使其它应用程序接收和存储由此应用程序控制的数据的相关接口即可。然而应用程序并不会直接调用这些接口,它们会利用 android.content.ContentResolver 类的对象来做这个工作,一个 ContentResolver 可以同任何 Content provider 进行通信;它与 Content provider 一起对相关的进程间通信进行管理。

关于如何使用 Content provider 的更多资料,可以参考 Content Providers 章节的内容。

注:任何时候当出现一个应该被某个组件处理的请求,Android都会保证这个组件在应用程序进程中保持运行,必要的话,还会启动这个组件(必要情况下会创建这个组件对象)使其运行,所以Android也会保证存在这个组件的实例对象。


1.2 Activating components: intents
当 ContentResolver 发出了一个请求,那么这个请求中所指定的目的 Content providers 会被自动激活。另外三种组件 -- activity, service broadcast receiver -- 是被一种称为 目的消息(intent) 的异步消息来激活的。一个目的消息是从 android.content.Intent 类继承的,并且包含了一些内容信息。

对于 activity 和 service 来说,目的消息指定了请求行为和此行为所针对的数据的URI,以及其它信息。比如它可能传达一个让某个activity显示图片的请求,又或者让用户对某些文本信息进行编辑的请求。

对于 broadcast receiver 来说,intent 对象指定了被广播的行为。比如它可能将拍摄按钮被按下这个事件(行为)宣布给感兴趣的broadcast receiver。


以下是一些激活某一种组件的独立方法:


1)通过将某个intent对象传递给 android.content.Context.startActivity(Intent)android.app.Activity.startActivityForResult(Intent, int) 方法,可以启动一个activity(或者做一些别的操作)。负责响应的activity(目标activity)可以调用 android.app.Activity.getIntent() 方法来获取这个Intent对象。若在这之后有新到达的intent,Android系统会调用 android.app.Activity.onNewIntent(Intent) 方法。

通常情况下,一个activity会负责启动下一个activity,如果想从下一个activity获取启动结果码,可以调用 android.app.Activity.startActivityForResult(Intent, int) 方法。比如说启动了一个让用户挑选图片的activity,那么这个activity可能需要将用户的选择返回给其它的activity。结果码是通过 android.app.Activity.onActivityResult(int, int, Intent) 这个方法返回给调用activity的。

2)通过将某个intent对象传递给 android.content.Context.startService(Intent) 方法可以启动一个service(或者给正在运行的service传递新的指令)。Android会调用这个service的 android.app.Service(Intent, int) 方法,并传入这个Intent对象。
与此类似,也可以通过将一个Intent传递给 android.content.Context.bindService(Intent, ServiceConnection, int) 方法来与一个正在运行的service建立连接。系统会调用目标service的 android.app.Service.onStart(Intent, int) 方法,并将这个Intent对象传入(如果service还没有被启动的话,bindService 方法可以有选择地启动它)。比如一个activity可以和负责播放音乐的service建立连接,这样的话,这个activity就可以给用户提供一些用以控制播放行为的接口。这个activity需要首先调用 bindService() 来与service建立连接,然后就可以调用被service定义的方法来控制播放行为了。

关于和 service 绑定的更详细的信息,在后面的 "远程过程调用" 章节中被提到。

3)应用程序可以通过将一个 Intent 对象传递给android.content.Context.sendBroadcast(Intent)方法,或者android.content.Context.sendOrderedBroadcast(Intent, String)方法,或者android.content.Context.sendStickyBroadcast (Intent)方法以及它们任何的变体形式来产生一个广播。Android系统会通过调用 android.content.BroadcastReceiver.onReceive (Context, Intent)方法来将这个广播公告给所有感兴趣的 BroadcastReceivers。

关于 目的消息 更多更详细的资料,请参考 "目的消息和目的消息筛选器" 章节。


1.3 Shutting down components
一个content provider只有在响应一个来自ContentResolver的请求的时候才是激活状态。类似的,一个broadcast receiver只有在接收适当的广播消息的时候才是激活状态。所以没有必要显式地关闭这两种组件。

提供用户界面的activity则会一直保持激活状态以便与用户进行交互。service也是会一直保持激活状态以便在后台完成指定的任务。所以Android提供了方法来有序地显式关闭activity和service。

调用android.app.Activity.finish()方法可以关闭自己(activity)。一个activity可以调用android.app.Activity.finishActivity(int)方法来关闭另一个activity(由startActivityForResult()方法激活)。

通过调用android.app.Service.stopSelf()方法可以关闭service,也可以调用android.content.Context.stopService(Intent)方法来关闭。

当组件不再被使用时,或者Android需要激活更多组件从而需要系统资源的时候,组件也可能被系统关闭。在后面的 "组件生命周期" 的章节中会讨论这种情况和更详细的信息。

1.4 The manifest file
要让Android能启动一个组件,首先必须要让Android知道这个组件是存在的。应用程序在manifest文件中声明它所用到的组件,这个manifest文件也会绑定到 .apk文件中。

所有程序的manifest文件的名字都是 AndroidManifest.xml,并且都是xml格式。除了声明程序需要用到的组件之外,这个AndroidManifest.xml 也有很多其它功能,比如声明程序运行时需要用到的非默认链接库,标识程序权限。

AndroidManifest.xml 最重要的功能还是声明程序需要用到的组件。一个activity可能就是如下的声明:

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
<application . . . >
<activity android:name="com.example.project.FreneticActivity"
android:icon
="@drawable/small_pic.png"
android:label
="@string/freneticLabel"
. . .
>
</activity>
. . .
</application>
</manifest>


<activity>
标签中的name属性表明从 android.app.Activity类继承下来的具体的activity类的名字。icon属性和label属性表示包含了在这个activity中显示的图片和文本的资源文件。

与此类似,<service>标签代表一个service组件, <receiver>标签代表一个broadcast receiver组件,而<provider>标签代表一个content provider组件。

Activities,services,和content provider这3中组件若没有在 AndroidManifest.xml文件中声明则不可用。然而,broadcast receiver不但可以声明在AndroidManifest.xml 文件中,还可以在代码里动态创建它们(如android.content.BroadcastReceiver对象),然后调用android.content.Context.registerReceiver(BroadcastReceiver, IntentFilter)方法将其注册到系统中。

关于manifest文件更详细的信息,请参考 "AndroidManifest.xml文件" 章节。

1.5 Intent filters (Intents 筛选器)
一个 Intent 对象可以显式的指定目标组件的名字,这样的话,Android就会找到这个组件(通过manifest文件中的生命)并且激活它。但是如果没有显式的指定目标组件的名字,Android就要负责找到一个最合适的组件来响应这个Intent。Android是通过将这个Intent对象和所有可能的目标组件的intent filter进行比较来筛选出最合适的组件。一个组件的intent filter说明了这个组件可以处理的intent类型。intent filter也是在 manifest 文件中声明。示例如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
<application . . . >
<activity android:name="com.example.project.FreneticActivity"
android:icon
="@drawable/small_pic.png"
android:label
="@string/freneticLabel"
. . .
>
<intent-filter . . . >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter . . . >
<action android:name="com.example.project.BOUNCE" />
<data android:mimeType="image/jpeg" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

</activity>
. . .
</application>
</manifest>

 


示例中的第一个filter是"android.intent.action.MAIN"行为和"android.intent.category.LAUNCHER"类型的组合,这也是一种常用的filter。它表示这个activity是当用户选择启动这个应用程序的时候显示的第一个界面。

第二个filter声明表示这个activity可以处理指定类型的数据的行为。

一个组件可以用户任意多的intent filter,每一个filter声明了一组不同的能力。若组件没有没有声明任何intent filter,则需要在Intent对象中显式指明此组件的名字,才能激活这个组件。

对于在代码中进行创建和注册的broadcast receiver,一个intent filter直接是由一个android.content.IntentFilter对象来表示的。其余情况,filter都是在manifest文件声明的。

关于intent filter的更详细的信息,请参考 "Intent and Intent Filters" 章节。

posted on 2010-08-22 00:38  cheney23reg  阅读(846)  评论(2编辑  收藏  举报