//主线程
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == 1) {
//doing something
}
return false;
}
});
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
上面是在主线程中使用handler
,因为在Android
中系统已经在主线程中生成了Looper
,所以不需要自己来进行looper
的生成。如果上面的代码在子线程中执行,就会报
Can't create handler inside thread " + Thread.currentThread()
- " that has not called Looper.prepare()
如果想着子线程中处理handler
的操作,就要必须要自己生成Looper
了。
例2 、子线程中使用handler
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler=new Handler();
handler.post(new Runnable() {
@Override
public void run() {
}
});
Looper.loop();
}
});
上面在Thread
中使用handler
,先执行Looper.prepare
方法,来在当前线程中生成一个Looper
对象并保存在当前线程的ThreadLocal
中。 看下Looper.prepare()
中的源码:
//prepare
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
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到prepare
方法中会先从sThreadLocal
中取如果之前已经生成过Looper
就会报错,否则就会生成一个新的Looper
并且保存在线程的ThreadLocal
中,这样可以确保每一个线程中只能有一个唯一的Looper
。
另外:由于Looper
中拥有当前线程的引用,所以有时候可以用Looper
的这种特点来判断当前线程是不是主线程。
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
boolean isMainThread() {
return Objects.requireNonNull(Looper.myLooper()).getThread() ==
Looper.getMainLooper().getThread();
}
sendMessage vs post
先来看看sendMessage
的代码调用链:
-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RvbmdyaW1hb21hb3l1,size_16,color_FFFFFF,t_70)
enqueueMessage
源码如下:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage
的代码处理很简单,msg.target = this;
就是把当前的handler
对象给message.target
。然后再讲message
进入到队列中。
post代码调用链:
调用post
时候会先调用getPostMessage
生成一个Message
,后面和sendMessage
的流程一样。下面看下getPostMessage
方法的源码:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到getPostMessage
中会先生成一个Messgae
,并且把runnable
赋值给message
的callback.
消息都放到MessageQueue
中后,看下Looper
是如何处理的。
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);
}
Looper
中会遍历message
列表,当message
不为null
时调用msg.target.dispatchMessage(msg)
方法。看下message
结构:
也就是说msg.target.dispatchMessage
方法其实就是调用的Handler中的dispatchMessage
方法,下面看下dispatchMessage
方法的源码:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//
private static void handleCallback(Message message) {
message.callback.run();
}
因为调用post
方法时生成的message.callback=runnable
,所以dispatchMessage
方法中会直接调用 message.callback.run();
也就是说直接执行post
中的runnable
方法。 而sendMessage
中如果mCallback
不为null
就会调用mCallback.handleMessage(msg)
方法,否则会直接调用handleMessage
方法。
总结 post
方法和handleMessage
方法的不同在于,post
的runnable
会直接在callback
中调用run
方法执行,而sendMessage
方法要用户主动重写mCallback
或者handleMessage
方法来处理。
[]( )3、Looper会一直消耗系统资源吗?
首先给出结论,Looper
不会一直消耗系统资源,当Looper
的MessageQueue
中没有消息时,或者定时消息没到执行时间时,当前持有Looper
的线程就会进入阻塞状态。
下面看下looper
所在的线程是如何进入阻塞状态的。looper
阻塞肯定跟消息出队有关,因此看下消息出队的代码。
消息出队
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
if(hasNoMessage)
{
nextPollTimeoutMillis =-1;
}
}
}
上面的消息出队方法被简写了,主要看下面这段,没有消息的时候nextPollTimeoutMillis=-1
;
if(hasNoMessage)
{
nextPollTimeoutMillis =-1;
}
看for循环里面这个字段所其的作用:
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
最后
下面是辛苦给大家整理的学习路线