Android应用程序模型

Android 应用程序模型:应用程序,任务,进程和线程

 

大多数操作系统,在应用程序所寄存的可执行程序映像(如 Windows 系统里的 .exe )、它所运行的进程以及和用户交互的图标和应用之间有一种严格的 1 1 关系。在 Android 系统里,这些关联要松散得多。并且重要的是要理解各种概念怎么样组成整体。

由于 Android 应用固有的灵活性,当实现这些不同方面的时候有一些基本术语需要加以理解:

  • 一个 Android   .apk )文件,其中包含一个应用程序的代码和资源。这是应用程序分发和下载的文件,用户用来安装该应用程序在他们的设备上。
  • 一个 任务 一般而言是指用户视为的一个可启动应用程序:通常任务在桌面( home screen )有一个可访问的图标,且可以被切换到前台。
  • 一个 进程 是一个运行着应用程序代码的底层核心过程。通常所有 .apk 里的代码运行在一个专有的进程里。不过, 进程 标记也可以用来限定代码运行位置,或者为 整个 .apk 或者为个别的活动 activity ,接收者 receiver 服务 或提供者 provider ,组件。

任务

这里的一个关键点是: 当用户看到一个 应用 时,他们实际上在和任务打交道 。如果您刚刚创建一个包含若干活动的 .apk ,其中之一是顶层入口点(通过动作 android.intent.action.MAIN 的意图过滤器 intent-filter 和类别 android.intent.category.LAUNCHER ),那么这事实上将为您的 .apk 创建一个任务,并且您从那儿起动的任何活动都将作为那个任务的一部分运行。

一个任务,那么,从用户的角度来看是您的应用程序 ; 而从应用程序开发者的角度来看,它是一个或多个用户在那个任务中已经经历过且未关闭的活动,或者说是一个活动栈。一个新的任务通过以 Intent.FLAG_ACTIVITY_NEW_TASK 标志起动一个活动意图来创建 ; 这一意图将被用来作为任务的根意图,定义任务是什么。任何不以这个标志起动的活动将和起动它的活动在相同的任务中运行(除非该活动已请求特别启动模式,稍后会讨论)。任务可以被重新安排:如果您使用 FLAG_ACTIVITY_NEW_TASK 标志但已经有一个任务以这个意图运行,则当前任务的活动栈将被切换到前台而不是开始一个新的任务。

 

FLAG_ACTIVITY_NEW_TASK 必 须谨慎使用:使用它意味着,在用户看来,一个新的应用程序由此起动。如果这不是你所期望的行为,你就不该去创建一个新的任务。另外,仅在用户可以从桌面返 回到他原来的地方和以一个新任务启动相同意图的情况下,你才应该使用新的任务标记。否则,如果用户在你已经启动的任务里按桌面( HOME )键,而不是返回( BACK )键,你的任务及其活动将被放置到桌面后面,没有办法再切换回去。

 

 

任务共用性 Affinity

在某些情况下, Android 需要知道一个活动属于哪个任务即使它没有被启动到一个具体的任务里。这是通过任务共用性( Affinities )完成的。任务共用性( Affinities )为这个运行一个或多个活动的任务提供了一个独特的静态名称,默认的一个活动的任务共用性( Affinity )是实现了该活动的 .apk 包的名字。这提供了预期的标准特性,即所有在一个特定的 .apk 包里的活动是单个用户应用程序的一部分。

当开始一个没有 Intent.FLAG_ACTIVITY_NEW_TASK 标志的活动时,任务共用性 affinities 不会影响将会运行该新活动的任务 : 它总是运行在启动它的任务里。但是,如果使用了 NEW_TASK 标志,那么共用性( affinity )将被用来判断是否已经存在一个有相同共用性( affinity )的任务。如果是这样,这项任务将被切换到前面而新的活动会启动于这个任务的顶层。

 

这种特性在您必须使用 NEW_TASK 标志的情况下最有用,尤其是从状态栏通知或桌面快捷方式启动活动时。结果是,当用户用这种方式启动您的应用程序时,它的当前任务将被切换到前台,而且想要查看的活动被放在最上面。

你可以在程序清单( Manifest )文件的应用程序 application 标签中为 .apk 包中所有的活动分配你自己的任务共用性 Affinites ,或者在 活动 标记中为各个活动进行分配。一些说明其如何使用的例子如下:

  • 如果您的 .apk 包含多个用户可以启动的高层应用程序,那么您可能需要对用户看到的每个活动指定不同的 affinities 。一个不错的命名惯例是以附加一个以冒号分隔的字符串来扩展您的 .apk 包名。例如, “ com.android.contacts ”.apk 可以有 affinities:“com.android.contacts Dialer” “ com.android.contacts ContactsList”
  • 如果您正在替换一个通知,快捷方式,或其他可以从外部发起的应用程序的 内部 活动,你可能需要明确设定您替代活动的 taskAffinity 和您准备替代的应用程序一样。例如,如果您想替换 contacts 详细信息视图(用户可以创建并调用快捷方式),你得把 taskAffinity 设置成 “com.android.contacts”

启动模式和启动标志

  

您控制活动和任务交互的主要途径是通过活动的 launchMode   属性和意图相关的标志 flags 。这两个参数可以以各种方式合作来控制活动启动的结果,正如它们相关文档中描述的那样。在这里,我们将看看一些常见的用例和参数组合。

你将使用的最常见的启动模式(除了默认的 standard 模式)是 singleTop 。这并不影响任务 ; 它只是避免多次在一个堆栈顶部起动同一活动。

 

singleTask 启动模式对任务有重大的影响:它使活动始终是开始于一项新的任务(或其现有的任务被带到前台)   。使用这种模式需要谨慎对待你如何与系统其他部分进行交互,因为这影响到这个活动中的每一个路径。它应当仅在活动处于应用程序前台时使用(也就是支持 MAIN 动作和 LAUNCHER 类别)。

singleInstance 启动模式更是专业,并应仅用于整个就是被实现为一个活动的应用程序中。

有一种你会经常遇到的情况是当另一个实体(如 SearchManager   NotificationManager )开始您的一个活动。在这种情况下,必须使用 Intent.FLAG_ACTIVITY_NEW_TASK   标签,因为该项活动是在任务之外起动的(而且应用 / 任务可能根本不存在)。正如前面所述,这种情况下的标准行为是把匹配新活动 affinity 的任务带到前台和在此之上起动新的活动。不过,也有其他您可以实施的行为类型。

其中一种常见的做法是,还可以使用 Intent.FLAG_ACTIVITY_CLEAR_TOP   国旗与 NEW_TASK  。通过这样做,如果你的任务已经运行,那么将提请前景,所有的活动,其堆栈清除除根系活力和根系活力的 onNewIntent  (意图)   所谓的意图正在开始。请注意,该活动还常常使用 singleTop   singleTask   发射模式时,使用这种方法,因此,目前的情况是由于新的意图而不需要将它摧毁,一个新的实例开始。

 

一种通常的办法是和 NEW_TASK 联合起来使用 Intent.FLAG_ACTIVITY_CLEAR_TOP 标志。这样,如果您的任务已经运行,那么它将会被带到前台,除根活动外其它所有堆栈中的活动都被清除,而且这个根活动的方法 onNewIntent(Intent) 会在该意图起动时被调用。注意这个活动使用这个方法时经常使用 singleTop 或者 singleTask 起动模式,这样当前实例被赋予新的意图而不是需要销毁它然后重新起动一个新的实例。

 

您能采取的另外的方法是设置通知活动的任务 affinity 为空字符串 “” (表示没有 affinity ),并设置 finishOnBackground 属性。这种方法是有用的如果你希望这个通知把用户带到一个单独的描述它的活动中,而不是返回到应用程序的任务。通过指定这个属性,该活动将被结束不管用户通过 BACK 还是 HOME 离开它 ; 如果这个属性没有指定,按首页将导致这个活动及其任务仍保留在系统里,且可能没有办法返回它。

请务必阅读关于 launchMode 属性 Intent 标志 的文档以获取这些选项的详细说明。

 

 

进程

 

Android 里,进程完全是应用的实现细节,而不是用户通常了解的那样。其主要用途就是:

  • 通过安置不受信任的或不稳定的代码到另一个进程来提高稳定性或安全性。
  • 通过在同一进程里运行多个 .apks 的代码来减少开销。
  • 通过把重量级代码放在单独的进程中来帮助系统管理资源,该进程可以在不影响应用程序其他部分的情况下被终止。

正如前面所述,这个 进程 属性用来控制运行着特定应用程序组件的进程,注意,此属性不能用于违反系统安全性:如果有两个不共享相同用户 ID .apks 尝试运行在同一进程中,这将不会被允许,相反会为它们每一个创建不同的进程。

参见 安全   文档以获取更多关于安全限制方面的信息。

 

 

线程

每个进程包含一个或多个线程。多数情况下, Android 避免在进程里创建额外的线程,以保持应用程序单线程,除非它创建自己的线程。一个重要的结果就是所有对活动 Activity ,广播接收器 BroadcastReceiver 以及服务 Service 实例的调用都是由这个进程的主线程创建的。

注意新的线程并不会为每个活动,广播接收器,服务或者内容提供器( ContentProvider ) 实例而创建:这些应用程序的组件在进程里被实例化(除非另有说明,都在同一个进程处理),实际上是进程的主线程。这说明当被系统调用时没有哪个组件(包括 服务)会进行远程或者阻塞操作(就像网络调用或者计算循环),因为这将阻止进程中的所有其他组件。你可以使用标准的线程 Thread 或者 Android HandlerThread 便捷类去对其它线程执行远程操作。

这里有一些关于这个线程规则的重要的例外:

     IBinder 或者 IBinder 实现的接口的调用由调用线程或本地进程的线程池(如果该呼叫来自其他进程)分发,而不是它们的进程的主线程。 特殊情况下, 一个服务 IBinder 可以这样调用。(尽管调用服务里的方法已经在主线程里完成。)这意味着 IBinder 接口的实现必须要有一种线程安全的方法,这样任意线程才能同时访问它。
  
 对ContentProvider主要方法的调用 由调用线程或者主线程分发,如同 IBinder 一样 。被指定的方法在内容提供器的类里有记录。这意味着实现这些方法必须要有一种线程安全的模式,这样任意其它线程可以同时访问它。
  
  视图及其子类中的调用由正在运行着视图的线程产生。通常情况下,这会被作为进程的主线程,如果你创建一个线程并显示一个窗口,那么继承的窗口视图将从那个线程里启动。

 

原文链接: http://insford.com/android/docs/intro/appmodel.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值