今天我们来回顾复习下Handler,处于性能优化的考虑,Android的UI线程是线程不安全的。为了避免多个线程并发操作UI的引发UI显示错乱问题,Android指定只允许在UI线程修改更新UI组件。其他线程更新UI抛出android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.异常,当我们在子线程做完耗时操作时,需要更新UI时,我们就需要通过Handler来实现啦。
1、Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。
2、线程之间通信。
post(runnable)
sendMessage(message)
总结:
1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2、Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的消息队列中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联。
4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放。
解决:
第1种在UI线程创建Handler,子线程发送消息,UI线程收到消息进行处理:
由于在Activity的启动代码中在当前UI线程调用了Looper.prepare()和Looper.loop()方法,所以我们在Activity创建Handler就可以了,详情我们可以查看ActivityThread类的main 方法中,我们就会发现调用了Looper.prepareMainLooper()和Looper.loop()方法。
ActivityThread创建Looper的系统源码:
public final class ActivityThread {
public static void main(String[] args) {
// 省略部分代码
//prepareMainLooper()方法中调用了Looper.prepare(false); 创建出Looper对象
Looper.prepareMainLooper();
// 开始轮询
Looper.loop();
// 如下代码我们发现在UI线程我们不能手动调用停止Loop,不然会抛异常。
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
例子:在子线程发送消息在UI线程弹出Toast具体代码:
package com.example.ling.review.handler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.example.ling.review.R;
import java.lang.ref.WeakReference;
public class HandlerActivity extends AppCompatActivity {
private Handler mHandler = new MainHandler(this);
// Handler改为静态内部类,避免内存泄漏
public static class MainHandler extends Handler {
private WeakReference mWeakReference = null;
public MainHandler(HandlerActivity activity) {
// 将Activity 包装成弱引用,避免内存泄漏
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
// 接收处理消息
HandlerActivity activity = mWeakReference.get();
if (activity != null) {
Toast.makeText(activity, "我在UI线程上哦", Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
initView();
}
private void initView() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
// 第一种 post(runnable)用法
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "我在UI线程上哦", Toast.LENGTH_SHORT).show();
}
});
// 第二种 sendMessage(message)用法
Thread.sleep(2000);
Message message = Message.obtain();
mHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
// activity 销毁时 Handler 移除所有回调和信息
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}
}
第2种子线程创建Handler,在子线程发送消息,创建Handler的子线程中接收到消息进行处理:
1、子线程使用Handler需要自己创建Looper对象;
2、Looper创建完,在创建Handler对象;
3、开启Looper轮询,Handler就可以发送、处理消息啦;
例子:
public class HandlerActivity2 extends AppCompatActivity {
private ThreadHandler mHandler;
// Handler改为静态内部类,避免内存泄漏
public static class ThreadHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// 接收处理消息
Log.d("TAG", "接收消息啦----- 当前线程为:" + Thread.currentThread().getName());
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
newThread1();
newThread2();
}
/**
* 创建子线程1 创建Handler
*/
private void newThread1() {
new Thread(new Runnable() {
@Override
public void run() {
Log.d("TAG", "当前线程为:" + Thread.currentThread().getName());
// 1、创建Looper
Looper.prepare();
// 2、创建Handler
mHandler = new ThreadHandler();
// 3、Looper开始轮询
Looper.loop();
}
}).start();
}
/**
* 创建子线程2 用子线程1的handler 发送消息
*/
private void newThread2() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.d("TAG", "当前线程为:" + Thread.currentThread().getName());
Thread.sleep(2000);
Message message = Message.obtain();
mHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
// activity 销毁时 Handler 移除所有回调和信息
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}
}
运行打印结果如下:
我们发现在Thread-2子线程创建了Handler, 在Thread-3子线程发送消息,接收到消息在Thread-2 子线程。