在多线程的开发中,Handler机制如同在主线程中运行一样,只是需要注意在非主线程中Handler机制的使用限制,本文将对这些内容作出解释。
如果在子线程中对上UI界面进行操作,将抛出异常。为此,Android中引入了Handler消息
传递机制,来实现在子创建的线程中更新UI界面,下面将对Handler消息传递机制进行介绍。
一.Looper简介
1.首先需要知道一个概念,那就是MessageQueue,在Android中,一个线程对应一个Looper对象
,而一个Looper对象又对应一个MessageQueue(消息队列)。MessageQueue用于存放Message,
在MessageQueue中,存放的消息以队列的模式执行。
2.Looper对象用来为一个线程开启一个消息循环,用来操作MessageQueue。默认情况下,Android
中新创建的线程是没有开启消息循环的,但是主线程除外,系统自动为主线程创建Looper对象,开启消息循 环。所以,在主线程中,应用下面的代码创建Handler对象时,不会出错。而如果在新创建的非主线程中,应用下面的代码创建Handler对象时,将产生异常信息。
如果想要在非主线程中,创建Handler对象,首先要使用Looper类的prepare()方法来初始化一个
Looper对象,然后创建这个Handler对象,再使用Looper对象的loop()方法,启动Looper,从消息队列里
获取和处理消息。
源代码分析 :
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static final void loop() {
Looper me = myLooper();
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();
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.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("Looper", "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();
}
}
}
public void quit() {
Message msg = Message.obtain();
// NOTE: By enqueueing directly into the message queue, the
// message is left with a null target. This is how we know it is
// a quit message.
mQueue.enqueueMessage(msg, 0);
}
再看下Handler的构造函数,在子线程中如果没有调用Looper.prepare()就new Handler()则会抛出异常。代码如下 :
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
3.Looper对象
提供了几个方法:
prepare()---- 用于初始化Looper
loop()---- 用于开启消息循环,当调用了loop()方法后,Looper线程就真正的开始工作了,它会从消息队列中
获取消息并处理消息
quit()---- 用于结束Looper消息循环
注意:
在loop()之后的代码不会被执行,这个函数内部是一个消息循环,除非调用quit()方法,loop()才会终止,
其后面的代码才能得以运行。
源代码如下:
onCreate()方法:
- public class Thread_Handler_Activity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_thread__handler_);
- LooperHandler thread=new LooperHandler();
- thread.start();
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.activity_thread__handler_, menu);
- return true;
- }
- }
自定义线程类:
- public class LooperHandler extends Thread{
- public Handler handler;
- @Override
- public void run() {
- // TODO Auto-generated method stub
- super.run();
- //初始化Looper对象
- Looper.prepare();
- //实例化一个Handler对象
- handler=new Handler(){
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- super.handleMessage(msg);
- Log.d("BruceZhang", "This is Test!!!");
- }
- };
- Message msg=handler.obtainMessage();
- msg.what=1;
- handler.sendMessage(msg);
- Looper.loop();
- }
- }
运行的结果是在日志中显示一条信息,如图所示运行结果:
但是,如果没有对Looper的声明,运行就会抛出如下的异常:
所以,在实际的应用中,应考虑Handler在哪一个线程的中的实现。