本人,语言功底有限,本编可能是白话文。
Handler可能是大家学习Android的拦路虎,下面我将分析Handler的整个调用过程及我的总结。
本文大纲:
1.了解ThreadLocal类。
2.Handler Looper MessageQueue三者的代码联系
3.关于回调的2种方法
4.HandlerThread的讲解
5.其他好用的方法。
一,看这个类名大概就应该知道什么,线程本地,改类主要提供了,线程本地(局部)存储,存储Looper对象,而真正存储Looper对象的是Thread.Values类
该对象其实就是一个表格存储这个对象,下面贴下源码。
Looper类
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
ThreadLocal类
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
@SuppressWarnings("unchecked")
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);
}
Values values(Thread current) {
return current.localValues;
}
Thread类
ThreadLocal.Values localValues;
2.Handler Looper MessageQueue三者的代码联系
在理解上面那张图片时,必须知道异步消息处理线程这个概念:线程启动后会进入一个无限循环之中,每次循环都会读取消息,并进行处理。
在Android中,异步消息处理线程可按照下面的格式来创建:
1、Looper.prepare() 创建一个消息队列
2.消息体
3.Looper.loop() 让该函数的线程进行无限循环。
下面我们来看源码怎么进入的:
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}//如果重复调用该方法会报错!!!!!所以说每个线程只能进行一次该方法
sThreadLocal.set(new Looper());
}
public static void loop() {
Looper me = myLooper();//该方法下面看
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
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 (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
// 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);
wallStart = SystemClock.currentTimeMicro();
threadStart = SystemClock.currentThreadTimeMicro();
}
msg.target.dispatchMessage(msg);
if (logging != null) {
long wallTime = SystemClock.currentTimeMicro() - wallStart;
long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
if (logging instanceof Profiler) {
((Profiler) logging).profile(msg, wallStart, wallTime,
threadStart, threadTime);
}
}
// 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();
}
}
}
public static Looper myLooper() {
return sThreadLocal.get();
}
现在我们看Handler 和上面的Looper类进行怎么样的消息交互和回调的。
在使用Handler时,大家一定很熟悉handler.sendEmptyMessage(msg);
但大家很少注意消息怎么发送到handler所属的线程和怎么进行回调的,下面老规矩贴源码:
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis)//最终消息处理会到这个函数
{
boolean sent = false;
MessageQueue queue = mQueue;//大家好奇这个mQueue怎么来的。看下面构造函数
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
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();//mLooper对象每个线程唯一,上面已经讲了(该方法见下面)
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//大家看到了吧,和looper.loop循环题一直
mCallback = null;
}
Threadlocal类中
public static Looper myLooper() {
return sThreadLocal.get();
}
总结上面代码:在非主线程中,如果Handler 不配合着和Looper.prepare(),Looper.loop()使用,在构造函数中将寻找不到mLooper和mQueue这2个变量,就不能像循环队列发出消息,进行回调,当然也有别的办法,列如下面的HandlerThread类。。。。
3.关于回调的2种方法
Handler的回调有2中方法:实现Callback接口和重写handleMessage();优先级当然是接口高点
下面贴代码
Handler myHandler=new Handler(this)
{
public void handleMessage(Message msg)
{
Log.v("two", "two");
}
};
@Override
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
Log.v("one", "one");
return false;
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
myHandler.sendEmptyMessage(1);
super.onWindowFocusChanged(hasFocus);
}
可以看到2个回调处理,当然当回调接口return true时,代表处理结束重写的方法将不调用。
4.关于HandlerThread本人认为是非常好的工具类。
看这个名字就知道继承了Thread类,个人认为与其他Thread类不同的地方有一个Looper对象
下面是代码的用法:
HandlerThread thread = new HandlerThread(servName);
thread.start();
Handler service = (Handler) clazz.getConstructor(Looper.class)
.newInstance(thread.getLooper());
services.put(servName, service);
service.sendEmptyMessage(SERVICE_START_MESSAGE_ID);
HandlerThread重要源码片段分析:
public void run() {
mTid = Process.myTid();
Looper.prepare();//
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();//2个非常重要的方法
mTid = -1;
}
可以看到已经在run方法创建了消息队列和队列循环工作了。
5。其他方法
handler.post(Runnable r)该方法是把r加到消息队列,但并未开辟新线程。等到消息被取出时才执行。
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;会把方法转成消息。
Looper.quit()停止这个消息队列。
Looper.myQueue().addIdleHandler(......)线程阻塞的时候调用,很管用。在主线程阻塞的情况下进行操作是很有必要的
如果觉得不错,请好评。亲。