由于发现市面上的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可能声明如下:

<?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属性,表示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文件里。如:

<?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,把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

未完待续

更多相关文章

  1. Activity启动流程详解
  2. Android将胜过Windows Mobile五大原因
  3. Android(安卓)上的安全性
  4. Android核心模块及相关技术(自IT168)
  5. mk中的android:sharedUserId和LOCAL_CERTIFICATE作用
  6. android应用程序的安装位置
  7. 【OSC手机App技术解析】- Android完全退出程序
  8. Android启动过程深入解析
  9. 系出名门Android(4) - 活动(Activity), 服务(Service), 广播(Bro

随机推荐

  1. 【原创】Python处理海量数据的实战研究
  2. 从正则表达式中浏览和提取字符类
  3. Python自学,Day3-Python基础
  4. 报错 src/png_io.c:3:17: error: png.h:
  5. Python语法教程总结规范
  6. python中字典的操作
  7. Python:运算类内建函数列举
  8. Python学习总结-(15)---返回函数和闭包初步
  9. 卸载gcc,ubuntu系统崩溃解决
  10. Python语言及其应用 PDF