Android进程和线程
进程(Process)是程序的一个运行实例,以区别于“程序”这一静态的概念;而线程(Thread)则是CPU调度的基本单位。
Android中的程序和进程具体是什么概念呢?
一个应用程序的主入口一般都是main函数,这基本上成了程序开发的一种规范——它是“一切事物的起源”。而main()函数的工作也是千篇一律的。总结如下:
初始化
比如Windows环境下通常要创建窗口、向系统申请资源等。
进入死循环
并在循环中处理各种事件,直到进程退出。
这种模型是“以事件为驱动”的软件系统的必然结果,因此几乎存在于任何操作系统和编程语言中。
以AndroidManifest.xml为例:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.
android.launchperf">
<application android:label="Launch Performance">
<activity android:name="SimpleActivity" android:label="Simple
Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
…
可以看到,Activity的外围有一个名为< application>的标签。换句话说,四大组件只是“application”的“零件”。
自动创建一个activity,然后在onCreate方法打个断点,
那么当这个Activity启动后,将会生成几个Thread呢(是不是只会有一个主线程)?如图所示。
一个由向导诞生的、没有任何实际功能的Activity程序,有三个线程,除了有我们最熟悉的main thread(即图中的Thread[<1> main])外,还有两个Binder Thread。主线程由ZygoteInit启动,经由一系列调用后最终才执行Activity本身的onCreate()函数。Zygote为Activity创建的主线程是ActivityThread:
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper(); /*只有主线程才能调用这个函数,普通线程应该使用prepare(),
*/
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler(); /*主线程对应的Handler*/
}
ActivityThread thread = new ActivityThread(); /*这个main()是static的,因此在这里需要创建一个实例*/
thread.attach(false); /*Activity是有界面显示的,这个函数将与WindowManagerService
建立联系。*/
…
Looper.loop(); /*主循环开始*/
throw new RuntimeException("Main thread loop
unexpectedly exited");/*如果程序运 行到这里,说明退出了上面的Looper循环*/
}
Service也是寄存于ActivityThread之中的,并且启动流程和Activity基本一致。
启动Service时,也同样需要两个Binder线程的支持。
对于同一个AndroidManifest.xml中定义的四大组件,除非有特别声明(,否则它们都运行于同一个进程中(并且均由主线程来处理事件)。
结论:
1.四大组件并不是程序(进程)的全部,而只是它的“零件”。
2.应用程序启动后,将创建ActivityThread主线程。
3.同一个包中的组件将运行在相同的进程空间中。
4.不同包中的组件可以通过一定的方式运行在一个进程空间中。
5.一个Activity应用启动后至少会有3个线程:即一个主线程和两个Binder线程。
Handler, MessageQueue, Runnable与Looper
概念初探
Runnable,Message,MessageQueue,Looper和Handler的关系简图
Runnable和Message可以被压入某个MessageQueue中,形成一个集合
注意,一般情况下某种类型的MessageQueue只允许保存相同类型的Object。图中我们只是为了叙述方便才将它们混放在同一个MessageQueue中,实际源码中需要先对Runnalbe进行相应转换。
Looper循环地去做某件事
比如在这个例子中,它不断地从MessageQueue中取出一个item,然后传给Handler进行处理,如此循环往复。假如队列为空,那么它会进入休眠。
Handler是真正“处理事情”的地方
它利用自身的处理机制,对传入的各种Object进行相应的处理并产生最终结果。
用一句话来概括它们,就是:
Looper不断获取MessageQueue中的一个Message,然后由Handler来处理。
它们各司其职,很像一台计算机中CPU的工作方式::中央处理器(Looper)不断地从内存
(MessageQueue)中读取指令(Message),执行指令(Handler),最终产生结果。
Thread和Handler的关系
① 每个Thread只对应一个Looper;
② 每个Looper只对应一个MessageQueue;
③ 每个MessageQueue中有N个Message;
④ 每个Message中最多指定一个Handler来处理事件。
由此可以推断出,Thread和Handler是一对多的关系。
Handler的作用:
1.处理Message,这是它作为“处理者”的本职所在。
2.将某个Message压入MessageQueue中。
实现第一个功能的相应函数声明如下:
public void dispatchMessage(Message msg);//对Message进行分发
public void handleMessage(Message msg);//对Message进行处理
Looper从MessageQueue中取出一个Message后,首先会调用Handler.dispatchMessage进行消息派发;后者则根据具体的策略来将Message分发给相应的责任人
Handler的第二个功能,相应的功能函数声明如下:
1. Post系列:
final boolean post(Runnable r);
final boolean postAtTime(Runnable r, long uptimeMillis);
…
2. Send系列:
final boolean sendEmptyMessage(int what);
final boolean sendMessageAtFrontOfQueue(Message msg);
boolean sendMessageAtTime(Message msg, long uptimeMillis);
final boolean sendMessageDelayed(Message msg, long
delayMillis);等等
Post和Send两个系列的共同点是它们都负责将某个消息压入MessageQueue中;区别在于后者处理的函数参数直接是Message,而Post则需要先把其他类型的“零散”信息转换成Message,再调用Send系列函数来执行下一步。
更贴近Android实现的“印象图”
Looper中包含了一个MessageQueue。下面是一个使用Looper的普通线程范例,名为LooperThread:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();/*一句简单的prepare,究竟做了些什么工作?
*/
mHandler = new Handler() {
public void handleMessage(Message msg) {
…/*处理消息的地方。继承Handler的子类通常需要修改这个函数
*/
}
};
Looper.loop();/*进入主循环*/
}
}
这段代码在Android线程中很有典型意义。概括起来只有3个步骤
(1)Looper的准备工作(prepare);
(2)创建处理消息的handler;
(3)Looper开始运作(loop)。
分析一下上边的代码:
首先是:
Looper.prepare();
既然要使用Looper类的函数,那么LooperThread中肯定就得执行如下操作:
import android.os.Looper;
仔细观察,Looper里有个非常重要的成员变量:
static final ThreadLocal<Looper> sThreadLocal = new
ThreadLocal<Looper>();
这是一个静态类型的变量,意味着一旦import了Looper后,sThreadLocal就已经存在并构建完毕。ThreadLocal对象是一种特殊的全局变量,因为它的“全局”性只限于自己所在的线程,而外界所有线程(即便是同一进程)一概无法访问到它。这从侧面告诉我们,每个线程的Looper都是独立的。
prepare:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {/*sThreadLocal.get返回的
是模板类,这个场景中是Looper。
这个判断保证一个Thread只会有
一个Looper实例存在*/
throw new RuntimeException("Only one Looper may be
created per thread");
}
sThreadLocal.set(new
Looper(quitAllowed));
}
sThreadLocal的确保存了一个新创建的Looper对象。
接下来创建一个Handler对象,我们单独将它提取出来以方便阅读。
public Handler mHandler;
…
mHandler = new Handler() {
public void handleMessage(Message msg) {…
}
};
可见,mHandler是LooperThread的成员变量,并通过new操作创建了一个Handler实例。
Handler有多个构造函数,比如:
public Handler();
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);
之所以有这么多构造函数,是因为Handler有如下内部变量需要初始化:
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
我们就以LooperThread例子中采用的第一个函数来讲解下它的构造函数:
public Handler() {
/*…省略部分代码*/
mLooper =Looper.myLooper();/*还是通过sThreadLocal.get来获取当
前线程中的Looper实例*/
…
mQueue= mLooper.mQueue; /*mQueue是Looper与
Handler之间沟通的桥梁*/
mCallback = null;
}
这样Handler和Looper,MessageQueue就联系起来了
UI主线程——ActivityThread
public static void main(String[] args) {
…
Looper.prepareMainLooper();//和前面的LooperThread不同
ActivityThread thread = new ActivityThread();//新建一个
ActivityThread对象
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();//主Handler
}
AsyncTask.init();
Looper.loop();
throw new RuntimeException("Main thread loop
unexpectedly exited");
}
prepareMainLooper和prepare
普通线程只要prepare就可以了,而主线程使用的是prepareMainLooper。
普通线程生成一个与Looper绑定的Handler对象就行,而主线程是从当前线程中获取的Handler(thread.getHandler)
(1)prepareMainLooper
public static void prepareMainLooper() {
prepare(false);//先调用prepare
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper
has already been prepared.");
}
sMainLooper = myLooper();
}
}
可以看到,prepareMainLooper也需要用到prepare。参数false表示该线程不允许退出,这和前面的LooperThread不一样。经过prepare后,myLooper就可以得到一个本地线程的Looper对象,然后将其赋给sMainLooper。从这个角度来讲,主线程的sMainLooper其实和其他线程的Looper对象并没有本质的区别
这个图描述的是一个进程和它内部两个线程的Looper情况,其中线程1是主线程,线程2是普通线程。方框表示它们能访问的范围,如线程1就不能直接访问到线程2中的Looper对象,但二者都可以接触到进程中的各元素。
线程1:因为是Main Thread,它使用的是prepareMainLooper(),这个函数将通过prepare()为线程1生成一个ThreadLocal的Looper对象,并让sMainLooper指向它。这样做的目的就是其他线程如果要获取主线程的Looper,只需调用getMainLooper()即可。
线程2:作为普通线程,它调用的是prepare();同时也生成了一个ThreadLocal的Looper对象,只不过这个对象只能在线程内通过myLooper()访问。当然,主线程内部也可以通过这个函数访问它的Looper对象。
(2)sMainThreadHandler。
当ActivityThread对象创建时,会在内部同时生成一个继承自Handler的H对象:
final H mH = new H();
ActivityThread.main中调用的thread.getHandler()返回的就是mH。
也就是说,ActivityThread提供了一个“事件管家”,以处理主线程中的各种消息。
Looper.loop
public static void loop() {
final Looper me = myLooper();/*loop函数也是静态的,所以它只能访问静态数据。函数myLooper 则调用sThreadLocal.get()来获取与之匹配的Looper实例(其实 就是取出之前prepare中创建的那个Looper对象)*/
…
final MessageQueue queue = me.mQueue;/*正如我们之前所说,Looper中自带一个MessageQueue*/
…
for (;;) {//消息循环开始
Message msg = queue.next();/*从MessageQueue中取出一个消息,可能会阻塞*/
if (msg == null) {/*如果当前消息队列中没有msg,说明线程要退出了。类比于上面Windows 伪代码例子中的while判断条件为0,这样就会结束循环*/
return; /*直接返回,结束程序*/
}
…
msg.target.dispatchMessage(msg); /*终于开始分派消息了,重心就在这里。变量target其实是一个Handler,所以dispatchMessage最终调用的是Handler中的处理函数。*/
…
msg.recycle();/*消息处理完毕,进行回收*/
}
}
loop()函数的主要工作就是不断地从消息队列中取出需要处理的事件,然后分发给相应的责任人。如果消息队列为空,它很可能会进入睡眠以让出CPU资源。而在具体事件的处理过程中,程序会post新的事件到队列中。另外,其他进程也可能投递新的事件到这个队列中。APK应用程序就是不停地执行“处理队列事件”的工作,直到它退出运行,如图所示。
在这个模型图中,ActivityThread这个主线程从消息队列中取出Message后,调用它对应的Runnable.run进行具体的事件处理。在处理的过程中,很可能还会涉及一系列其他类的调用(在图中用Object1,
Object2表示)。而且它们可能还会向主循环投递新的事件来安排后续操作。另外,其他进程也同样可以通过IPC机制向这一进程的主循环发送新事件,如触摸事件、按键事件等。这就是APK应用程序能“动起
来”的根本原因。
MessageQueue
/*以下代码还是Looper.java中的,不过只提取出MessageQueue相关的部分
*/
final MessageQueue mQueue; /*注意它不是static的*/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); /*new了一个MessageQueue,就是它了。也就是说,当Looper创建时,消息队列也同时会被创建出来*/
mRun = true;
mThread = Thread.currentThread();//Looper与当前线程建立对应关系
}
事实证明Looper内部的确管理了一个MessageQueue,它将作为线程的消息存储仓库,配合Handler、Looper一起完成一系列操作。
Thread类
Thread类的内部原理
public class Thread implements Runnable {
…
可以看到,Thread实现了Runnable,也就是说线程是“可执行的代码”:
public interface Runnable {
public void run();
}
Runnable是一个抽象接口类,唯一提供的方法就是run()。一般情况下,我们是这样使用Thread的:
方法1,继承自Thread
定义一个MyThread继承自Thread,重写它的run方法,然后调用:
MyThread thr = new MyThread(…);
thr.start();
方法2,直接实现Runnable
Thread的关键就是Runnable,因而下面是另一个常见用法:
new Thread(Runnable target).start();
这两种方法最终都通过start启动,它会间接调用上面的run实现。
checkNotStarted();
hasBeenStarted = true;
VMThread.create(this, stackSize);/*这里是真正创
建一个CPU线程的地方*/
}
在此之前,我们一直都运行在“老线程”中,直到VMThread.create——而实际上真正在新线程中运行的只有Run方法,这解释了上面第二种方法通过传入一个Runnable也可以奏效的原因。从这个角度来理解,Thread类只能算是一个中介,任务就是启动一个线程来运行用户指定的Runnable,而不管这个Runnable是否属于自身,如图5-13所示。
线程有如下几种状态:
public enum State {
NEW, //线程已经创建,但还没有start
RUNNABLE, //处于可运行状态,一切就绪
BLOCKED, //处于阻塞状态,比如等待某个锁的释放
WAITING, //处于等待状态
TIMED_WAITING, //等待特定的时间
TERMINATED //终止运行
}
Thread休眠和唤醒
1.wait和notify/notifyAll
这3个函数是由Object类定义的——也就意味着它们是任何类的共有“属性”
当某个线程(比如SystemServer所在线程)调用一个Object(比如BlockingRunnable)的wait方法时,系统就要在这个Object中记录这个请求。因为调用者很可能不止一个,所以可使用列表(见图5-15的
waiting list)的形式来逐一添加它们。当后期唤醒条件(也就是BlockingRunnable执行了run后)满足时,Object既可以使用notify来唤醒列表中的一个等待线程,也可以通过notifyAll来唤醒列表中的所
有线程,如图5-15所示
2.interrupt
调用一个线程的interrupt的目的和这个单词的字面意思一样,就是“中断”它的执行过程。此时有以下3种可能性。
1.如果Thread正被blocked在某个object的wait上,或者join(),sleep()方法中,那么会被唤醒,中断状态会被清除并接收到InterruptedException。
2.如果Thread被blocked在InterruptibleChannel的I/O操作中,那么中断状态会被置位,并接收到ClosedByInterruptException,此时channel会被关闭。
3.如果Thread被blocked在Selector中,那么中断状态会被置位并且马上返回,不会收到任何exception。
3.join
join方法有如下几个原型:
public final void join ();
public final void join (long millis, int nanos);
public final void join (long millis);
比如:
Thread t1 = new Thread(new ThreadA());
Thread t2 = new Thread(new ThreadB());
t1.start();
t1.join();
t2.start();
它希望达到的目的就是只有当t1线程执行完成时,我们才接着执行后面的t2.start()。这样就保证了两个线程的顺序执行。而带有时间参数的join()则多了一个限制,即假如在规定时间内t1没有执行完成,那么我们也会继续执行后面的语句,以防止“无限等待”拖垮整个程序。
4.sleep
wait是等待某个object,而sleep则是等待时间,一旦设置的时间到了就会被唤醒。
Thread状态迁移
Thread+Handler+Looper的组合实例
BusinessThread
private Thread mBusinessThread = null;
private boolean mBusinessThreadStarted = false;
private BusinessThreadHandler mBusinessThreadHandler = null;
private void startBusinessThread()
{
if (true == mBusinessThreadStarted)
return;
else
mBusinessThreadStarted = true;
mBusinessThread = new Thread(new Runnable()
{
@Override
public void run()
{
Looper.prepare();
mBusinessThreadHandler = new
BusinessThreadHandler();
Looper.loop();
}
});
mBusinessThread.start();
}
BusinessThread重写了run方法,并使用Looper.prepare和Looper.loop来不断处理调整请求。这些请求是通过mBusinessThreadHandler发送到BusinessThread的消息队列中的。
public class BusinessThreadHandler extendsHandler
{
public boolean sendMessage(int what, int arg1, int arg2)//重写sendMessage
{
removeMessages(what); //清理消息队列中未处理的请求
return super.sendMessage(obtainMessage(what, arg1,arg2));//发送消息到队列
}
public void handleMessage(Message msg)
{
switch(msg.what)
{
case MSG_CODE:
//在这里执行耗时操作
break;
default:
break;
}
}
};
Android应用程序如何利用CPU的多核处理能力
开发人员如何主动去利用CPU的多核能力,从而有效提高自身应用程序的性能呢?
答案就是针对Java-Based的并行编程技术。
第一种方式就是Java线程,它在Android系统中同样适用。使用上也和Java没有太多区别,我们只要继承Thread类或者实现Runnable接口就可以了。不过采用这类方法有一点比较麻烦的地方,就是和主线程的通信需要通过Message Queue——因为只有主线程才能处理UI相关的事务,包括UI界面更新
另一种可选的并行编程方法是AsyncTask,它是Android开发的专门用于简化多线程实现的Helper类型的类。优点很明显,就是可以不需要通过繁琐的Looper、Handler等机制来与UI线程通信。
AsyncTask在设计时的目标是针对比较短时间的后台操作,换句话说,如果你需要在后台长时间执行某些事务的话,我们建议你还是使用java.util.concurrent包所提供的Executor、ThreadPoolExecutor和FutureTask等其它API接口。
第三种比较常用的“工作线程”实现模型是IntentService。
Android应用程序的典型启动流程
APK类型的应用程,它们通常由两种方式在系统中被启动
1.在Launcher中点击相应的应用程序图标启动
这种启动方式大多是由用户发起的。默认情况下APK应用程序在Launcher主界面上会有一个图标,通过点击它可以启动这个应用程序指定的一个Activity。
2.通过startActivity启动
这种启动方式通常存在于源码内部。比如在Activity1中通过startActivity来启动Activity2。
这两种启动方式的流程基本上是一致的,最终都会调用ActivityManagerService的startActivity来完成。
无论以什么方式发起一个Activity的启动流程,最终都会调用到AMS的startActivity函数。
如果一切顺利,AMS才会最终尝试启动指定的Activity。如果读者写过APK应用程序,应该清楚Activity的生命周期中除了onCreate,onResume外,还有onPause,onStop等。其中的onPause就是在此时被
调用的——因为Android系统规定,在新的Activity启动前,原先处于resumed状态的Activity会被pause。这种管理方式相比于Windows的多窗口系统简单很多,同时也完全可以满足移动设备的一般需求。将一
个Activity置为pause主要是通过此Activity所属进程的ApplicationThread.schedulePauseActivity方法来完成的。ApplicationThread是应用程序进程提供给AMS的一个Binder通道。
当收到pause请求后,此进程的ActivityThread主线程将会做进一步处理。除了我们熟悉的调用Activity.onPause()等方法外,它还需要通知WindowManagerService这一变化——因为用户界面也需要发生改变。做完这些以后,进程通知AMS它的pause请求已经执行完成,从而使得AMS可以接着完成之前的startActivity操作。
假如即将启动的Activity所属的进程并不存在,那么AMS还需要先把它启动起来。这一步由Process.start实现,它的第一个入参是“android.app.ActivityThread”,也就是应用程序的主线程,然后调用它的main函数。
ActivityThread启动并做好相应初始化工作后,需要调用attachApplication来通知AMS,后者才能继续执行未完成的startActivity流程。具体而言,AMS通过ApplicationThread.scheduleLaunchActivity请求应用程序来启动一个指定的Activity。之后的一系列工作就要依靠应用进程自己来完成,如Activity创建
Window,ViewRootImpl,遍历View Tree等。