三、线程/进程
进程
进程优先级
前台进程(Foreground process)。它表明用户正在与该进程进行交互操作,android系统依据下面的条件来将一个进程标记为前台进程:
-
该进程持有一个用户正在与其交互的Activity(也就是这个activity的生命周期方法走到了onResume()方法)。
-
该进程持有一个Service,并且这个Service与一个用户正在交互中的Activity进行绑定。
-
该进程持有一个前台运行模式的Service(也就是这个Service调用了startForegroud()方法)。
-
该进程持有一个正在执行生命周期方法(onCreate()、onStart()、onDestroy()等)的Service。
-
该进程持有一个正在执行onReceive()方法的BroadcastReceiver。
一般情况下,不会有太多的前台进程。杀死前台进程是操作系统最后无可奈何的做法。当内存严重不足的时候,前台进程一样会被杀死。
可见进程(Visible process)。它表明虽然该进程没有持有任何前台组件,但是它还是能够影响到用户看得到的界面。android系统依据下面的条件将一个进程标记为可见进程:
- 该进程持有一个非前台Activity,但这个Activity依然能被用户看到(也就是这个Activity调用了onPause()方法)。例如,当一个activity启动了一个对话框,这个activity就被对话框挡在后面。
- 该进程持有一个与可见(或者前台)Activity绑定的Service。
服务进程(Service process)。除了符合前台进程和可见进程条件的Service,其它的Service都会被归类为服务进程。
后台进程(Background process)。持有不可见Activity(调用了onStop()方法)的进程即为后台进程。通常情况下都会有很多后台进程,当内存不足的时候,在所有的后台进程里面,会按照LRU(最近使用)规则,优先回收最长时间没有使用过的进程。
空进程(Empty process)。不持有任何活动组件的进程。保持这种进程只有一个目的,就是为了缓存,以便下一次启动该进程中的组件时能够更快响应。当资源紧张的时候,系统会平衡进程缓存和底层的内核缓存情况进行回收。
LowMemoryKiller
OOM全称Out Of Memory,是Linux当中,内存保护机制的一种。该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核将该进程杀掉。
当Kernel遇到OOM的时候,可以有2种选择:
1) 产生kernelpanic(死机)
2) 启动OOM killer,选择一个或多个“合适”的进程,干掉那些选择中的进程,从而释放内存。
在Android中,及时用户退出当前应用程序后,应用程序还是会存在于系统当中,这是为了方便程序的再次启动。但是这样的话,随着打开的程序的数量的增加,系统的内存就会不足,从而需要杀掉一些进程来释放内存空间。至于是否需要杀进程以及杀什么进程,这个就是由Android的内部机制LowMemoryKiller机制来进行的。Andorid的Low Memory Killer是在标准的linux lernel的OOM基础上修改而来的一种内存管理机制。当系统内存不足时,杀死不必要的进程释放其内存。
线程
线程和线程池使用
在Java中,已经提供了2种实现线程的方式。
继承Thread类
class MyThread extends Thread{
@Override
public void run() {
//耗时操作
}
}
new MyThread().start();
实现Runnable接口
new Thread(new Runnable() {
@Override
public void run() {
//耗时操作
}
}).start();
两种方式虽然都可以实现,但是两者还是有一定的区别,因为Java的单继承性质,继承了Thread类则不能在继承其他的类,而实现Runnable接口则没有这种限制。另外在多线程去操作同一个资源的时候,也应该选择使用实现Runnable接口的方法来实现。
线程池
针对多线程并发的问题,引入线程池的好处就比较明显了:
- 可以控制同一时间内线程的最大并发数,合理利用系统资源,提高应用性能。
- 当任务完成后,可以重用已经创建的的线程,避免频繁创建新的线程而导致的GC。
- 通过Executors工具类,可以更方便的使用线程池,控制线程的最大并发数、线程的定时任务、单线程的顺序执行等。
Handler / Looper /Message
1.MessageQueue
MessageQueue顾名思义,指的就是消息队列,说这个之前我们首先需要知道什么是Message,比如说我们在UI界面时点击一个按钮,或者是接收到了一条广播,其实都算是一条Message,这些事件都被封装成一条Message被添加到了MessageQueue队列当中,因为我们知道一个线程在一段时间只能对一种操作进行相关的处理,因此这些消息的处理就要有先后顺序,因此采用MessageQueue来管理,也就是消息队列。消息队列其实就是一堆需要处理的Message而形成的传送带。一旦有消息发送进来,那么直接执行enqueueMessage()方法。也就是将消息压入到队列当中,一旦线程空闲下来,那么直接从MessageQueue中取出消息,使得消息出队。
2.Looper
Looper的主要作用是与当前线程形成一种绑定的关系,同时创建一个MessageQueue,这样保证一个线程只能持有一个Looper和MessageQueue同时Looper使得MessageQueue循环起来,就好比水车和水一样,MessageQueue好比水车,当他有了Looper的时候,那么水车会随着水去转动也就是说Looper为MessageQueue提供了活力,使其循环起来,循环的动力往往就少不了Thread。一般而言子线程一般是没有MessageQueue,因此为了使线程和消息队列能够关联那么就需要有Looper来完成了,因此我们可以这样去实现
3.Handler
handler起到了处理MQ上的消息的作用(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。
Loader
Android 3.0 中引入了 Loader 机制,让开发者能轻松在 Activity 和 Fragment 中异步加载数据,Loader 机制一般用于数据加载,特别是用于加载 ContentProvider 中的内容,比起 Handler + Thread 或者 AsyncTask 的实现方式,Loader 机制能让代码更加的简洁易懂,而且是 Android 3.0 之后最推荐的加载方式。
Loader 机制的 使用场景 有:
- 展现某个 Android 手机有多少应用程序
- 加载手机中的图片和视频资源
- 访问用户联系人