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 子线程。
相关文章推荐
- HandlerThread面试知识小结
- 面试复习知识小结
- Android UI卡顿面试知识小结
- Git版本控制面试知识小结
- ListView面试知识小结
- Android Binder 面试知识小结
- BrodcastReceiver面试知识小结
- Android Proguard代码混淆面试知识小结
- android冷启动与热启动面试知识小结
- IntentService面试知识小结
- Android构建面试知识小结
- WebView面试知识小结
- AsyncTask面试知识小结
- oracle知识小结和javaweb面试常见的问题
- 面试知识小结
- android内存泄漏面试知识小结
- 2015腾讯应用开发面试基础知识小结
- Service面试知识小结
- [Java面试三]JavaWeb基础知识总结.
- C语言知识小结(四)