Android中使用 Handler场景
在开发中更新我们刷新视图都需要在主线程中更新,子线程是不支持更新视图操作的。所以当我们做一些耗时操作的时候可以不能马上得到反馈刷新UI,比如下载文件或者下载图片这些操作都比较耗时,我们一般会重新创建一个子线程异步处理耗时操作,这样就不会堵塞主线程导致卡顿的情况。异步处理成功后如果这个时候我们需要更新视图操作就不能直接更新了,这个时候Handler就起到了作用。我们可以用Handler Looper MessageQueue这套异步消息处理机制来处理这种情况,线程中发送消息通知,主线程来处理消息刷新视图。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new Runnable() {
@Override
public void run() {
//耗时的下载代码...
//发送消息
mHandler.sendMessage(Message.obtain());
}
}).start();
}
static Handler mHandler = new Handler() {
@Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
//处理消息刷新视图
}
};
Handler使用的生产消费者设计模式
生产消费者模式就是类似于生活中寄快递一样,寄东西的人可以视为生产者,而快递员就是消息缓存区、快递员负责全部快递的维护和派发、只要有还有快递快递员就是一直送到没有为止,收件人就是消费者负责接收快递员的快件。可以看到整个过程发件人不需要关心收件人的具体情况,只需要把收件人的地址写对就行。而快递员也不需要关心发件人和收件人的信息,快递员只负责收快递按快递地址送到到收件人手里。收件人只需要等待快递的送达就好了。
优点
低耦合
为什么不直接让消费者调用生产者的某个方法?如果这样直接调用必然会产生相互依赖的情况也就是耦合,如果以后生产者和消费者某一方变化都有可能会影响到对方,但是如果二者直接不直接依赖而是通过缓冲区来交互,这样耦合性就大大减低了。支持并发
生产者可以放心的产生数据直接扔给消息缓冲区,而不用等待消费者那边是否已经处理完了消息,而导致生产者等待状态(生产者消息堵塞),这样就可以不用依赖消费者的处理速度了,互相独立。自由发挥
这种模式还有一好处就是,如果生产者生产的数据速度过快,而消费者那边处理的比较慢,那么这个时候消息都会存在于缓冲区。这样生产者就能慢慢的消费这些数据了,所以定为自由发挥。
Handler Looper MessageQueue 的职责
MessageQueue 就是设计模式中的缓冲区,它负责接收生产者发送过来的数据先进先出的队列形式,保存着所有消息。在UI Thread中通过looper 不断从MessageQueue 取出消息在执行任务。
Looper 的主要工作就是维护MessageQueque中的消息队列,它负责从MessageQueue中取出要执行的消息任务,先判断Looper是否为null,不为null就循环状态不断从MessageQueue中取出消息,然后通过dispatchMessage派发出去就行处理。
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 (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
........
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
.........
msg.recycleUnchecked();
}
}
每个线程只能有一个Looper对象,而且是通过ThreadLocal来存放,其他线程无法访问当前的Looper。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
Looper可以让一个普通线程具有消息循环的能力,这是源码给出的一段示例。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
每个application都默认拥有一个Looper对象注释可以看到。Framework会通过JNI调用如下二个方法创建一个MainLooper,所以说UI线程默认就有一个Looper对象,可通过Looper.myLooper()方法获取到。
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
常用的方法 prepare()创建一个looper对象,在其他线程中想具有消息循环功能这个方法就必须调用。
public static void prepare() {
prepare(true);
}
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));
}
开始循环取出消息
public static void loop() {}
Handler 是负责把Message压入Queue中,还负责处理Message。Handler工作必须依赖Looper才行,没有Looper对象会抛出RuntimeException异常,不管是post sendMessage 还是延时的消息发送,最终都会到enqueueMessage方法中把消息传到缓冲区等待Looper处理。
public Handler(Callback callback, boolean async) {
.......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler导致的内存泄漏问题
下面这段代码是一个内部类,在Java中内部类或者匿名内部类都会隐私的持有外部类对象,而在Android中使用Handler的一般都是Activity,这就导致如果handler还在执行中而actiivty finsh掉,activity就不能被正常销毁回收。进而GC的时候就导致JVM不能回收Activity,有可能多次操作后就OOM了。
Handler mHandler = new Handler() {
@Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
//处理消息刷新视图
}
};
解决办法
1.把内部类换成static, static不会隐式持有外部对象。
static Handler mHandler = new Handler() {
@Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
//处理消息刷新视图
}
};
由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用
2.一般Handler导致的内存泄漏都是因为,消息处理是异步的。finsh的时候消息还在处理等待状态,这个时候可以在activity finsh的时候把handler移除掉,调用removeCallbacks方法移除。
3.关闭Activity的时候停掉你的后台线程。
总结
Handler 消息处理和发送的角色, 主要有二个作用 1.发送消息 2.处理消息
Looper 消息轮循器 looper方法里面是一个死循环,它不断从MessageQueue中取出消息,直到为null为止。
MessageQueue 消息队列 保存着全部消息的一个队列
在其他线程中使用Looper,可以看到一个Looper对象可以有多个Handler对象。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler1=new Handler(){
@Override
public void dispatchMessage(Message msg) {
Log.i(TAG, System.currentTimeMillis()+"=1");
}
};
Handler handler2=new Handler(){
@Override
public void dispatchMessage(Message msg) {
Log.i(TAG, System.currentTimeMillis()+"=2");
Looper.myLooper()
}
};
handler1.sendMessage(Message.obtain());
Looper.loop();
handler1.sendMessage(Message.obtain());
handler1.sendMessage(Message.obtain());
handler1.sendMessage(Message.obtain());
handler2.sendMessage(Message.obtain());
}
}).start();