Handler机制是面试中的常客了,明天和大家一起通过源码层面来解析一下。
@[toc]
前提知识点
Handler机制波及到几个类: MessageQueue, Looper, Message, ActivityThread。
- ActivityThread: 主线程,开启loop循环,治理application过程, 调度治理activity, 播送及其他操作。
- Message: handler机制中音讯的载体,蕴含相干形容和数据对象,蕴含属性what, obj, arg1,arg2等。
- MessageQueue: 音讯队列。
- Looper:循环去MessageQueue中音讯。
AppCompatActivity.java-> FragmentActivity.java->SupportActivity.java->Activity.java
在Activity中默认有一个ActivityThread这是一个main thread,其中final Looper mLooper = Looper.myLooper();实例化了一个looper对象。
在ActivityThread.java中的main办法中,有如下代码
public static void main(String[] args) {
...省略其余代码
Looper.prepareMainLooper();
...省略其余代码
Looper.loop();
}
Looper.java
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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构造函数中初始了音讯队列MessageQueue对象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
创立looper 对象之前,会判断 sThreaLocal 中是否曾经绑定过 Looper 对象,如果是则抛出异样,确保一个线程中Looper.prepare()只调用一次。
如果在MainActivity中调用,如下代码,会报错。
通过上述代码能够得悉,Activity中默认开始了loop()循环,用于获取handler发送的音讯。
最简略的利用
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private final String TAG = "MainActivity";
public final int MSG_DOWN_FAIL = 1;
public final int MSG_DOWN_SUCCESS = 2;
public final int MSG_DOWN_START = 3;
@BindView(R.id.btn_start_thread)
Button btnStart;
@BindView(R.id.tv_status)
TextView tvShow;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DOWN_START:
tvShow.setText("down start");
break;
case MSG_DOWN_SUCCESS:
tvShow.setText("down success");
break;
case MSG_DOWN_FAIL:
tvShow.setText("down fail");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
btnStart.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start_thread:
new MyThread().start();
break;
default:
break;
}
}
class MyThread extends Thread {
@Override
public void run() {
handler.sendEmptyMessage(MSG_DOWN_START);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = Message.obtain();
msg.what = MSG_DOWN_SUCCESS;
handler.sendMessage(msg);
}
}
}
sendMessage之后产生了什么
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
sendMessage调用了sendMessageDelayed->sendMessageAtTime->enqueueMessage, 最终调用了MessageQueue的enqueueMessage的办法, 并将本人设置为message的target
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
接下来就到了音讯队列MessageQueue中了,来看一下
- 如果message的target为null,则间接抛出异样
- 依照message的工夫when有序插入到MessageQueue中,所以MessageQueue是一个按工夫排序的有序队列。
怎么取MessageQueue中的音讯
其实这里就是Looper.loop()办法从音讯队列中一直循环取音讯了。
一直调用MessageQueue的next()办法取音讯,如果message不为null, 则调用handler的dispatchMessage散发音讯。
这个handleMessage办法就是在创立Handler中笼罩的办法。
至此 Handler 的发送音讯和音讯解决流程曾经介绍结束。
面试常见问题
1.Looper.loop() 为什么不会阻塞主线程
Android的Ui线程,开启了一个死循环,然而并没有阻塞主线程是为什么呢?
在MessageQueue的next办法中有这样一行代码
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
nativePollOnce 办法是一个 native 办法,当调用此 native 办法时,主线程会开释 CPU 资源进入休眠状态,直到下条音讯达到或者有事务产生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采纳的 epoll 机制。
2.Handler 的 sendMessageDelayed 或者 postDelayed 是如何实现的
messageQueue的enqueueMessage是依照音讯的when工夫有序退出到队列的,取的时候也是依照工夫进行取的
能够看到如果以后工夫小于msg设置的工夫,会计算一个timeout,在timeout到了之后,才会将UI线程唤醒,交由CPU执行。
总结
1.APP启动创立主线程的时候会通过ActivityThread执行Looper.prepare(),创立一个looper 对象,在公有的构造方法中又创立了 MessageQueue 作为此 Looper 对象的成员变量,Looper 对象通过 ThreadLocal 绑定 MainThread 中。
2.在创立Handler对象的时候,通过构造函数获取ThreadLocal 绑定的looper对象,并通过looper获取音讯队列MessageQueue作为成员变量
3.子线程发送音讯时,将msg的target设置为handler本身,之后调用成员MessageQueue的enqueueMessage将音讯依照msg.when工夫排序插入到音讯队列
4.主线程通过Looper.loop()开启不阻塞UI线程的死循环,通过绑定的looper对象获取MessageQueue,调用next()办法一直获取msg, 并通过(handler)msg.target.dispatchMessage发送至咱们创立的handler时笼罩的handleMessage()办法
发表回复