咱们都知道,在操做系统中进程是OS分配资源的最小单位,而线程是执行任务的最小单位。一个进程能够拥有多个线程执行任务,这些线程能够共享该进程分配到的资源。当咱们的app启动运行后,在该app没有其余组件正在运行的前提下,Android系统会启动一个新Linux进程来运行app,这个进程只包含了一个线程在运行。在默认状况下,app的组件都运行在该进程中,最初就包含的这个线程也被称为主线程或者UI线程。若是咱们启动该app的时候,系统中已经有一个进程在运行该app的组件,那么该app也会在该进程中运行。固然,咱们也可让app中不一样的组件运行在不一样的进程中,也能够在任意进程中新开线程执行任务。html
前面提到过,在默认状况下同一app的全部组件都是运行在同一进程中的,并且大多数app并不须要去更改这个设定。但若是咱们真的须要指定进程来运行特定组件,那么能够在manifest文件中设置。咱们在manifest文件中定义了各个组件,例如activity,service,receiver,provider等等,咱们能够设置process属性来指定一个新线程来运行该组件。经过设置process属性,咱们能够设置每一个组件都运行在不一样的进程中,也能够指定几个组件运行在同一个进程中。咱们甚至能够设置不一样来自app的组件运行在同一个进程中(经过指定相同的process属性并共享相同的user ID)。数据库
以前的文章中提到,Android系统启动后会载入通用的framework的代码与资源以后,启动一个Zygote进程。为了启动一个新的程序进程,系统会fork Zygote进程生成一个新的进程,而后在新的进程中加载并运行应用程序的代码。这就使得大多数的RAM pages被用来分配给framework的代码,同时促使RAM资源可以在应用的全部进程之间进行共享。缓存
Android系统在内存不足的状况下会杀死一些进程来知足那些直接和用户交互的进程,在这些被杀死的进程中运行的组件也会被注销掉。当这些组件从新运行时, 才会启动该线程。那么,系统将会如何决定要杀死哪一个进程呢?Android系统须要根据进程与用户的相关重要性来判断的。例如,与那些正在显示activity的进程相比,系统更倾向于杀死那些再也不显示的activity所在的进程。安全
Android系统会尽量长时间的维持一个进程的运行,可是最终会回收旧进程的内存空间提供给新的进程或是更重要的进程使用。Android系统采用了基于组件运行的进程以及组件状态的“重要性层级”的策略,根据重要性逐层清除进程。服务器
重要性层级从高到低共分为5层:网络
一、前台进程foreground process 多线程
当前正在与用户交互的进程。若是一个进程P知足以下任意一个条件,则进程P被称为前台进程:app
一般状况下,某时刻系统只会有不多一部分前台进程存在。它们只会在内存很是低的状况下才会被杀死,在这种状况下,设备达到了“memory paging state”状态,只有杀死一些前台进程才能保证系统的快速响应。异步
二、可见进程Visable process async
没有任何前台组件在此进程中运行,可是仍然能够影响到用户所看到的屏幕。若是进程P知足如下任意一个条件,则进程P被称为可见进程:
可见进程相对而言比较重要,但在某些状况下为了保证前台进程的运行,系统仍是会杀死这些可见进程的。
三、服务进程service process
若是一个service经过startservice方法启动,而且不属于以上两种更高级别的状况,那么运行该service的进程被称为service process。尽管该service并不与任何能被用户看到的组件绑定,可是它们作的工做是用户关心的,例如音乐播放,文件下载等等。
四、后台进程background process
某个当前不可见的activity(回调了onStop方法)运行于该线程。此类线程没法直接影响到用户体验,系统可能随时杀死此类进程回收内存供以上三种进程使用。一般状况下,系统中有多个后台进程在运行,因此它们被存于一个LRU列表中,从而最不常被用户用到的进程会先被杀死。若是一个activity正常回调了它的生命周期的函数并存储了相应的状态数据,那么杀死该后台进程是不会影响到用户体验的。由于当用户试图返回到该activity时,系统会恢复该activity全部的状态。
五、空进程 empty process
该进程中没有运行任何组件,保持此类进程存在的惟一缘由就是等待任务。一旦有组件须要运行,则能够缩短进程启动时间。因此系统每每会杀死这些进程用来平衡进程缓存和底层内核缓存之间的系统资源。
Android系统中,一个进程的等级是能够动态提高的,由于其余的进程可能会依赖于该进程。某个进程为其余进程提供服务,那么该进程的等级必定不会低于它所服务的进程。例如,某content provider 运行于进程A中,它为处于进程B中的客户端B提供服务;或者若是处于进程A的service绑定于位于进程B中的组件,那么A进程的重要等级只会高于或等于进程B。
因为一个运行service的进程等级要高于那些运行处于后台activity的进程,若是咱们须要有长时间执行的操做,那么从一个activity中启动一个service来完成这些操做就比在activity中新开子线程来完成这些操做效果要好(特别是这些子线程的持续时间要比activity长的状况下)。例如,一个activity想要上传一张图片给服务器,那么应当开启一个service在后台来完成上传操做,即便用户离开了当前activity,这些操做也可以在后台完成。使用service能够保证这些操做至少具备service优先级,不管当前activity的状态是否改变。这也是为何broadcast receiver应当使用service而不是简单的把耗时操做放在子线程中的缘由。
当应用程序启动后,系统将会建立一个主线程来运行应用程序。主线程很是重要,它负责为适当的用户控件分发任务和事件,包括绘制任务等等。同时,主线程也负责UI组件和应用程序的交互,因此咱们也称主线程为UI线程。
系统并不会为每一个组件单独开启一个线程来运行,全部的组件都会在主线程中初始化并运行运行在同一个进程中,系统经过主线程来调用每一个组件。因此,系统回调方法(例如onKeyDown,生命周期回调方法等)一般运行于主线程。
例如,当用户点击屏幕上的按钮,UI线程会将点击事件分发给控件。控件就会设置自身的按下状态,并将重绘请求添加到事件请求队列。UI线程从事件队列中取出该重绘请求后,通知该控件重绘。
当用户和app交互频繁时,单线程的模式可能会致使响应速度慢,用户体验不尽人意。若是在主线程中进行网络或数据库请求等耗时操做,则会致使线程阻塞,主线程将没法调度分发事件和任务。当超过5s的阻塞会使系统弹出ANR窗口。另外,UI控件都不是线程安全的,因此系统规定只能在UI线程中修改控件。咱们须要遵循两个规则:
上面讨论了只有UI线程工做的状况。为了提升应用程序UI的响应速度,得到更好的用户体验,咱们须要把耗时操做放在子线程中来完成。可是咱们须要注意的是,不要在子线程中操做UI控件。咱们一般使用Android的Handler机制来解决线程间通讯的问题,详细请参看以前的文章Android线程间异步通讯机制源码分析。同时,Android也提供了async task来完成异步任务。
async task在子线程中执行耗时任务,而后将结果返回给UI线程,无需本身手动建立handler。关于async task的使用就不在这里介绍了,在使用async task的过程当中,咱们须要注意的是多线程问题。因为运行配置的问题(例如屏幕横竖方向改变),会致使子线程任务未通过咱们容许就从新启动执行。
多数状况下,咱们的方法有可能被多个线程所调用,因此咱们必须考虑到线程安全的问题。特别是对于那些能够被远程调用的方法更是如此,例如,绑定service的方法。当咱们试图调用在IBinder中实现的方法,若是调用者和IBinder处于同一个进程,那么方法将会在调用者所在线程中执行。若是调用者与IBinder并不处于同一个进程中,那么系统从所维护的线程池中取出一个线程来执行该方法,该线程池与IBinder运行在同一个进程中(并不是在UI线程中执行)。举个栗子,尽管service的进程的UI线程将会调用service的onBind方法,然而在onBind方法所返回的IBinder对象中实现的那些方法就会被线程池中线程执行。由于一个service能够由多个客户端访问,线程池中的多个线程能够在同一时刻调用同一方法。因此IBinder对象中实现的方法须要是线程安全的。
相似的,一个ContentProvider能够接收到来自不一样进程的数据请求,虽然CP和CR类中隐藏了进程间通讯管理的细节,可是CP中对应的查询,删除,修改,插入等请求方法将会被交给CP所在进程的线程池中线程来执行。这些方法可能在同一时刻被多个进程所调用,因此这些方法必须是线程安全的。
Android系统提供了远程调用RPC机制来完成进程通讯IPC,经过RPC机制,应用程序中的组件(例如activity)做为调用者在本地调用某个方法,该方法在远程(另一个进程中)执行,而后将结果返回给调用者。这就须要将所调用方法和它的数据解析为操做系统能够理解的程度,而后从本地进程和地址空间传递给远程的进程和地址空间后,再进行重组和执行。