Android doc 之应用程序基础

来源:互联网 发布:软件开发者路线图 编辑:程序博客网 时间:2024/05/26 02:53

由于发现市面上的Android教程很肤浅,还是官方的文档比较好。从今天开始,重新看一遍Android的官方文档,深入了解Android的体系结构

 

 

应用程序基础

 

Android应用程序是通过Java语言编写的。这些被编译的Java代码,还有应用程序所需要的其他数据和资源文件,通过appt工具写进Android包里---即一个以.apk文件结尾的文件。这个文件是安装和发布程序的一种手段。在一个.apk文件里的代码可以被认为是一个程序。

不同的Android应用程序都活在它自己的世界里面:

  • 默认情况下,每一个程序都在一个独立的Linux进程当中运行。当程序执行时Android启动进程,当程序很长时间没有用或者系统资源需要被其他程序使用的时候,关闭进程
  • 不同的进程都有它自己的虚拟机(VM),所以应用程序代码和其他的应用程序代码没有多大相关
  • 默认情况下,不同的应用程序被赋予一个唯一的Linux User ID。这个可以用来设置权限,使得应用程序的文件仅仅只对用户或者应用程序本身可见---虽然也有很多方式把它暴露给其他应用程序。

当两个应用程序希望看到对方的文件的时候,可以给它们赋予相同的User ID。为了保存节省系统资源,相同ID的应用程序允许在同一个Linux进程当中执行,以共享相同的VM。

 

应用程序组件

      Android的一个主要特征就是一个应用程序可以充分启用其他进程的元素(假设其他进程允许)。比如,如果你的应用程序需要显示一个滚动的图像列表,而且有一个已开发了的程序为其他程序提供了可用的功能,你可以调用它去实现你的功能,而不需要去自己独立开发一个。不像其他大多数的其他系统,Android的应用程序没有一个单一的入口点,他们有基本的组件,系统可以再需要的时候去调用他们。系统有以下4个类型的组件:

  • Activity

一个Acitivity是一个用户可看见虚拟的UI,即类似C#里的Form或者Java里的JFrame。一个应用程序由一个或者多个的Activity构成,其中,只有一个应用程序被设置为用户启动的UI,即主窗口。Activity里的内容是通过一层层的View构成的,它们都继承与View这个类。每个View控制着窗口的一个特定的区域。而Parent View容纳和组织了Child View的布局。Activity可以通过Activity.setContentView去设置它的布局。

  • Service

Service跟Activity类似,但它没有UI,它是在后台执行的一个程序。比如,一个Service可能在后台播放音乐,不同的Service都必须从Service类继承。我们可以连接一个正在运行的Service,使用它所提供的服务,比如,电话进来的时候,停止播放音乐,这个时候可以向MusicService发送一个暂停播放的一个请求。

  • Broadcast Receiver

BroadcastReceiver是一个专门用于接收广播或者通告的一个类。系统代码里有很多的广播,比如电量低,新短信等。应用程序也可以创建一个广播,比如,让其他程序知道一些数据已经下载好并可以使用。一个应用程序对不同的广播可以有任意数据的BroadcastReceiver。所有的Receiver都必须继承BroadcastReceiver基类。

BroadcastReceiver并不显示UI。然而,他们可以针对所收到的信息启动一个Activity,或者他们可以使用NotificationManager去提醒用户。提醒有很多种方式:手机背景变亮,振动,播放声音等等。但比较典型的做法是放置一个icon到状态栏,用户可以打开它获取所需的信息。

  • Content Providers

content provider使得指定的应用程序数据能够和其他程序共享。这个数据可以存储在文件系统,在SQLite数据库,或者其他方式。content provider必须继承ContentProvider基类去实现一套标准的方式,使得其他应用程序可以获取和存储数据,并控制它。然而应用程序并不通过直接调用这些方法去实现它们,而是使用一个ContentResolver对象去调用这些方法。一个ContentResolver可以和任何的content provider进行通讯。

 

激活组件:intents

Content Providers是通过ContentResolver激活的。其他的三个组件:activities,services,broadcast receivers,通过一个叫intents的异步消息。一个intent是一个带有消息内容的Intent对象。

有以下的几种方法去激活不同类型的组件:

  • 一个Activity通过传递一个Intent对象给Context.startActivity()或者Context.startActivityForResult()方法去启动。相应的activity可以通过getIntent()方法获取到启动它的Intent。Android调用activity的onNewIntent方法去传递一个intent。

一个Activity通常是启动另外一个Activity,为了获取它所启动的其他activity的数据,可以用startActivityForResult()带起startActivity()去启动其他activity。比如,如果它启动了一个activity让用户去挑选一张图片,它可能希望返回选择的图片。这个返回的结果可以通过Intent对象传递给activity的onActivityResult方法。

  • 一个service通过传递一个Intent对象给Context.startService()启动。Android调用service的onStart()方法,并且传递Intent对象。

类似的,一个intent也可以传递给Context.bindService()去建立调用目标service的连接。这个service在onBind()方法里收到一个Intent对象。(如果service还没启动,则bindService会启动它。)。例如,一个activity可能建立了之前提到的音乐播放service的连接,所以这个service可以为用户提供控制播放的接口。

  • 一个应用程序可以通过传递Intent对象给类似Context.sendBroadcast或者Context.sendOrderedBroadcast和Context.sendStickyBroadcast() (还有很多变种)去创建一个广播。

 

关闭组件

content provider仅仅在响应ContentResolver的请求时是活动的(active).broadcast receiver仅当收到一个广播消息时是活动的,所以没有必要去显示关闭这些控件。

android为activities、services提供了关闭的方法

此情况不详细描述,Activity------finish()。一个activity通过startActivityForResult()调用另一个activity时可以使用finishActivity()关闭被调用的activity

   Service------stopSelf或Context.stopService()

这些组件也有可能在很久没使用或者系统需要回收内存时被关闭。

 

manifest 文件

在Android启动一个应用程序组件之前,它必须知道这个组件的存在。因此,应用程序在manifest文件中声明了它们的组件,并把他们写进android包里,.apk文件也同样拥有这些代码、文件、资源。

manifest是一个结构化的XML文件,而且总是命名为AndroidManifest.xml。它除了声明程序的组件以外做了很多事情,比如,声明程序所需要连接的库,和声明程序所需要赋予的权限。

但manifest文件的首要任务是告诉Android的应用程序的组件,比如,一个activity可能声明如下:

 

<activity>的name属性,表示Activity子类的名称。icon和label属性指出可以显示给用户的资源文件,包含的图标和标签(即文字)。

其他的组件也是以类似的方式声明的,services是用<service>声明,broadcast receivers用<receiver>声明,content providers用<provider>声明。没有在manifest文件里声明的activities,services,content provider对系统是不可见的,因此也无法运行。然而,broadcast  receivers可以声明在manifest,或者在代码中中袋创建(作为一个BroadcastReceivers对象),并且通过调用Context.registerReceiver()向系统注册。

 

Intent filters

 

一个Intent对象可以显式的指定一个目标对象。如果显式指定的话,Android可以通过它找到那个对象(在manifest文件里)并激活它。但如果目标没有显示指定,Android必须找到最适合这个Intent的对象。它通过比较Intent对象的intent filters寻找潜在的目标。一个组件的intent filters告诉Android各种各样可处理的intent。比如组件的基本信息,他们可以声明在manifest文件里。如:

 

例子里的第一个filter,把action-"android.intent.action.MAIN"和category-"android.intent.category.LAUNCHER"结合起来,这是常见的一个。它标记了这个activity是这个应用程序的入口点,用户可以在选择启动程序时看到它。

第二个filter声明了activity可以执行的特别类型的action

一个组件可以有任意数目的intent filter,不同的filter声明了不同的能力结合。如果activity没有任何filter,它只能通过intent显式指定目标名称去激活。

对于在代码里创建和注册了的broadcast receiver,intent filter可以直接实例化为IntentFilter对象。其他所有的filter都应该在manifest里建立。

 

[以下内容摘自:http://blog.csdn.net/infsafe/article/details/5668971]

 

Activity 和 Task

 

之前提到的,一个 Activity 可以启动另一个,即便是定义在不同应用程序中的 Activity 。例如,假设你想让用户显示一些地方的街景。而这里已经有一个 Activity 可以做到这一点,因此,你的 Activity 所需要做的只是在Intent 对象中添加必要的信息,并传递给 startActivity() 。地图浏览将会显示你的地图。当用户按下 BACK键,你的 Activity 会再次出现在屏幕上。

 

对于用户来说,看起来好像是地图浏览与你的 Activity 一样,属于相同的应用程序,即便是它定义在其它的应用程序里,并 运行在那个应用程序的进程里。 Android 通过将这两个 Activity 保存在同一个 Task 里来体现这一用户体验。简单来说,一个 Task 就是用户体验上的一个“应用”。它将相关的 Activity 组合在一起,以 stack的方式管理。 stack 中根 Activity 启动 Task ——典型的,它就是用户在应用程序启动栏中选择的 Activity 。位于 stack 顶端的 Activity 是当前正在运行的——能够聚焦用户的动作。当一个 Activity 启动另一个,新的Activity 进入 stack ;它成为正在运行的 Activity 。之前的 Activity 仍保留在 stack 中。当用户按下 BACK 键,当前的 Activity 从 stack 中退出,之前的那个成为正在运行的 Activity 。

 

stack 包含对象,因此,如果一个 Task 中有多个同一个 Activity 的实例时——多个地图浏览,例如—— stack 为每个实例拥有一个独立的入口。位于 stack 中的 Activity 不会重新调整,只是进入和退出。

 

一个 Task 就是一组 Activity ,不是一个类或者在 manifest 中定义的一个元素。因此,没有办法为 Task 设置独立于它的 Activity 的属性值。 Task 的值作为整体在根 Activity 中设置。例如,下一个章节会讨论 Task 的“affinity ”;那个值就是从 Task 中的根 Activity 中读取的。

 

Task 中的所有 Activity 作为一个单元一起移动。整个 Task (整个 Activity stack )可以进入前台或者退到后台。例如,假设当前 Task 中的 stack 中有 4 个 Activity —— 3 个位于当前 Activity 下方。用户按下 HOME键,进入到应用程序启动栏,然后选择一个新的应用程序(实际上, 一个新的 Task )。当前 Task 退到后台,并且新 Task 中的根 Activity 会显示出来。然后,经过一段时间后,用户回到 Home 画面,然后再次选择前一个应用程序(前一个 Task )。那个拥有 4 个 Activity 的 Task 会进入前台。当用户按下 BACK 键,屏幕不会显示用户刚刚离开的 Activity (前一个 Task 的根 Activity )。而是,这个 stack 中的顶端 Activity 移除,相同Task 中的前一个 Activity 会显示出来。

 

刚才描述的行为是 Activity 和 Task 的默认行为。但有方法来完全改变它。 Task 之间的关联,和一个 Task 中的一个 Activity 行为,受启动 Activity 的 Intent 对象中设置的 Flag 和 manifest 文件中 Activity 的 <activity> 元素的特性值交互控制。调用者和响应者都有权决定如何发生。

 

核心的 Intent Flag 有:

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

FLAG_ACTIVITY_SINGLE_TOP

 

核心的 <activity> 特性有:

taskAffinity

launchMode

allowTaskReparenting

clearTaskOnLaunch

alwaysRetainTaskState

finishOnTaskLaunch

 

接下来的章节将描述一些 Flag 和特性的用法,如何相互影响,以及在使用时的建议。

 

Affinity 和新 Task

默认情况下,一个应用程序中的所有 Activity 都有 affinity ——也就是说,属于同一个 Task 中所有 Activity 有一个设定。然而,每个 Activity 都可以在 <activity> 元素的 taskAffinity 特性上设置单独的值。定义在不同应用程序中的 Activity 可以共享同一个 affinity ,或者定义在同一个应用程序中的 Activity 设置不同的 affinity 。Affinity 在两种环境下工作: Intent 对象包含 FLAG_ACTIVITY_NEW_TASK 标志,和 Activity 的allowTaskReparenting 特性设置为“ true ”。

FLAG_ACTIVITY_NEW_TASK:

之前描述的,一个 Activity 一般通过调用 startActivity() 启动并加入到 Task 中。它同调用者一样,进入同一个 Task 。然而,如果传递给 startActivity() 的 Intent 对象中包含 FLAG_ACTIVITY_NEW_TASK 时,系统会搜索一个新的 Task 来容纳新的 Activity 。通常,如标志的名字所示,是一个新的 Task 。然而,并不是必须是。如果已经存在一个 Task 与新 Activity 的 affinity 相同,这个 Activity 就会加入到那个 Task中。如果不是,启动一个新的 Task 。

allowTaskReparenting :

如果一个 Activity 的 allowTaskReparenting 特性设置为“ true ”,它就能从启动的 Task 中移到有着相同affinity 的 Task (这个 Task 进入到前台的时候)。例如,在一个旅游的程序中定义了一个可以报 告选择城市的天气情况的 Activity 。它和同一个应用程序的其它 Activity 一样,有着相同的 Affinity (默认的Affinity ),并且它允许重新宿主。你的 Activity 中的一个启动了天气预报,因此,它初始化到和你Activity 相同的 Task 中。然而,当旅游应用程序下一次进入到前台时,天气预报那个 Activity 将会重新编排并在那个 Task 中显示。

 

如果从用户的角度出发,一个 .apk 文件包含多个“应用”的话,你可能希望为关联的 Activity 设置不同的affinity 。

 

Launch Mode

 

这里 4 种不同的启动模式可以设置到 <activity> 元素的 launchMode 特性上:

standard (默认模式)

singleTop

singleTask

singleInstance

 

这些模式有以下四点区别:

1、哪个 Task 将容纳响应 Intent 的 Activity 。对于“ standard ”和“ singleTop ”来说,是产生 Intent 的那个 Task (并调用 startActivity() )——除非 Intent 对象包含 FLAG_ACTIVITY_NEW_TASK 。在那种情况下,不同的 Task 将被选择,如“ Affinity 和新 Task ”中描述的那样。对比而言,“ singleTask ”和“singleInstance ”指示 Activity 总是一个 Task 的根。它们定义一个 Task ;它们不会加入到另一个 Task中。

2、是否有多个 Activity 的实例。“ standard ”和“ singleTop ”可以实例化多次。它们可以属于多个 Task ,一个特定的 Task 可以有相同 Activity 的多个实例。对比而言,“ singleTask ”和“ singleInstance ”只能有一个实例。因为这些 Activity 只能位于 Task 的底部,这一限制意味着在设备的某个时间,不会出现这样 Task 的多个实例。

3、是否可以在同一个 Task 中拥有其它的 Activity 。“ singleInstance ” Activity 保持单身,在它的 Task 中它是仅有的 Activity 。如果它启动另一个 Activity ,那个 Activity 将会放入到不同的 Task 中,而不管它的启动模式——好像 FLAG_ACTIVITY_NEW_TASK 在 Intent 中一样。对于其它方面,,“ singleInstance”等同于“ singleTask ”。其它三个模式允许多个 Activity 加入到这个 Task 中。“ singleTask ”Activity 总是位于 Task 的底部,但它可以启动其它的 Activity 并放入到它的 Task 中。“ standard ”和“singleTop ”的 Activity 可以出现在 stack 的任何地方。

4、是否一个新的实例启动来处理新的 Intent 。对于默认的“ standard ”来说,都是创建一个新的实例来响应新的 Intent 。每个实例处理一个 Intent 。对于“ singleTop ”来说,如果它位于目标 Task 的顶端,那么,已经存在的实例就可以重复使用来处理这个新的 Intent 。如果它不在顶端,那么它就不能重复使用。替代的,新的实例将创 建来响应新的 Intent ,并进入到 stack 中。

例如,假设一 个 Task 的 Activity stack 中包含根 Activity A 和其它 Activity B , C , D ,并且 D 位于顶端,因此, stack 是 A-B-C-D 。有一个 Intent 来了,它要启动 D 类型的 Activity 。如果 D 有默认的“standard ”启动模式,那么,一个新的实例将被启动并且 stack 变成 A-B-C-D-D 。然而,如果 D 的启动模式“ singleTop ”,已经存在的实例将去处理新来的 Intent (因为它正好处在 stack 的顶端),并且 stack依旧是 A-B-C-D 。

换句话说,如果来临的 Intent 是冲着 B 类型的,那么, B 类型的实例将被创建启动而不管 B 的模式是“standard ”或“ singleTop ”(因为 B 不处在 stack 的顶端),因此, stack 将会是 A-B-C-D-B 。

之前提到的,设备上不会出现超过一个实例的“ singleTask ”或“ singleInstance ” Activity ,因此,那个实例都将去处理所有新来的 Intent 。“ singleInstance ” Activity 总是位于 stack 的顶端(因为它是task 中唯一的 Activity ),因此,它总是处于能处理 Intent 的位置。然而,“ singleTask ” Activity 可能有或没有其它 Activity 处于它的上方。如果有,它就不处于能处理 Intent 的位置,那么,这个 Intent 将被丢弃。(即使 Intent 被丢弃了,它的到来会引发那个 Task 进入到前台,在那里,它会继续保留。)

 

当一个存在的 Activity 请求去处理一个新的 Intent 时, Intent 对象将传到该 Activity 的 onNewIntent() 的方法中。(原来启动 Activity 的 Intent 对象可以通过调用 getIntent() 得到。)

 

注意:当一个新的实例创建来处理新的 Intent 时,用户可以按下 BACK 键返回到之前的状态(前一个 Activity)。但一个存在的实例来处理新的 Intent 时,用户不能按下 BACK 键返回到新 Intent 到来之前的状态。

 

清除 stack

如果一个任务栈在很长的一段时间都被用户保持在后台的,那么系统就会将这个任务栈中除了根activity以外的其它所有activity全部清除掉。从这之后,当用户再将任务栈切换到前台,则只能显示根activity了。
以上说的是默认模式,可以通过<activity>标签的一些属性来更改:
    1)alwaysRetainTaskState属性
    如果将根activity的alwaysRetainTaskState属性设置为“true”,则即便一个任务栈在很长的一段时间都被用户保持在后台的,系统也不会对这个任务栈进行清理。
    2)clearTaskOnLaunch属性
    如果将根activity的clearTaskOnLaunch属性设置为“true”,那么只有这个任务栈切换到了后台,那么系统就会将这个任务栈中除了根activity以外的其它所有activity全部清除掉。即和alwaysRetainTaskState的行为完全相反。
    3) finishOnTaskLaunch属性
    这个属性的行为类似于clearTaskOnLaunch,但是此属性作用于单个的activity对象,而不是整个任务栈。当这个任务栈切换到了后台,这个属性可以使任务栈清理包括根activity在内的任何activity对象。
    这里也有另一种方法来使activity对象从任务栈中被移除。若Intent对象包含FLAG_ACTIVITY_CLEAR_TOP标志,并且在目标任务栈中已经存在了用于处理这个Intent对象的activity类型的一个实例,那么在任务栈中这个实例之上的所有activity实例会被移除。从而用于处理这个Intent对象的activity类型的那个实例会位于任务栈的栈顶,并用来处理那个Intent对象。若那个匹合的activity类型的启动模式是“standard”,则这个已经存在于任务栈中的匹合的activity类型的实例也会被移除,并且一个新的此类型activity的实例被创建并压栈来处理这个Intent对象。
    FLAG_ACTIVITY_CLEAR_TOP这个标志经常和FLAG_ACTIVITY_NEW_TASK标志结合使用,这样结合使用的意思是在另一个任务栈中定位已经存在的匹合的activity类型的实例,并且让此实例位于栈顶。

 

启动 Task

如果一个 Activity 的 Intent Filter 的 action 为“ android.intent.action.MAIN ”、 category 为“android.intent.category.LAUNCHER ”时,它就可以作为一个 Task 的入口点。有这种类型的 Filter 会在导致这个 Activity 在应用程序启动栏显示一个图标和标签,给用户提供一个方式可以启 动这个 Task 和在任何时候可以再次回到这个 Task 。

 

第二个能力很重要:用户一定可以离开一个 Task ,然后可以再次回到它。基于这个原因,两个启动模式,“singleTask ”和“ singleInstance ”应该只在有 MAIN 和 LAUNCHER 的 Activity 上使用。例如,假设这个Filter 没有的话:一个 Intent 启动了一个“ singleTask ” Activity ,初始化一个新的 Task ,然后用户花费了一些时间在它上面。然后,用户按下 HOME 键。现在,这个 Task 处于后台并且被 HOME 画面遮盖。由于它不能在应用程序启动栏显示,用户就没有办法可以 返回它。

 

在面对 FLAG_ACTIVITY_NEW_TASK 时,也有相似的困难。如果这个标志导致一个 Activity 启动了一个新的Task ,并且用户按下 HOME 键离开它,这里必须有方法可以再次回到它。一些机能(如 Notification Manager)总是在外部的 Task 中启动 Activity ,而不是作为自己的一部分,因此,它总是把FLAG_ACTIVITY_NEW_TASK 标志放入 Intent ,然后传递给 startActivity() 。如果你的 Activity 可能会被外部的机能(可能使用这个标志)调用,注意用户可以额外 的方式可以返回到启动的 Task 。

 

如果你不想用户回到某个 Activity ,可以把 <activity> 元素的 finishOnTaskLaunch 设置为“ true ”。

 

 

 区分Activity的四种加载模式:
http://marshal.easymorse.com/archives/2950
反编译apk:
http://marshal.easymorse.com/archives/3051

 

未完待续