Handler 自我学习笔记
以下内容原文链接:https://blog.csdn.net/luoyingxing/article/details/86500542
Handler的通俗理解:
首先,我将Handler相关的原理机制形象的描述为以下情景:
- Handler:快递员(属于某个快递公司的职员)
- Message:包裹(可以放置很多东西的箱子)
- MessageQueue:快递分拣中心(分拣快递的传送带)
- Looper:快递公司(具有处理包裹去向的管理中心)
情景分析:在子线程中更新主线程的UI
其中的原理机制可以形象的理解为:
某天,你想给朋友寄一件礼物,首先你拿个箱子装好礼物并包裹好,下单叫了某家的快递员上门取件,快递员揽收你的包裹后,会将包裹送往快递分拣中心,等待配送车送出你的包裹。等配送车来了,就按照你的包裹地址信息,送到指定地方站点,然后分派给相应的快递员,将你的包裹送到你的朋友手里。
这整个邮寄包裹的过程可以形象的理解为Handler的工作机制原理,下面还原一下实际工作过程:
某时,你想刷新主界面的TextView,无奈你不在主线程,此时你就会包装好Message,然后声明一个Handler,让Handler将你的Message送往主线程(Looper),Handler将你的Message送到主线程后,还需要排队等待,等轮到你的时候,主线程就会告诉Handler,这个Message可以处理了,你负责分发一下,于是,Handler将该Message分发到相应的回调或者handleMessage( ) 方法中,于是,你就在该方法中更新了UI。
以下是对Handler源码进行了一个大概逻辑的分析:
MessageQueue的消息队列
//MessageQueue.java
Message mMessages;
MessageQueue中存在一个Message类型的变量mMessages;
//Message.java
Message next;
Message类里面又存在一个Message类型的变量next;
这说明每一个message都会有一个指向下一个节点的next指针,也就是说我们的MessageQueue所持有的mMessages只是一个链表的表头,它的后面有无数个message,这就是我们所说的MessageQueue的消息队列。
MessageQueue初始化
//handler.java
mQueue = mLooper.mQueue
由代码可以看出,mQueue并不是通过new的方式来初始化的,而是通过获取mLooper中的mQueue变量来进行初始化的。
mLooper
//ActivityThread.java
public static void main(String[] args){
...
Looper.prepareMainLooper(); //1
...
ActivityThread thread = new ActivityThread();
Looper.loop(); //2
...
}
//Looper.java
public static void prepareMainLooper(){
prepare(false); //3
synchronized(Looper.class){
if(sMainLooper != null){
throw new IllegalStateException("The main Looper has already");
}
sMainLooper = myLooper(); //4
}
}
//3处调动源码
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)); //5
}
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;
...
for(;;){ //6
Message msg = queue.next(); //might block //7
...
try{
msg.target.dispatchMessage(msg); //8
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
...
msg.recycleUnchecked(); //9
}
}
代码1会调用代码3和代码4,代码3的函数关键就在代码5的位置,代码5的位置new了一个Looper,所以Looper的初始化在prepare函数里面。代码5的意思是将初始化的Looper和sThreadLocal进行了绑定,也就是说有且只有一个Looper和主线程进行了绑定。
代码2调用了Looper.loop(),这个Looper.loop()函数里面就是维持了一个for的死循环,也就是代码6。代码6每次轮询都会去调用queue.next(),就是代码7,代码7的意思是去MessageQueue消息队列里面取出一个消息出来。取出消息后交由代码8处理,代码8处的消息调用了target.dispatchMessage(),dispatchMessage就是负责分发这个message的,这个target就是Message创建的时候它对应的Handler。这保证了从哪里发送的这个消息同时就会从哪一个同样的Handler里面去进行一个处理。
Looper就是Handler机制里的一个轮询机制,它会不断地去轮询MessageQueue里面是否有消息,如果有消息它就拿出来进行一个处理。Handler的sendMessage就是往消息队列里面添加消息。
代码9做的事情是消息处理完后进行回收再利用。如果不进行这个回收再利用操作,就会发生内存抖动。因为不回收利用它就会不断地去new message,在不断new的过程中它又会不断地释放message。这个时候就会带来内存抖动,内存抖动又会带来频繁的GC,这样就会造成我们系统的卡顿。
内存抖动是指内存频繁地分配和回收,而频繁的gc会导致卡顿,严重时和内存泄漏一样会导致OOM。
抄录别人的小demo:
public class TestActivity extends AppCompatActivity {
private Handler mHandler;
private static final String TAG = "TestActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
final TextView textView;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
textView = findViewById (R.id.textView);
final Handler handler = new Handler (){ //创建Handler
@Override
public void handleMessage(Message msg) {
super.handleMessage (msg);
Log.d (TAG, "handleMessage: "+msg.what);
if(msg.what == 1001){ //判断哪个线程来的消息,what是线程标识
textView.setText ("你好!"); //更新UI
}
}
};
findViewById (R.id.btn_1).setOnClickListener (
new View.OnClickListener () {
@Override
public void onClick(View v) {
new Thread(new Runnable() { //子线程
@Override
public void run() {
handler.sendEmptyMessage(1001); //传递消息,附带线程标识what
}
}).start(); //启动线程
}
});
}
}