几个概念的介绍:
Looper:Handler和MessageQueue之间的桥梁。系统会为主线程(UI线程)创建一个Looper对象,就是说对于主线程可以直接使用Looper对象,实际上主线程的工作就是不断的从其Looper对象的消息队列中取出消息并执行。系统并没有为子线程创建Looper对象,如果子线程要使用Looper对象必须先创建,有两种创建方式:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
从prepare方法的定义中可以看出,prepare方法就是将Looper对象放到ThreadLocalpublic static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg); //重点
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
myLooper()方法返回与当前线程关联的Looper对象,里面有好多代码不太懂,但是最重要的一句话msg.target.dispatchMessage(msg);可以看出loop()方法会将消息队列中取出的消息交给消息中的Handler对象来处理,这也就是为什么说Looper是Handler和MessageQueue之间的桥梁。调用Message的recycle()方法会将处理完的Message对象释放到系统的线程池中。
Message:用于描述各种数据,换句话说,Message对象可以用来存放各种类型的数据这里的数据是要交由Handler中的handleMessage方法来处理的。Message有几个重要的成员变量:Handler、Bundle(键值对的形式携带数据)、Runnable,arg1、arg2用来携带int类型的数据,what用于标记是哪个Message。Object用来携带对象。关于Message对象的实例化系统提供了new Message()方法,但是考虑到效率和内存,推荐使用Handler.obtainMessage(),这个方法还有几个接收不同类型参数的重载方法,都是用来在创建Message对象的时候为Message对象中的成员变量赋值,具体内容可以看API,这里就不说了。
MessageQueue:顾名思义,消息队列,被封装到Looper中,具体怎么工作的不用管,只需要知道他是以队列的方式来存储消息的。
Handler:用于处理Runnable对象和Message对象。可以将Runnable对象和Message对象发布到消息队列中,还可以对他们进行相应的处理。使用默认构造器new Handler()创建的Handler对象与当前所在线程的Looper关联,当然也可以使用new Handler(Looper)来在在创建Handler对象的同时指定与之相关联的Looper对象。也就是说,在创建Handler对象的时候必须保证已经有一个Looper与当前线程相关联。前面已经说了,系统会为主线程关联一个Looper对象,这也就是说,在主线程中可以直接new一个Handler,但是对于子线程,系统并没有为其关联一个Looper,因此在子线程中创建Handler之前必须先为子线程关联一个Looper对象,否则会抛出异常。
看了前面几个关键的类之后,可能还是有点迷糊,现在用一个简单的图来描述一下Android的消息机制的工作流程:
工作流程:
1、在线程中关联Looper对象(主线程中这个工作由系统完成)和Handler对象,并覆盖其中的handleMessage(Message msg)方法(如果你仅仅只是要处理Runnable对象的话可以不覆盖这个方法,直接new就行了);
2、在线程中(可以与上面的线程是同一个也可以不同)调用Handler上面的发布消息的方法(各种post和send),如果是发布Message对象,当然要先创建(obtainMessage),消息都存在MessageQueue中(MessageQueue被封装在Looper中,不需要自己创建);
3、调用Looper上的loop()方法开始工作,循环从MessageQueue中取出消息并交个Handler的handleMessage方法处理。(对于主线程这个工作也是由系统完成)
说明:
1、一个线程至多有一个Looper与之关联(主线程必定有一个,子线程就看我们自己了),一个Looper必定只有一个MessageQueue;
2、一个Looper可以与多个Handler关联,但是一个Handler只能与一个Looper关联;
3、一个Handler可以处理多个消息,即使这些消息来自不同的对象;
下面看几个例子。
在主线程中对Message对象的处理
前面已经说过,系统会为主线程关联一个Looper对象,因此下面的代码中没有调用prepare方法。
package com.zju.test_handler6;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
Handler handler;
static int count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.v("adb", "msg.what = " + msg.what);
break;
case 2:
Log.v("adb", "msg.what = " + msg.what);
break;
default:
Log.v("adb", "click too much");
}
}
};
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 在创建Message对象的时候给what变量赋值,what用于标记Message对象
Message msg = handler.obtainMessage(count++);
msg.sendToTarget();
//handler.removeMessages(2);
}
}
连续点击三次Button的运行结果:
如果把最后一行的注释取消掉,则不会打印出第二行,这说明不仅可以通过Handler来发布消息,也可以通过Handler来从消息队列中删除消息。
在主线程中对Runnable对象的处理:
package com.zju.test_handler4;
import com.example.test_handler4.R;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
static Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
Button post = (Button) findViewById(R.id.post);
Button postAtTime = (Button) findViewById(R.id.postAtTime);
Button postDelayed = (Button) findViewById(R.id.postDelayed);
Button postAtFrontOfQueue = (Button) findViewById(R.id.postAtFrontOfQueue);
post.setOnClickListener(new Listeners());
postAtTime.setOnClickListener(new Listeners());
postDelayed.setOnClickListener(new Listeners());
postAtFrontOfQueue.setOnClickListener(new Listeners());
}
}
class Threads implements Runnable {
int num;
public Threads(int id) {
this.num = id;
}
@Override
public void run() {
switch (num) {
case 1:
Log.v("adb", "Thread num = " + num);
Log.v("adb", "Thread id = " + Thread.currentThread().getId());
Log.v("adb", "执行run之后系统时间为:" + System.currentTimeMillis());
Log.v("adb", "");
break;
case 2:
Log.v("adb", "Thread num = " + num);
Log.v("adb", "Thread id = " + Thread.currentThread().getId());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.v("adb", "执行run之后系统时间为:" + System.currentTimeMillis());
Log.v("adb", "");
break;
case 3:
Log.v("adb", "Thread num = " + num);
Log.v("adb", "Thread id = " + Thread.currentThread().getId());
Log.v("adb", "执行run之后系统时间为:" + System.currentTimeMillis());
Log.v("adb", "");
break;
}
}
}
class Listeners implements OnClickListener {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
int whitch = v.getId();
Log.v("adb", "执行run之前系统时间为:" + System.currentTimeMillis());
switch (whitch) {
case R.id.post:
// 把一个Runnable对象发布到MessageQueue中
Threads thread_1 = new Threads(1);
MainActivity.handler.post(thread_1);
//MainActivity.handler.removeCallbacks(thread_1);
break;
case R.id.postAtTime:
// 控制Runnable对象中的run方法运行指定的时间
MainActivity.handler.postAtTime(new Threads(2), 2000);
break;
case R.id.postDelayed:
//控制Runnable对象中的run方法延迟指定的时间后运行
MainActivity.handler.postDelayed(new Threads(3), 2000);
break;
/*
* 这个方法没有搞太明白,也基本用不到
case R.id.postAtFrontOfQueue:
MainActivity.handler.postAtFrontOfQueue(new Threads(4));
break;
*/
}
}
}
界面就是四个Button,依次点击前三个Button的运行结果:
由于这个例子没有涉及到对Message的处理,故没有覆盖Handler中的handleMessage方法。调用Handler中的方法发布Runnable对象时,有几种不同的方式,每一种发布方式的作用在注释中说了,运行结果也进行了验证。需要说明的是,使用Handler发布的Runnable对象仍旧属于主线程(结果中线程id都是一样的),尽管Runnable对象中的run方法执行了。这也说明:Handler只是将Runnable对象发布到主线程的消息队列中,在loop方法中,取出该对象并交由Handler执行Runnable中的run方法。也正因为如此,在使用主线程的Looper的时候,最好不要在Handler中处理耗时操作。跟处理Message对象时一样,如果把代码中第一个case 中的注释删掉,第一个Runnable对象的run方法就不会执行。
当然,最常见的还是上面的两个例子的结合:
package com.zju.test_handler5;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
static Handler handler = new Handlers();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 几个按钮控制Message携带不同类型的数据
Button int_button = (Button) findViewById(R.id.button1);
Button object_button = (Button) findViewById(R.id.button2);
Button bundle_button = (Button) findViewById(R.id.button3);
int_button.setOnClickListener(new Listeners());
object_button.setOnClickListener(new Listeners());
bundle_button.setOnClickListener(new Listeners());
}
}
class Listeners implements OnClickListener {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
int whitch = v.getId();
switch (whitch) {
case R.id.button1:
MainActivity.handler.post(new Threads(1));
break;
case R.id.button2:
MainActivity.handler.post(new Threads(2));
break;
case R.id.button3:
MainActivity.handler.post(new Threads(3));
break;
}
}
}
//定义自己的Handler对象
class Handlers extends Handler {
// 在这个方法里面处理接收到的消息
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
Log.v("adb", "msg_1.arg1 = " + msg.arg1);
Log.v("adb", "msg_1.arg2 = " + msg.arg2);
break;
case 2:
Log.v("adb", "msg_2.obj = " + msg.obj);
break;
case 3:
Log.v("adb", "msg_3.bundle = " + msg.getData().getString("name"));
break;
}
}
}
class Threads implements Runnable {
int num;
public Threads(int num) {
this.num = num;
}
// 这样做是为了给不同的Runnable对象定义不同的行为
@Override
public void run() {
switch (num) {
case 1:
Message msg_1 = MainActivity.handler.obtainMessage(1);
// 用于标记是哪个Message
msg_1.what = num;
// 携带int型数据
msg_1.arg1 = 12;
msg_1.arg2 = 13;
//msg_1.sendToTarget();
MainActivity.handler.sendMessage(msg_1);
break;
case 2:
Message msg_2 = MainActivity.handler.obtainMessage(1);
msg_2.what = num;
// 携带Object
msg_2.obj = new Class_to_send();
//msg_2.sendToTarget();
MainActivity.handler.sendMessage(msg_2);
break;
case 3:
Message msg_3 = MainActivity.handler.obtainMessage(1);
msg_3.what = num;
// 用Bundle携带数据(键值对形式,可以携带各种进本类型的数据和IBinder对象)
Bundle bundle = new Bundle();
bundle.putString("name", Thread.currentThread().getName());
msg_3.setData(bundle); // 把Bundle放到msg_3中
//msg_3.sendToTarget();
MainActivity.handler.sendMessage(msg_3);
break;
}
}
}
class Class_to_send {
}
在上面的代码中,我写了两钟发布消息的方法,两种方法的效果是一样的,依次点击三个按钮的运行结果:
这个例子很常见的一种用法就是用来避免直接在子线程中操作UI,我们经常说的,不能在子线程中更新UI,是因为系统没有为View提供同步锁,为了避免死锁,系统就禁止在子线程中直接更新UI。这个例子提供了一种思路:在Runnable对象的run方法更新UI信息,然后把更新后的UI信息用一个Message对象保存起来并发布到主线程的消息队列中,然后在主线程的Handler对象(与主线程的Looper对象关联的Handler对象,不一定是在主线程中定义的)的handleMessage方法中更新UI。从前面的例子中我们知道,Runnable对象的run方法还是在定义它的线程中执行的,并没有另开一个线程(因为自始至终没有调用线程的start方法),这样就是说,如果Runnable对象时定义在主线程中的,自然可以在run方法中更新UI信息,如果Runnable对象是定义在子线程中的,也可以通过Handler在run方法实现对UI信息的更新,因为Handler对消息的处理是异步的。
上面的几个例子都是在主线程中进行的操作,下面再看几个在子线程中进行操作的例子,这个时候就要为子线程关联自己的Looper了。
为子线程关联Looper:
package com.zju.test_handler7;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread sub_Thread = new Sub_Thread();
sub_Thread.start();
}
}
class Sub_Thread extends Thread {
@Override
public void run() {
// 为当前线程关联一个Looper,Looper中有一个MessageQueue,所以不用再创建了
Looper.prepare();//static方法可以直接通过类名调用
// 使用默认构造器,与当前线程的Looper关联,发布的消息在当前线程的消息队列中
Handler sub_thread_handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.v("adb", "sub_msg.what = " + msg.what);
Log.v("adb", "current thread name = "
+ Thread.currentThread().getName());
//结束工作,非static方法,通过Looper独享调用
Looper.myLooper().quit();
}
};
// 与主线程的Looper关联,发布的消息在主线程的消息队列中
Handler main_thread_handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Log.v("adb", "main_msg.what = " + msg.what);
Log.v("adb", "current thread name = "
+ Thread.currentThread().getName());
}
};
Message sub_msg = sub_thread_handler.obtainMessage(1);
sub_thread_handler.sendMessage(sub_msg);
Message main_msg = main_thread_handler.obtainMessage();
// 发送空消息就必须指定what,不然的话,如果发送了很多空消息就不知道谁是谁了
main_thread_handler.sendEmptyMessage(2);
//Looper开始工作了,loop方法会一直执行知道调用quit方法,因此这个方法最好放在线程的最后
Looper.loop();
}
}
运行结果:
从结果中可以验证上面的说法:Handler对象与哪个线程的Looper关联(使用默认构造器时与Handler所在的线程的Looper对象关联),handleMessage就会在哪个线程中处理消息,而跟Handler在哪个线程中无关。在上面的代码中消息是从子线程中发布出去的,但是第二个Handler对消息的处理是在主线程红完成的,这也就实现了从子线程到主线程的通信。前面说的在子线程中更新UI信息的原理跟这个例子是一样的。
一个Looper与多个Handler关联:
package com.zju.test_handler8;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建两个与主线程关联的Handler对象
Handler handler_1 = new Handler_1();
Handler handler_2 = new Handler_2();
Message msg_1 = handler_1.obtainMessage(1);
Message msg_2 = handler_2.obtainMessage(2);
msg_1.sendToTarget();
msg_2.sendToTarget();
}
}
class Handler_1 extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1)
Log.v("adb", "handler_1处理消息");
}
}
class Handler_2 extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == 2)
Log.v("adb", "handler_2处理消息");
}
}
在上面的代码中,我是将两个Handler都与主线程的Looper关联,当然你也可以讲多个Handler与其他线程的Looper关联,原理都是一样的。
一个Message被多个Handler处理:
package com.zju.test_handler9;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler_1.post(run);
handler_2.post(run);
}
Handler handler_1 = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.v("adb", "handler_1" + msg.toString());
}
};
Handler handler_2 = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.v("adb", "handler_2 " + msg.toString());
}
};
class handler_3 implements Handler.Callback{
@Override
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
return false;
}
}
Runnable run = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Message msg = handler_1.obtainMessage();
//为Message关联一个Handler对象
msg.setTarget(handler_2);
msg.sendToTarget();
}
};
}
前面说了,哪Handler发布的Message就由哪个Handler来处理,现在在补充一点:Handler也可以处理不是由自己发布的Message,调用Message.setTarget(Handler)方法就行了。
写了这么多,逻辑可能有点混乱,如果有什么错误或者补充,欢迎讨论。