为什么要是使用Handler?
我们知道Android建议不要在主线程中进行耗时操作,否则会导致程序无法响应即ANR。考虑一种情况,假如我们需要从服务端拉
取一些信息并将其显示在UI上,这个时候必须在子线程中进行拉取工作,拉取完毕后又不能在子线程中直接访问UI,如果没有
Handler,那么我们的确没有办法将访问UI的工作切换到主线程中去执行。因此,系统之所以提供Handler,主要原因就是为了解
决在子线程中无法访问UI的矛盾。
思考系统为什么不允许在子线程中访问UI呢?
这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。
那为什么系统不对UI控件的访问加上锁机制呢?
缺点有两个:首先加上锁机制会让UI访问的逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执
行。鉴于这两个缺点,最简单且高效的方法就是采用单线程模型来处理UI操作,对于开发者来说也不是很麻烦,只是需要通过
Handler切换一下UI访问的执行线程即可。
看下Handler简单用法
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_LONG).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sendMsg();
}
public void sendMsg() {
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);//模拟一个耗时操作
Message msg = new Message();
msg.what = 1;//区分发送的消息
msg.obj = "我是子线程发来的消息";
handler.sendMessage(msg);
}
}).start();
}
}
接下来介绍下Handler的原理,分别观察下主线程干了什么,子线程干了什么
Android操作系统帮我们实现了Looper.prepare()和Loop.loop()
想知道子线程和主线程是如何沟通的,如何交互的,那么需要知道这四部分的源码
先看下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));
}
这里创建了一个new Looper()对象,看下这里面做了哪些工作
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
这里创建一个MessageQueue即消息队列,MessageQueue主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作,插
入和读取对应的方法分别为enqueueMessage和next,其中enqueueMessage的作用是往消息队列中插入一条消息,而next的作用
是从消息队列中取出一条消息并将其从消息队列中移除。尽管MessageQueue叫消息队列,但是它的内部实现并不是用的队列,实际
上它是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。
所以Loop.prepare()做了2件事
- 创建了Looper对象,在构建函数中创建了MessageQueue
- 然后将Looper对象通过ThreadLocal跟当前的主线程绑定
可以去了解下ThreadLocal的用法,类似一个Map结构,key就是当前线程,value就相当于你设置一个值。
不了解的可以研究下ThreadLocal的工作原理
我在主线程的ThreadLocal里面放了一个Looper对象,只要下面的代码是在主线程里面执行,那么在任何地方都可以将这个Looper对象拿到.那就可以拿到MessageQue
了解下new Handler做了哪些工作?
public Handler(@Nullable 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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
拿到了主线程中的Looper对象,就是将上面Looper.prepare()创建的Looper对象给拿到了
new Handler做了2件事
- 通过Looper类中的ThreadLocal从主线程拿到了Looper对象
- 然后通过Looper对象获取到了MessageQueue的引用
handler.sendMessage()做了哪些工作?
public boolean sendMessageAtTime(@NonNull 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);
}
必须要有mQueue,我们再sendMessage的时候,当前必须要有消息池的,需要找到消息池,把message对象添加到消息池。
如何把message添加到消息池里面的的?
通过enqueueMessage(),把什么什么压入队列的意思
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target =this //这里的this是handler
Handler.sendMessage()做了哪些事?
- 首先找到MessageQueue对象
- 然后msg.target =this,将handler对象成为msg的一个成员变量
- 最后把msg添加MessageQueue
Looper最重要的一个方法是loop方法,只有调用了loop后,消息循环系统才会真正地起作用。
接下来看下Looper.loop()源码
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
Looper的loop方法的工作过程也比较好理解,loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了
null。
MessageQueue的next方法来获取新消息,而next是一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,这也导致loop方
法一直阻塞在那里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:
msg.target.dispatchMessage(msg);
Looper.loop()做了哪些事?
- 找到MessageQueue
- 然后开启死循环遍历MessageQueue池
- 当获取到msg的时候,通过msg.target找到发送这个msg的handler
- 然后调用handler的dispatchMessage(msg)
可以发现,Handler发送消息的过程仅仅是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给
Looper,Looper收到消息后就开始处理了,最终消息由Looper交由Handler处理,即Handler的dispatchMessage方法会被调用,
这时Handler就进入了处理消息的阶段。dispatchMessage的实现如下所示。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage()方法里面执行了handleMessgae(),
所以handleMessage是在Looper.loop()里面调用的。
总结:
handler负责发送消息,looper负责接收Handler发送的消息,并直接把消息回传给Handler自己,MessageQueue就是一个存储消息的容器