简单聊聊Handler机制

Handler可以说是android开发面试毕问的一道题了,所以我本人也会经常看一些相关的文章,写这片文章的用意既是给自己看的,同时也想分享给那些对Handler不是太了解朋友们(大神请绕道),好啦,那么进入正题吧。

想要了解handler机制那么我们先要知道它的作用,大家都知道在android中,UI线程是非线程安全的,也就是更新UI只能在UI线程中完成,其他工作线程无法直接操作UI线程。耗时操作要在工作线程中完成(不能阻塞主线程)。为了解决以上问题,Android设计了Handler机制,由Handler来负责与子线程进行通讯,从而让子线程与主线程之间建立起协作的桥梁。

相信看完上面的话大家对handler有了个大概的了解,我们再来看看Handler 、 Looper 、Message 这三者的关系


我们首先看一下looper的prepare()方法。

prepare()方法

public static final void prepare() {

if(sThreadLocal.get() !=null) {

thrownewRuntimeException("Only one Looper may be created per thread");

}

sThreadLocal.set(newLooper(true));

}

从代码中我们可以看出 将一个looper的对象放入到sThreadLocal当中,并且sThreadLocal如果不为空就抛出异常,那么说明prepare()被重复调用就会抛出异常,那么也就是一个线程中只有一个looper实例。

我们再来看一下looper的构造方法

private Looper(boolean quitAllowed) {

mQueue =new MessageQueue(quitAllowed);

mRun =true;

mThread = Thread.currentThread();

}

我们可以看到构造looper 被初始化的同时也会初始化一个MessageQueue(消息队列)

我们再来看一下loop()方法

public static void loop() {

finalLooper 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 longident = 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 longnewIdent = 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实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。

me.mQueue拿到该looper实例中的mQueue(消息队列),在for循环 这个方法Message msg = queue.next();里取出一条消息,如果没有消息则阻塞。

调用  msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理消息,target其实就是Handler实例

looper介绍完了,我们再来看看Handler


public  Handler() {

this(null,false);

}

public Handler(Callback callback,booleanasync) {

if(FIND_POTENTIAL_LEAKS) {

finalClass 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) {

thrownewRuntimeException(

"Can't create handler inside thread that has not called Looper.prepare()");

}

mQueue = mLooper.mQueue;

mCallback = callback;

mAsynchronous = async;

}

通过Looper.myLooper()获取了当前线程保存的Looper实例,然后在mQueue = mLooper.mQueue;又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。


了解完了looper和handler我们总结一下吧


首先Looper.prepare()保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次否则会抛异常,所以MessageQueue在一个线程中只会存在一个。Looper.loop()会让当前线程进入一个无限循环(如果没有消息则阻塞),不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。


注:

程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。而在子线程中需要先调用Looper.prepare()才能创建Handler对象。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值