0,写在前面
博主在学习Handler消息传递机制时,学习的方法是通过问题来驱动理解,都是关注度比较高的问题,以下是我在学习后的简单整理,欢迎吐槽!
1,Handler是什么?能干嘛?
Google的Android开发文档是这么写的:
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
简言之,就是android.os.Handler允许我们发送和处理与线程的MessageQueue相关的Message和Runnable对象。 每个Handler实例都与单个线程和该线程的消息队列相关联。
主要用于:1)消息创建;2)将消息插入到队列中;3)处理消费者线程上的消息;4)管理队列中的消息。
2,为什么需要Handler?
这张图片清晰的说明了谷歌工程师为什么要开发Handler这个类。可以看出,Google工程师开发它主要是为了解决在非UI线程中更新UI组件比较麻烦的问题。
3,Handler都有哪些特点?
handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:
1)安排消息或Runnable 在某个主线程中某个地方执行;
2)安排一个动作在不同的线程中执行。
Handler中分发消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上post类方法允许你排列一个Runnable对象到主线程队列中,
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新。
4,Handler执行的流程图
UI线程:就是主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会 创建一个与其关联的MessageQueue;
Handler:作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象
Message:Handler接收与处理的消息对象
MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;
Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理!
大白话来说,就是当子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理。
5,如何理解Handler消息处理机制?
消息处理机制本质:一个线程开启循环模式持续监听并依次处理其他线程给它发的消息。
简单来说,就是一个线程开启一个无限循环模式,不断遍历自己的消息列表,如果有消息就挨个拿出来做处理,如果列表没消息,自己就堵塞(相当于wait,让出cpu资源给其他线程),其他线程如果想让该线程做什么事,就往该线程的消息队列插入消息,该线程会不断从队列里拿出消息做处理。
6,Looper、Handler、MessageQueue,Message作用和存在的意义?
6.1,Looper
我们知道一个线程是一段可执行的代码,当可执行代码执行完成后,线程生命周期便会终止,线程就会退出,那么做为App的主线程,如果代码段执行完了会怎样?,那么就会出现App启动后执行一段代码后就自动退出了,这是很不合理的。所以为了防止代码段被执行完,只能在代码中插入一个死循环,那么代码就不会被执行完,然后自动退出,怎么在在代码中插入一个死循环呢?那么Looper出现了,在主线程中调用Looper.prepare()...Looper.loop()就会变当前线程变成Looper线程(可以先简单理解:无限循环不退出的线程),Looper.loop()方法里面有一段死循环的代码,所以主线程会进入while(true){...}的代码段跳不出来,但是主线程也不能什么都不做吧?其实所有做的事情都在while(true){...}里面做了,主线程会在死循环中不断等其他线程给它发消息(消息包括:Activity启动,生命周期,更新UI,控件事件等),一有消息就根据消息做相应的处理,Looper的另外一部分工作就是在循环代码中会不断从消息队列挨个拿出消息给主线程处理。
6.2,MessageQueue
MessageQueue 存在的原因很简单,就是同一线程在同一时间只能处理一个消息,同一线程代码执行是不具有并发性,所以需要队列来保存消息和安排每个消息的处理顺序。多个其他线程往UI线程发送消息,UI线程必须把这些消息保持到一个列表(它同一时间不能处理那么多任务),然后挨个拿出来处理,这种设计很简单,我们平时写代码其实也经常这么做。每一个Looper线程都会维护这样一个队列,而且仅此一个,这个队列的消息只能由该线程处理。
6.3,Message
想让主线程做什么事,总要告诉它吧,总要传递点数据给它吧,Message就是这个载体。
6.4,Handler
简单说Handler用于同一个进程的线程间通信。Looper让主线程无限循环地从自己的MessageQueue拿出消息处理,既然这样我们就知道处理消息肯定是在主线程中处理的,那么怎样在其他的线程往主线程的队列里放入消息呢?其实很简单,我们知道在同一进程中线程和线程之间资源是共享的,也就是对于任何变量在任何线程都是可以访问和修改的,只要考虑并发性做好同步就行了,那么只要拿到MessageQueue 的实例,就可以往主线程的MessageQueue放入消息,主线程在轮询的时候就会在主线程处理这个消息。那么怎么拿到主线程 MessageQueue的实例,是可以拿到的(在主线程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 为了统一添加消息和消息的回调处理,又专门构建了Handler类,你只要在主线程构建Handler类,那么这个Handler实例就获取主线程MessageQueue实例的引用(获取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的时候就通过这个引用往消息队列里插入新消息。Handler 的另外一个作用,就是能统一处理消息的回调。这样一个Handler发出消息又确保消息处理也是自己来做,这样的设计非常的赞。具体做法就是在队列里面的Message持有Handler的引用(哪个handler 把它放到队列里,message就持有了这个handler的引用),然后等到主线程轮询到这个message的时候,就来回调我们经常重写的Handler的handleMessage(Message msg)方法。
7,Handler的用法
7.1,消息的创建
Message obtainMessage(int what, int arg1, int arg2);
Message obtainMessage();
Message obtainMessage(int what, int arg1, int arg2, Object obj);
Message obtainMessage(int what);
Message obtainMessage(int what, Object obj);
7.2,将消息插入到队列中
7.2.1,添加任务到消息队列中
Message obtainMessage(int what, int arg1, int arg2)
Message obtainMessage()
Message obtainMessage(int what, int arg1, int arg2, Object obj)
Message obtainMessage(int what)
Message obtainMessage(int what, Object obj)
7.2.2,添加数据对象到消息队列中
boolean sendMessage(Message msg)
boolean sendMessageAtFrontOfQueue(Message msg)
boolean sendMessageAtTime(Message msg, long uptimeMillis)
boolean sendMessageDelayed(Message msg, long delayMillis)
7.2.3,添加数据对象到消息队列中
boolean sendEmptyMessage(int what)
boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
boolean sendEmptyMessageDelayed(int what, long delayMillis)
7.3,消息的两种处理形式
7.3.1,任务消息
handler.post(new Runnable() {
@Override
public void run() {
//TODO : Do some operation
}
});
7.3.2,数据消息
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
//TODO : Get the data from Message and perform opertation accordingly.
}
};
handler.sendMessage(message);
7.4,如何跟踪消息队列处理?
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, TAG));
Example of tracing a message:
handler.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Executing Runnable");
}
});
mHandler.sendEmptyMessage(111);
8,Handler使用的一个简单demo实现
Thread + Handler
布局代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="26dp"
android:orientation="vertical"
tools:context="com.example.luolu.handlerwiththreaddemo.ThreadHandlerAndroidExample">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://www.baidu.com/"
android:textStyle="bold"
android:textSize="20sp"/>
<Button
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="Start"/>
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:indeterminate="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="10"
android:progress="0"/>
<TextView
android:id="@+id/msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
ThreadHandlerAndroidExample.java:
package com.example.luolu.handlerwiththreaddemo;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
-
@author luolu
*/
public class ThreadHandlerAndroidExample extends AppCompatActivity {private Handler handler = new Handler();
private MyHandlerThread myHandlerThread;Button btnStart;
ProgressBar progressBar;
TextView textMsg;@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);progressBar = (ProgressBar)findViewById(R.id.progress); textMsg = (TextView)findViewById(R.id.msg); btnStart = (Button)findViewById(R.id.start); myHandlerThread = new MyHandlerThread("myHandlerThread"); final Runnable myRunnable = new Runnable() { @Override public void run() { for (int i = 0; i <= 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //is accessed from within inner class, needs to be declared final final int finalI = i; handler.post(new Runnable() { @Override public void run() { progressBar.setProgress(finalI); } }); } handler.post(new Runnable() { @Override public void run() { textMsg.setText("finished"); } }); } }; myHandlerThread.start(); myHandlerThread.prepareHandler(); btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myHandlerThread.postTask(myRunnable); } });
}
@Override
protected void onDestroy() {
myHandlerThread.quit();
super.onDestroy();
}public class MyHandlerThread extends HandlerThread {
private Handler handler; public MyHandlerThread(String name) { super(name); } public void postTask(Runnable task){ handler.post(task); } public void prepareHandler(){ handler = new Handler(getLooper()); }
}
}
此外,还需在清单文件中添加权限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
实现效果图: