在安卓中由于主线程中不能进行耗时操作,我们往往需要另外开启一个线程来进行耗时操作,操作完成之后,我们通常需要使用Handler来将结果展现在界面中,下面我来说明下handler的使用。
Handler的创建
public Handler mHandler = new Handler();
Handler的创建只要new 一个Handler的对象就可以了,那么新建一个对象,系统究竟做了什么呢?
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
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 = callback;
mAsynchronous = async;
}
其中最重要的就是
mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;mQueue是MessageQueue类的一个对象,是一个消息队列,主要的作用就是储存由一个线程发送到主线程的消息,而Looper则是用来对MessageQueue来进行管理。
如果在该线程中没有Looper类,则会报错
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
在Activity中,由系统自动给我们创建Looper。
Handler其实也可以创建在非主线程中,前提是在非主线程中,我们手动的创建Looper,即调用Looper.prepare()方法。
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));
}
那么 怎么确保在一个线程中只有一个Looper呢,这要通过sThreadLocal来实现
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
上面就是ThreadLocal的get和set方法,简单点说就是将Looper与创建Looper的线程采用一一对应的关系保存起来,以此来确保在一个线程中只有一个Looper。
就是说如果在一个线程中创建了多个Looper,则会报错,并提示Only one Looper may be created per thread,如果在该线程中没有创建过Looper,则新建一个Looper
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
由他的构造方法可以看到,创建Looper的同时,系统会创建MessageQueue对象,这个对象也就是上面提到的Handler创建时的那个MessageQueue对象,所以在一个线程中只有一个Looper,也即只有一个MessageQueue对象,但是需要注意的是可以有多个Handler对象,那怎么确保由正确的Handler对象处理由其发送的Message呢,我们接着看源码。
在子线程中我们一般都是调用Handler的sendMessage方法发送消息,其实Handler的发送消息的方法,包括sendMessage,sendEmptyMessage,sendEmptyMessageDelayed等经过处理,最终都会调用下面这个方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,在enqueueMessage方法中msg.target的值为this,也就是这个Handler对象本身,就是说只要有这个message,就可以通过target这个参数知道是有谁发送的,所以就解决了上面关于如果有多个Handler怎么正确处理消息的问题。
现在完成了消息的发送。
下面我们看下怎么处理消息。
Looper的创建一般包括两部分,一个是Looper.prepare(),我们上面已经说过了,还有一个就是Looper.loop(),这个loop就是取消息,函数中会有一个死循环,不停地从MessageQueue中获取消息,即获取我们发送的Message对象,再通过Message中的target也就是发送消息的Handler对象的handMessage方法实现回调,完成从一个线程发送消息到另一个线程。
需要注意的是在loop方法中他会有一个类似锁的机制,即当MessageQueue中没有消息的时候,程序会阻塞在消息队列中,由于Looper是创建在主线程中,也就是说主线程此时是阻塞的。
但是这并不会造成系统ANR,因为系统ANR的原因实际是因为系统通过binder发送给应用执行某种命令后,会启动一个延时的message,如果在指定的时间应用没有给出相应的回应,则会触发消息,出现ANR。
而在发送给应用时由于有了消息,则主线程不在阻塞,按时处理了任务并发送给系统确定remove掉那个延时的message,在应用的主线程继续阻塞等待下一个消息。