进程、线程与任务程序之间的关系
1,Aplication 一个.apk包就可以称一个application,一般application会有很多Activity 或其他service组成。
2,task:完成用户的一个目的的所有activity 组成一个task.提到task就该提到task stack任务栈也有人叫活动栈。Android系统用一个栈来记录一个任务,既然一个任务是由许多activity组成的,那栈里存的就是所有的 activity。为什么需要记录呢?因为记录可以通过按back 键回到上一个activity.这也是为什么我们按back键可以回到上一个活动的原因。那么什么时会开启一个新的任务呢?A)Notification 启动Activity会开启一个task,因为通过notification开启activity之后不需要返回到notification,所以需要开启一个新的task,这就是为什么我们在nofication里面启动一个Activity需要设置Intent的Flag为 Intent.FLAG_ACTIVITY_NEW_TASK.
3,explicit intent 是明确指定启动哪个Activity,比如 Intent intent= new Intent(ActivityA.this, ActivityB.class).而implicit intent并不明确指定启动那个Activity,而是通过设置一些Intent Filter来让系统去帅选合适的activity来处理。为什么需要分这两种Intent呢?我觉得好处有两个:第一,Activity 的重用,当一个Activity在其manifest里设置了许多intent filter,当发生了implicit intent时,系统就会去匹配这些filter,是否符合目标。所以在设计Activity的时候就要考虑到是否重用问题,若需要重用就要设置 intent filter. 第二,Implicit intent可以让用户选择自己喜欢的Activity来处理。若在系统匹配filter时,发现多个符合目标的activtiy就会弹出对话框让用户来选择Activity.
4,启动一个Activity可以是同一个application也可以是不同的application来启动,这就是为什么每个 Activity需要自己独立的生命周期。这意味着task stack里有可能不同application的activity组成。也就是说一个task是可以有不同的application组成或者说一个 task由不同的application完成的。
Back 键与Home键
android的手机的back键默认行为是finish处于前台的Activity的即Activity的状态为Destroy状态,再次启动该Activity是从onCreate开始的。
而Home键默认是stop前台的Activity即状态为onStop而不是Destroy,若再次启动它,则是从OnResume开始的,即会保持上次Activityd的状态。
back键也有例外的,按back键不会关闭Activity的,比如播放音乐,按了back键之后仍可以继续播放音乐,这是Music这支ap已经重写了back键的事件处理。
为什么需要Home键和Back键呢?一个使得Activity 为Stop一个使得为Destroy呢?我想原因的原因在于是android也是一个多任务的操作系统,通过Home键切换不同的任务,而通过back关闭任务中的某一个活动。若仔细想想就觉得PC的多任务行为一样的。
应用程序模型:应用、任务、进程、线程
在多数操作系统中,在应用程序驻留的可执行映像文件、运行的进程和用户交互的图标和应用之间有强1对1的关系。在Android操作系统中,这些关联更多的是不固定,重要的是理解不同的部分怎么整合在一起。
因为Android应用成需的灵活的特性,当实现程序的各个部分的时候,有一些基本术语需要理解。
1、android包(简称.apk)是包容程序代码和资源的文件。这也是发布应用程序的文件,当用户在其设备上安装应用程序时,由用户下载这个文件。
2、一般一个任务是用户觉察出的作为一个能运行的应用程序的东西:通常一个任务在HOME屏幕有一个图标,通过这个图标可以被用户访问,作为顶层条目,任务可获得,能在其他任务前面放到前台。
3、进程是低级核心处理过程,用于运行应用程序代码。通常,在.apk中的所有代码只运行在一个进程中,每个.apk都有专用的进程。但是,进程标签能用于修改代码运行的地方,要么针对整个.apk,要么针对单个活动、接收器、服务、提供者、组件。
任务
这里关键点:当用户看到认为他们是一个应用程序时,实际上处理的东西就是一个任务。如果你仅仅创建一个带有大量活动的.apk,其中一个活动是顶级入口点(通过一个intent过滤器针对操作android.intent.action.MAIN和分类 android.intent.category.LAUNCHER),那么的确有一个任务针对你的.apk创建起来,从这里启动的任何活动也作为那个任务的一部分运行。
一个任务,来自用户的眼中看,是应用程序;从程序开发者眼中看,是一到多个活动,用户已经越过进入那个任务,但是还没有关闭,或者,一个活动堆栈。一个新任务是通过使用Intent.FLAG_ACTIVITY_NEW_TASK 标志启动一个活动Intent来创建的;这个Intent会用于作为任务的根Intent,定义是什么任务。任何没有使用这个标志启动的活动会运行在和正在运行的那个活动所在的相同的任务中(除非活动已经请求一个特殊的启动模式,稍后讨论)。任务能被排序:如果你使用FLAG_ACTIVITY_NEW_TASK 但是已经有一个任务运行于那个Intent,当前任务的活动堆栈会送到前台而不是启动一个新的任务。
FLAG_ACTIVITY_NEW_TASK 必须小心地单独使用:使用这个标志就说明,从用户的角度看,一个新的应用程序在这个点启动。如果这不是你喜欢的行为,你应该不创建一个新任务。另外,如果用户可能从HOME屏幕回退到原来的位置,你应该仅仅使用新任务标志,以运行和新任务同样的Intent。否则,如果用户从正在运行的任务中按下HOME键而不是BACK键,你的任务机器活动将会在home屏幕之后被排序,而没有办法返回。
任务亲和力
在一些情况下,Android需要知道活动属于哪一个任务,即使当活动没有运行在指定的任务中时。这是通过任务亲缘关系完成的,任务亲缘关系提供一个唯一的任务静态名称,一到多个活动打算运行在具有静态名称的任务中。一个活动默认的任务亲和力是实现活动的.apk包的名称。这就提供了通常的可期望的行为,其中,在指定.apk文件中所有活动对用户来说是单个应用程序的一个部分。
当启动一个新活动而不是Intent.FLAG_ACTIVITY_NEW_TASK 标志的时候,任务亲缘关系没有影响新活动运行的任务:总是运行在触发任务的活动的任务中。但是,如果NEW_TASK被使用,那么亲缘关系会用于决定一个任务是否和一个相同的亲缘关系已经存在。如果是已经存在,那么任务将会被提到前台,新活动运行在那个任务的顶层。
这种行为对于必须使用NEW_TASK标志的情况是非常有用的,特别是从状态栏通知或者home屏幕快捷方式运行活动时。结果是:当用户使用这种方式运行你的程序时,当前任务状态会拉到前台,用户现在想看到的活动被放到顶层。
你能在manifest的应用程序标签中指定你自己的任务亲缘关系,针对.apk的所有活动,或者针对单个活动的活动标签。怎么使用亲缘关系的例子如下:
1、如果你的.apk包含多个顶层的用户能运行的应用程序,那么你会可能想分派不同的亲和力到用户从你的.apk中找到的每个活动。使用不同的名称的好的惯例是添加你的.apk包名称,使用克隆的独立的字符串。例如,"com.android.contacts".apk文件可能有亲缘关系"com.android.contacts:Dialer" 和 "com.android.contacts:ContactsList"。
2、如果你正在替换通知、快捷方式、其他内嵌的在内部运行的活动,那么你可能需要显式设置你替换的活动的任务亲缘关系为与你替换的应用程序相同。例如,如果你正在替换通讯录详细视图(用户可以创建和调用快捷方式),那么你会想设置任务亲缘关系到"com.android.contacts"。
运行模式和运行标志
控制活动和任务交互的主要方法是通过活动的launchMode属性和与Intent关联的标志。这两个参数能一同工作在控制活动运行的结果的各种方法之下,正如在关联文档中描述的那样。在这里,我们将会看到一些常用的用例和这些参数的组合。
最常用的运行模式(除了默认的标准模式外)是singleTop。这不会对任务有影响,这种模式恰好避免不同时间启动同一个活动(在堆栈顶层)
singleTask 运行模式对任务有一个主要的影响:导致活动总是启动在新任务里(或者,已存在的任务被拉到前台)。使用这种模式要求关心怎么和系统的其他部分交互,因为它影响到活动的每个路径。这种模式应该仅被用于作为应用程序前门的活动(即,支持MAIN操作和LAUNCHER分类)
singleInstance运行模式更多地是专用的,应该仅仅用于完全作为一个活动来实现应用程序的情况下。
你经常会运行进入的一种情况是另外一个实体(比如:SearchManager、NotificationManager)启动你的其中一个活动。在这种情况下,Intent.FLAG_ACTIVITY_NEW_TASK 标志必须使用,因为活动在任务的外部启动(应用程序和任务甚至可以不存在)。正如前面描述的,这种情况下的标准行为是,把当前任务提到前台,当前任务匹配新活动的affinity,并在顶层启动新活动。但是,有您能实现的其他类型的行为。
一个普通的方法是,与NEW_TASK一起使用Intent.FLAG_ACTIVITY_CLEAR_TOP标志。通过这样做,如果你的任务已经在运行,那么将会被提到前台,堆栈中的所有活动将被清除,除了根活动,根活动的onNewIntent(Intent)在Intent被启动的时候被调用。注意,当使用这种方法的时候,活动经常使用singleTop或者singleTask运行模式,因此,当前实例给予新Intent而不是请求销毁和产生新实例。
你能采用的另外一个办法是,设置通知活动的任务affinity为空串(表示无affinity),并设置 finishOnBackground属性。这种办法是有用的,如果你将会希望通知把用户带到一个分离的活动,胜于返回到应用程序的任务。通过指定这个属性,不论用户是否使用BACK或者HOME离开活动,活动都会完成。如果属性没有被指定,按HOME将会导致进入活动,其任务保持在系统中,可能没有办法返回。
保证阅读launchMode属性和Intent标志相关文档,以了解这些选项。
进程
在Android操作系统中,进程完全是应用程序的具体实现,不是用户通常认为的那种东西。他们的主要用途是简单的:
1、改善稳定性或者安全性,通过把未信任或者不稳定的代码放入独立的进程的方法来解决
2、简化在同一进程中多个.apk文件的代码的运行
3、有助于系统管理资源,通过吧重量级代码放入独立的进程,可以被杀掉,而且和程序的其他部分无关。
正如前面描述的那样,进程属性用于控制细小程序组件运行的进程。注意,这些进程属性不能用于违反系统安全规则:如果没有共享同一个用户ID的两个.apk文件试图运行在同一个进程中,那么,这是不允许的,不同的进程将会分别针对每个.apk文件被创建。
参见安全相关的文件了解更多的关于安全规则的信息。
线程
每个进程有一到多个线程运行在其中。在多数情况下,Android避免在进程中创建额外的线程,保证应用程序是单线程的,除非应用程序自己又创建了线程。重要印证是,注意,新线程不会针对每个Activity/BroadcastReceiver/Servie/ContentProvider实例:这些程序组件在对应的进程中实例化(除非另作说明所有的组件都位于同一个进程),在那个进程的主线程中。这意味着,当被系统调用的时候,没有组件(包括服务)应该在进程中执行长的或者阻塞的组件(例如网络调用或者计算循环),因为这将会阻塞所有进程中的其他组件。你可以使用标准库线程类或者Android的 HandleThread类来在另外一个线程中执行长操作。
There are a few important exceptions to this threading rule:
线程规则有几处例外:
1、到IBinder的访问或者在IBinder上实现的接口从调用它们的线程被调度,或者如果来自其他进程则从本地进程中的线程池被调度,不是从他们的进程的主线程被调度。特别地,到服务的IBinder的访问将会以这种方式调用。(虽然到在服务自身的调用来自主线程。)这意味着,IBinder接口的实现必须总是在线程安全模式下编写,因为他们能在同一时间从许多随机线程中被调用。
2、到ContentProvider的调用从调用线程或者IBinder所在的主线程被调度。在ContentProvider类的相关文档中这个方法有描述。这意味着,这些方法的实现必须总是以线程安全方式编写,因此,他们能在同一时刻从许多随机线程被调用。
3、在View和它的子类上调用来自视图窗口运行的线程。正常情况下,这将会是进程的主线程,但是如果你创建一个线程,然后从线程中显示一个窗口,那么这个窗口的视图层次将会从那个线程被调用。
__________________________________________________________________________________________________________________________
在大多数操作系统里,存在独立的一个1对1的可执行文件(如Windows里的exe文件), 它可以产生进程,并能和界面图标、应用进行用户交互。但在Android里,这是不固定的,理解将这些分散的部分如何进行组合是非常重要的。
由于Android这种可灵活变通的,在实现一个应用不同部分时你需要理解一些基础技术:
一个android 包(简称.apk ) ,里面包含应用程序的代码以及资源。这是一个应用发布,用户能下载并安装他们设备上的文件。
一个 任务 ,通常用户能当它为一个“应用程序”来启动:通常在桌面上会有一个图标可以来启动任务,这是一个上层的应用,可以将你的任务切换到前台来。
一个 进程 是一个底层的代码运行级别的核心进程。通常.apk包里所有代码运行在一个进程里,一个进程对于一个.apk包;然而, 进程 标签常用来改变代码运行的位置,可以是 全部的.apk包 或者是独立的 活动, 接收器, 服务, 或者 提供器组件。
任务
记住关键的一点:当用户看到的“应用”,无论实际是如何处理的,它都是一个任务。如果你仅仅通过一些活动来创建一个.apk包,其中有一个肯定是上层入口(通过动作的intent-filter 以及分类android.intent.category.LAUNCHER),然后你的.apk包就创建了一个单独任务,无论你启动哪个活动都会是这个任务的一部分。
一个任务,从使用者的观点,他是一个应用程序;对开发者来讲,它是贯穿活动着任务的一个或者多个视图,或者一个活动栈。当设置Intent.FLAG_ACTIVITY_NEW_TASK标志启动一个活动意图时,任务就被创建了;这个意图被用作任务的根用途,定义区分哪个任务。如果活动启动时没有这个标记将被运行在同一个任务里(除非你的活动以特殊模式被启动,这个后面会讨论)。如果你使用FLAG_ACTIVITY_NEW_TASK标记并且这个意图的任务已经启动,任务将被切换到前台而不是重新加载。
FLAG_ACTIVITY_NEW_TASK必须小心使用:在用户看来,一个新的应用程序由此启动。如果这不是你期望的,你想要创建一个新的任务。另外,如果用户需要从桌面退出到他原来的地方然后使用同样的意图打开一个新的任务,你需要使用新的任务标记。否则,如果用户在你刚启动的任务里按桌面(HOME)键,而不是退出(BACK)键,你的任务以及任务的活动将被放在桌面程序的后面,没有办法再切换过去。
任务亲和力(Affinities)
一些情况下Android需要知道哪个任务的活动附属于一个特殊的任务,即使该任务还没有被启动。这通过任务亲和力来完成,它为任务中一个或多个可能要运行的活动提供一个独一无二的静态名字。默认为活动命名的任务亲和力的名字,就是实现该活动.apk包的名字。这提供一种通用的特性,对用户来说,所有在.apk包里的活动都是单一应用的一部分。
当不带Intent.FLAG_ACTIVITY_NEW_TASK 标记启动一个新的活动,任务亲和力对新启动的活动将没有影响作用:它将一直运行在它启动的那个任务里。然而,如果使用NEW_TASK标记,亲和力会检测已经存在的任务是否具有相同的亲和力。如果是,该任务会被切换到前台,新的活动会在任务的最上面被启动。
你可以在你的表现文件里的应用程序标签里为.apk包里所有的活动设置你自己的任务亲和力,当然也可以为单独的活动设置标签。这里有些例子演示如何使用:
如果你的.apk包里包含多个用户可启动的上层应用程序,那么你可能想要为每个活动分配不同的亲和力。这里有一个不错的协定,你可以将不同的名字字串加上冒号附加在.apk包名字的后面 。 例如,"com.android.contacts"的亲和力命名可以是"com.android.contacts:Dialer" and "com.android.contacts:ContactsList"。
如果你想替换一个通知,快捷键,或者其它能从外部启动的应用程序的内部活动,你需要在你想替换的活动里明确的设置任务亲和力(taskAffinity)。例如,如果你想替换联系人详细信息浏览界面(用户可以直接操作或者通过快捷方式调用),你需要设置任务亲和力(taskAffinity)为“com.android.contacts”。
启动模式以及启动标记
你控制活动和任务通信的最主要的方法是通过设置启动模式的属性以及意图相应的标记。这两个参数能以不同的组合来共同控制活动的启动结果,这在相应的文档里有描述。这里我们只描述一些通用的用法以及几种不同的组合方式。
你最通常使用的模式是singleTop(除了默认为standard模式)。这不会对任务产生什么影响;仅仅是防止在栈顶多次启动同一个活动。
singleTask模式对任务有一些影响:它能使得活动总是在新的任务里被打开(或者将已经打开的任务切换到前台来)。使用这个模式需要加倍小心该进程是如何和系统其他部分交互的,它可能影响所有的活动。这个模式最好被用于应用程序入口活动的标记中。(支持MAIN活动和LAUNCHER分类)。
singleInstance启动模式更加特殊,该模式只能当整个应用只有一个活动时使用。
有一种情况你会经常遇到,其它实体(如搜索管理器SearchManager 或者 通知管理器NotificationManager)会启动你的活动。这种情况下,你需要使用Intent.FLAG_ACTIVITY_NEW_TASK 标记,因为活动在任务(这个应用/任务还没有被启动)之外被启动。就像之前描述的一样, 这种情况下标准特性就是当前和任务和新的活动的亲和性匹配的任务将会切换到前台,然后在最顶端启动一个新的活动。当然,你也可以实现其它类型的特性。
一个常用的做法就是将Intent.FLAG_ACTIVITY_CLEAR_TOP 和NEW_TASK一起使用。这样做,如果你的任务已经处于运行中,任务将会被切换到前台来, 在栈里的所有的活动除了根活动,都将被清空,根活动的onNewIntent(Intent) 方法传入意图参数后被调用。当使用这种方法的时候singleTop 或者singleTask启动模式经常被使用,这样当前实例会被置入一个新的意图,而不是销毁原先的任务然后启动一个新的实例。
另外你可以使用的一个方法是设置活动的任务亲和力为空字串(表示没有亲和力),然后设置finishOnBackground属性。 如果你想让用户给你提供一个单独的活动描述的通知,倒不如返回到应用的任务里,这个比较管用。要指定这个属性,不管用户使用BACK还是HOME,活动都会结束;如果这个属性没有指定,按HOME键将会导致活动以及任务还留在系统里,并且没有办法返回到该任务里。
请确保阅读过文档启动模式属性(launchMode attribute) 以及 意图标记(Intent flags) ,关注这些选项的详细信息。
进程
在Android中,进程是应用程序的完整实现,而不是用户通常了解的那样。他们主要用途很简单:
提高稳定性和安全性,将不信任或者不稳定的代码移动到其他进程。
可将多个.apk包运行在同一个进程里减少系统开销。
帮助系统管理资源,将重要的代码放在一个单独的进程里,这样就可以单独销毁应用程序的其他部分。
像前面描述的一样,进程的属性被用来控制那些有特殊应用组件运行的进程。注意这个属性不能违反系统安全: 如果两个.apk包不能共享同一个用户ID,却试图运行在通一个进程里,这种情况是不被允许的,事实上系统将会创建两个不同的进程。
请查看安全相关文档以获取更多关于安全限制方面的信息。
线程
每个进程包含一个或多个线程。多数情况下,Android 避免在进程里创建多余的线程,除非它创建它自己的线程,我们应保持应用程序的单线程性。 一个重要的结论就是所有呼叫实例, 广播接收器, 以及 服务的实例都是由这个进程里运行的主线程创建的。
注意新的线程不是为活动,广播接收器,服务或者内容提供器实例创建:这些应用程序的组件在进程里被实例化(除非另有说明,都在同一个进程处理),实际上是进程的主线程。这说明当系统调用时这些组件(包括服务)不需要进程远距离或者封锁操作(就像网络呼叫或者计算循环),因为这将阻止进程中的所有其他组件。你可以使用标准的线程 类或者Android的HandlerThread 类去对其它线程执行远程操作。
这里有一些关于创建线程规则的例外:
呼叫IBinder或者IBinder实现的接口,如果该呼叫来自其他进程,你可以通过线程发送的IBinder或者本地进程中的线程池呼叫它们,从进程的主线程呼叫是不可以的。特殊情况下,,呼叫一个服务 的IBinder可以这样处理。(虽然在服务里呼叫方法在主线程里已经完成。)这意味着IBinder接口的实现必须要有一种线程安全的方法,这样任意线程才能同时访问它。
呼叫由正在被调用的线程或者主线程以及IBinder派发的内容提供器 的主方法。被指定的方法在内容提供器的类里有记录。这意味着实现这些方法必须要有一种线程安全的模式,这样任意其它线程同时可以访问它。
呼叫视图以及由视图里正在运行的线程组成的子类。通常情况下,这会被作为进程的主线程,如果你创建一个线程并显示一个窗口,那么继承的窗口视图将从那个线程里启动。
___________________________________________________________________________________________________________________________________