一. 概念
Handler是Android消息机制的顶层接口,通过它能够将一个任务切换到Handler所在的线程中去执行,常用于更新UI。
主要包含了Handler,MessageQueue,Looper和ThreadLocal。
为什么要有这种功能?
Android出于安全问题,规定只能在主线程访问UI,并且建议耗时的操作不在主线程中进行。考虑如下情况,我要从服务端拉取很多信息并显示在UI上,这时候就产生了矛盾,因此需要Handler进行线程切换。
1. ThreadLocal
线程内部的数据存储类,只能在指定线程中获取到存储的数据,其他线程无法获取。在Handler中通过ThreadLocal来获取当前线程的Looper
代码演示:
val mBooleanThreadLocal : ThreadLocal<Boolean> = ThreadLocal()
mBooleanThreadLocal.set(true)
Log.d("Thread",mBooleanThreadLocal.get().toString())
Thread{
mBooleanThreadLocal.set(false)
Log.d("Thread",mBooleanThreadLocal.get().toString())
}.start()
Thread{
Log.d("Thread",mBooleanThreadLocal.get().toString())
}.start()
//输出:说明三个线程各自的threadLocal都不一样
//true
//false
//null
2. MessageQueue
即消息队列,底层实现不是队列,而是一个单链表的数据结构
插入方法:enqueMessage
读取方法:next,读取一条消息,并将其移除;如果没有消息,next方法会一直阻塞,直到有消息来
3. Looper
消息循环,即不停地从消息队列中查看是否有新消息,如果有就立刻处理,否则就一直阻塞在那里。
Looper的构造方法,会创建一个MessageQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
常用方法:
Looper.loop() 开启消息循环
Looper.prepare() 为当前线程创建一个Looper
prepareMainLooper()为主线程创建Looper
getMainLooper在任何地方都可以获取得到主线程的Looper
quit 直接退出Looper
quitSafely 设定一个退出标记,只有消息队列的已有消息处理完毕后才安全退出
Thread{
Looper.prepare()//
val handler:Handler = Handler()
Looper.loop()
}.start()
4. Handler原理
分为消息发送和接收两部分
- handler将消息发送到Looper的消息队列中(messageQueue),
- messageQueue 将数据按照时间先后排好队,等待Looper.loop()按照先后顺序取出Message
- Looper.loop()取出消息之后,调用消息的Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中
- handler 在 handleMessage(msg)方法中处理我们自己的逻辑。
数量关系:
一个Thread只能有一个Looper,可以有多个Handler,而一个Looper中又维护了一个MessageQueue队列
二. 实例演示
1. 整体使用方法
Handler的使用方式有两种:
Handler.sendMessage()
一,创建Handler子类
1、自定义Handler子类,并重写handleMessage()方法
class mHandler extends Handler {
//重写handleMessage()方法
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//执行的UI操作
}
}
2、主线程中创建mHandler的实例
private mHandler mhandler = new mHandler();
3、在子线程中创建需要发送的消息对象
Message msg = Message.obtain();
msg.what = 1;
4、在子线程中通过Hander发送消息到消息队列
mhandler .sendMessage(msg);
5、启动子线程
二、使用匿名Handler子类
1、在主线程中通过匿名内部类创建Handler类对象
Handler mhandler = new Handler(){
//重写handleMessage()方法
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//执行的UI操作
}
};
2、在子线程中创建需要发送的消息对象
Message msg = Message.obtain();
msg.what = 1;
3、在子线程中通过Hander发送消息到消息队列
mhandler .sendMessage(msg);
4、启动子线程
Handler.post()
1、在主线程中创建Handler实例
private Handler mhandler = new Handler();
2、在子线程中使用Handler.post()
mhandler.post(new Runnable() {
@Override
public void run() {
//执行的UI操作
}
});
3、启动子线程
2. 实例演示
- 耗时子线程的使用
public class MainActivity extends AppCompatActivity {
private static int MSG_1 = 0;
private Handler handler = new Handler(Looper.myLooper()){
//运行在主线程
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if(msg.what == MSG_1){
textview.setText("Recv Msg:"+(String)msg.obj);
}
}
};
private Button btn_start;
private TextView textview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start = findViewById(R.id.btn_start);
textview = findViewById(R.id.textView);
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//模拟耗时任务,在子线程中完成
new Thread(new Runnable() {
@Override
public void run() {//子线程任务
String buf = "abc123";
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//handler send message
Message msg = new Message();
msg.what = MSG_1;
msg.obj = buf;
handler.sendMessage(msg);
}
}).start();
Toast.makeText(MainActivity.this,"main thread go on",Toast.LENGTH_SHORT);
}
});
}
}