7、 清理堆栈
当用户离开一个task一段时间后,系统就会清理掉task里出了rootActivity以外的Activity,如果用户又回来了,显示的是rootActivity,就像是用户离开又回来,是放弃以前的东西,开始新的东西。
上面说的是默认的情况,有一些Activity的属性可以用来控制和修改这些行为。
alwaysRetainTaskState
如果一个task里的root Activity的alwaysRetainTaskState属性设置为true,那么前面描述的默认情况就不会出现了,task即使过了一段时间也会一直保留所有的Activity。
clearTaskOnLaunch
如果一个task里的root Activity的clearTaskOnLaunch属性设置为true,和alwaysRetainTaskState相反,即使是一瞬间的离开,系统马上就会清理掉task里出rootActivity以外的所有Activity。
finishOnTaskLaunch
这个属性和clearTaskOnLaunch一样,但是他是对一个Activity起作用,不是整个task,他能引起所有的Activity离开,包括root Activity,当这个属性设置为true,只是当用户使用这个应用时Activity才在task里,一旦用户离开后重新回来,显示的不是当前的界面。
还有其他的方法来从task里强制移动Activity,如果一个Intent对象里包含FLAG_ACTIVITY_CLEAR_TOP标志,并且目标task里已经一个在自己task里可以处理Intent的Activity(就是处理这个Intent无需实例化另外一个Activity),那么在这个Activity之上的所有Activity将被清除,能处理这个Intent的Activity就移到栈顶来处理这个Intent,例如ABCD堆栈,含有FLAG_ACTIVITY_CLEAR_TOP标志的Intent来启动B,那么清除CD,B到达栈顶来响应Intent,此时是AB,如果B设置了standard属性,那么还是清楚CD,然后再创建一个实例来响应Intent,此时是ABB,因为standard属性的Activity总是创建一个新的实例来响应新的Intent。
8、 进程和线程(Processes and Threads)
当一个应用的第一个组件需要运行时,android系统就为这个组件启动一个只有一个线程的Linux进程,默认的,应用的所有组件都运行这个进程中的这个线程中。
但是,你可以安排组件运行在其他的进程中,并且为你的任意的进程增加若干线程。
1、 进程
组件运行的进程是在manifest文件里控制的,四大组件都一个process属性可以指定进程来运行,这些属性可以被设置为了每个组件都可以运行在他自己的进程中,或者几个组件共享一个进程,或者不共享,如果两个应用共享一个Linux user ID并且有相同的权限,那么就可以使这两个应用中的组件运行在相同的进程中,也有process属性,用来指定对所有组件的属性。
所有的组件都在指定的进程中的主线程中实例化,系统调用这些组件就是从主线程里发出的,其他的线程将不会对每个组件再实例化,所有作为调用的回应的这些方法,比如说View.onKeyDown()还是组件的生命周期函数等等都是运行在这个主线程中的,这就意味着当系统调用这个组件时,这个组件不能长时间的阻塞线程(比如说网络操作,循环计算),因为这样会阻塞这个进程中的其他组件,你可以将很耗时的任务分到其他的线程中。
当内存不足或者有其他更紧急的进程要求时,Android系统可能关闭一个进程,运行在这个进程中的应用组件因此被销毁,当用户又重新回来时,进程才被重新启动。
至于究竟要停止哪个进程,android系统是通过衡量哪个进程对用户来说更重要来实现的
2、 线程
你可以限制你的应用运行在一个进程中,但是有的时候你需要新开一个线程在后台运行,用户界面需要随时对用户的要求做出反应,所以一些很耗时的工作应该重新启动一个线程来做,以免阻塞主进程。
Android系统提供了一系列方便的类来管理线程(Looper,Handler,HandlerThread)
3、 远程调用(Remote procedure calls)
Android系统有一个轻量级的远程调用机制(RPC)-----一个方法在本地调用,但是在远程执行(在另外一个进程里),返回给调用端的所有结果都必须的系统能理解的,将数据从本地进程和地址空间传递到远程的进程和地址空间,并在远端重新装配,返回值的时候传输方向相反,android系统会去做这些传输的工作,让你能够集中精力来定义你的RPC
一个RPC接口只能包含方法,默认的,即使是没有值返回,所有的方法都是同步执行的,就是说本地方法一直会阻塞直到远端的方法执行完毕)。
简单的说,这个远程调用的机制是这样工作的:
首先你需要用IDL(interface definition language)声明你的RPC接口,然后android系统会使用aidl工具来形成一个java接口,并且这个java接口是本地进程和远端进程都可以获得的,这个java接口包含了两个内部类,请看下图:
这两个内部类有管理远程调用(你用IDL声明的接口)的所以代码,两个内部类都实现IBinder接口,一个是在本地(内部)使用,这个你可以不用自己写代码,另外一个叫做Stub,继承自Binder类的,包含所有完成进程间通信(IPC)的代码,他包含你在RPC接口中声明的所有方法,你应该继续继承Stub类来实现这些方法。
一般的,远端进程应该由一个service来管理(因为一个service能通知系统关于这个进程和他连接到的其他进程)。
9、 进程的生命周期(Processes and lifecycles)
Android系统总是尽最大的努力来维持一个应用的进程,但系统的内存不足时就可能需要关闭一些旧的进程了,但是决定关闭哪个进程呢,android系统把所以的进程放进一个重要性树里,最低重要性的进程将会被停止,系统有5种重要性等级,重要性从高到低如下:
(1)、前台进程。一个前台进程是当前执行用户请求的进程,如果有如下的一种情形的那么他就是前台进程:
a、这个进程里运行着一个正在和用户交互的Activity(这个Activity的onResume()方法被调用)。
b、这个进程里有绑定到当前正在和用户交互的Activity的一个service
c、这个进程里有一个service对象,这个service对象执行了至少一个他生命周期的函数(onCreate(), onStart(), or onDestroy()).
d、这个进程里有一个执行了onReceive()方法的broadcastreceiver对象
只有一定数量的前台进程在任何时间都存在,他们只有在最后的时刻被停止---系统的内存太少了而不能运行这些仅有的前台进程了),一般的,在那个时刻,手机会重新设置内存页的状态,所以停止一些前台的进程是为了保持对用户操作的快速响应。
(2) 可见进程。一个可见进程一个没有任何前台显示的组件,但是仍然可以影响到用户当前屏幕所看见的东西,如果有如下的一种情形那么他就是可见进程。
a、 这个进程里一个Activity,但是这个Activity当前不是在前台显示,但是仍然对用户是可见的(这个Activity的onPause()方法被调用),比如说一个Activity调用一个dialog,那么这个dialog是当前显示的组件,这个Activity不是在前台显示,但是对用户是可见的。
b、 这个进程里有一个绑定到一个可见Activity(如上所述的Activity)的service
一个可见进程是极端重要的,只有在为了显示所有前台进程时,即显示前台进程都不够时,才会停止可见进程。
(3)、服务进程。一个服务进程是一个通过startService()启动的但是没有在前两个分类中的进程,虽然服务进程不是用户直接能看见的,但是他也总是做一些用户很关心的事(如在后台播放mp3,从网络上下载东西),所以系统会一直保持服务进程运行,除非内存不足以运行服务进程,前台进程,可见进程。
(4)后台进程。一个后台进程是运行一个当前对用户是不可见的Activity(这个Activity的onStop()被调用),这些进程对用户体验没有什么直接的影响,当内存不足以运行前台进程,可见进程,服务进程时,可以随时停止后台进程,通常有很多的后台进程在运行,系统会把这些后台进程放进一个LRU中(最近使用队列),最近使用的就最后停止。
(5) 空进程。一个空进程就是进程里没有任何活动的应用组件,维持这种进程的唯一原因就是作为一种缓存,当一个组件需要启动时加快启动的速度,系统为了平衡进程缓存和核心缓存会停止这些空的进程。
Android系统会取一个进程里的所以组件的最高重要性来安排进程的重要性,比如说,一个进程里有一个service和一个可见的Activity,那么这个进程会被安排成一个可见进程,而不是服务进程。
另外,一个进程的重要性有可能会因为其他进程的依赖而升高,一个进程不能比他所服务的进程的重要性低,比如有进程A里的service绑定到了进程B的组件上,那么进程A的重要性至少和进程B的一样,或者更高。
因为一个服务进程的重要性比运行一个后台Activity的进程高,所以,当一个Activity做一些长时间运行的任务时,最好启动一个service来做,而不是放到一个线程里去做,特别是这个任务的时间可能比Activity运行的时间还长的时候,比如在后台播放音乐,或者上传一张图片到网上,使用一个service保证了这个任务至少是服务进程的重要性,broadcast receiver也是一样,长时间运行的任务也最好是放到一个service里,而不是放到一个线程里。
文章来源于领测软件测试网 https://www.ltesting.net/