您的位置:首页 > 职场人生

Handler面试知识小结

2018-01-22 13:00 190 查看

前言

今天我们来回顾复习下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是什么?

1、Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。

2、线程之间通信。

2、Handler使用方法

post(runnable)

sendMessage(message)

3、Handler工作原理





Handler: 负责发送消息和处理消息。

Looper: 负责管理MessageQueue,从MessageQueue中取出消息,交给消息对应的Handler处理。一个线程只有一个Looper对象。

Message: Handler接收处理的消息对象。

MessageQueue: 消息队列,负责管理Message, 先进先出。程序创建Looper对象时,会在他的构造器里创建MessageQueue对象。

总结:

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三者关系

4、Handler引起的内存泄漏

原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放。

解决:

Handler内部持有外部Activity的弱引用。

Handler改为静态内部类。

Handler.removeCallback()。

5、代码实践

第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<HandlerActivity> 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 子线程。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: