进程、线程与任务程序之间的关系
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.i
ntent.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包名称,使⽤克隆的独⽴的
字符串。例如,"acts".apk⽂件可能有亲缘关系"acts:Dialer" 和
"acts:ContactsList"。
2、如果你正在替换通知、快捷⽅式、其他内嵌的在内部运⾏的活动,那么你可能需要显式设置你替换的活动的任务亲缘关系为与你替换的应⽤程序相同。例如,如果你正在替换通讯录详细视图(⽤户可以创建和调⽤快捷⽅式),那么你会想设置任务亲缘关系到"acts"。
运⾏模式和运⾏标志
控制活动和任务交互的主要⽅法是通过活动的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包名字的后⾯。例如,"acts"的亲和⼒命名可以是"acts:Dialer" and "acts:ContactsList"。
如果你想替换⼀个通知,快捷键,或者其它能从外部启动的应⽤程序的内部活动,你需要在你想替换的活动⾥明确的设置任务亲和⼒(taskAffinity)。例如,如果你想替换联系⼈详细信息浏览界⾯(⽤户可以直接操作或者通过快捷⽅式调⽤),你需要设置任务亲和⼒(taskAffinity)为“acts”。
启动模式以及启动标记
你控制活动和任务通信的最主要的⽅法是通过设置启动模式的属性以及意图相应的标记。这两个参数能以不同的组合来共同控制活动的启动结果,这在相应的⽂档⾥有描述。这⾥我们只描述⼀些通⽤的⽤法以及⼏种不同的组合⽅式。
你最通常使⽤的模式是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派发的内容提供器的主⽅法。被指定的⽅法在内容提供器的类⾥有记录。这意味着实现这些⽅法必须要有⼀种线程安全的模式,这样任意其它线程同时可以访问它。
呼叫视图以及由视图⾥正在运⾏的线程组成的⼦类。通常情况下,这会被作为进程的主线程,如果你创建⼀个线程并显⽰⼀个窗⼝,那么继承的窗⼝视图将从那个线程⾥启动。
___________________________________________________________________________________________________________________________________

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。