PART-A_写在前面
-
Handler是什么
Handler是Android提供的:
- 一套更新UI的机制.
- 一套消息处理的机制,我们可以通过Handler发送和处理消息.
-
为什么使用Handler
Android在设计时,就封装了一套消息的创建、传递、处理机制,如果不遵循这样的机制,就没有办法更新UI信息,就会抛出异常信息.
-
Google为什么只设计了Handler来解决更新UI的问题
解决多线程并发问题. 假设如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么就会出现更新界面错乱. 但是如果都进行加锁处理的话,又会导致性能下降. 因此,产生了Handler的更新机制,不用去关心多线程的问题,所有的更新UI的操作,都是在主线程的消息队列当中去轮询处理.
PART-B_Handler使用方法
-
首先提出一个BUG
new Thread(new Runnable() { @Override public void run() { SystemClock.sleep(1000); tv.setText("hello catface"); } }).start();
上代码片会报异常:只有主线程能进行更新UI的操作
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.解决办法如下
new Thread(new Runnable() { @Override public void run() { SystemClock.sleep(1000); // 通过handler的post()方法在子线程中更新UI handler.post(new Runnable() { @Override public void run() { tv.setText("hello catface"); } }); } }).start();
-
实现简单轮播图(本例使用ImageView,自行可用ViewPager)
-
图片轮播ImageRunnable实现
private int[] images = {R.drawable.1, R.drawable.2, R.drawable.3}; private int index; // 记录当前图片 private Handler handler = new Handler(); private ImageRunnable imageRunnable = new ImageRunnable(); class ImageRunnable implements Runnable { @Override public void run() { index++; index = index % 3; // 向ImageView控件设置轮播图片 iv.setImageResource(images[index]); handler.postDelayed(imageRunnable, 1000); } }
-
主线程中开启图片轮播
handler.postDelayed(imageRunnable, 1000);
-
-
handler.sendMessage()的使用:传递int和Object对象
private TextView tv; private Handler handler = new Handler() { // msg为发送来的携带数据的消息 @Override public void handleMessage(Message msg) { super.handleMessage(msg); tv.setText(msg.arg1 + "::" + msg.arg2 + "::" + msg.obj); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); tv = (TextView) findViewById(R.id.tv_handler); new Thread(new Runnable() { @Override public void run() { SystemClock.sleep(2000); // Message msg = new Message(); // 不推荐使用 // Message内部维护了Message池用于消息复用,避免消息对象过多浪费. 推荐使用Message.obtain() Message msg = handler.obtainMessage(); // 获取系统消息对象 msg.arg1 = 111; // int类型 msg.arg2 = 222; msg.obj = new Person("zhangsan", 99); // 向消息中添加对象 handler.sendMessage(msg); // 发送消息给Handler // msg.sendToTarget(); // 效果同上 } }).start(); } class Person { public String name; public int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name + "------" + age; } }
-
分析上述代码片中两处:
-
obtainMessage()源码分析:避免没必要的消息对象的创建
public final Message obtainMessage() { return Message.obtain(this); } . . . public static Message obtain(Handler h) { Message m = obtain(); m.target = h; // target消息发送对象为Handler本身 return m; } . . . public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } // 系统中没有Message对象时才会创建消息对象 return new Message(); }
-
sendToTarget()源码分析
public void sendToTarget() { // 调用Handler自己的sendMessage()方法,本质上一样 target.sendMessage(this); } . . . public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
-
-
Handler消息的移除:removeCallbacks()
handler.removeCallbacks(Runnable);
-
Handler消息拦截:Callback接口
private Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message message) { ShowTool.show(HandlerActivity.this, "1111..."); return false; // true为截获Handler发送来的消息 } }) { @Override public void handleMessage(Message msg) { ShowTool.show(HandlerActivity.this, "2222..."); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); new Thread(new Runnable() { @Override public void run() { handler.sendEmptyMessageDelayed(1, 2000); } }).start(); }
-
自定义与线程相关的Handler
private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); LogTool.d("HandlerThread", "main:" + Thread.currentThread()); } }; class MyThread extends Thread { public Handler handler; @Override public void run() { Looper.prepare(); // 子线程中必须必须必须创建Looper对象 handler = new Handler() { @Override public void handleMessage(Message msg) { LogTool.d("HandlerThread", "thred:" + Thread.currentThread()); } }; Looper.loop(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); MyThread thread = new MyThread(); thread.start(); SystemClock.sleep(800); thread.handler.sendEmptyMessage(1); // 子线程 handler.sendEmptyMessage(1); // 主线程 }
-
HandlerThread:解决多线程并发问题
-
先看存在BUG的代码片
private Handler handler = new Handler() { // 主线程的handleMessage方法中不要执行耗时操作,否则出现卡死状态 @Override public void handleMessage(Message msg) { super.handleMessage(msg); LogTool.d("HandlerThread", "UI..." + Thread.currentThread()); } }; class MyThread extends Thread { public Handler handler; public Looper looper; @Override public void run() { Looper.prepare(); handler = new Handler() { @Override public void handleMessage(Message msg) { LogTool.d("HandlerThread", "==..." + Thread.currentThread()); } }; Looper.loop(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); MyThread thread = new MyThread(); thread.start(); // 空指针:主线程执行到此方法时,MyThread类中Looper对象还没有创建成功 handler = new Handler(thread.looper) { @Override public void handleMessage(Message msg) { } }; handler.sendEmptyMessage(1); }
上代码片会报异常:MessageQueue空指针
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.catface/com.catface.demos.HandlerActivity}: java.lang.NullPointerException: Attempt to read from field ‘android.os.MessageQueue android.os.Looper.mQueue’ on a null object reference异常分析见代码片
handler = new Handler(thread.looper)
-
解决办法:通过HandlerThread
// 解决多线程并发问题 --> 将耗时操作交给子线程 thread = new HandlerThread("handler thread"); thread.start(); handler = new Handler(thread.getLooper()) { ...
-
-
主线程与子线程之间的信息交互
主线程和子线程互相定时发送消息给对方,通知对方运行相关逻辑.
private Handler threadHandler; // 子线程Handler private Handler handler = new Handler() { // 主线程Handler @Override public void handleMessage(Message msg) { Message message = new Message(); LogTool.d("threadchat", "main handler"); threadHandler.sendMessageDelayed(message, 1000); // 主线程给子线程发送消息 } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); // 解决Looper空指针异常 HandlerThread handlerThread = new HandlerThread("handler thread"); handlerThread.start(); // 通过HandlerThread来解决线程并发问题 threadHandler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { Message message = new Message(); LogTool.d("threadchat", "thread handler"); handler.sendMessageDelayed(message, 1000); // 子线程给主线程发送消息 } }; // 开始主子线程间的交互,互相定时发送消息给对方 findViewById(R.id.bt_start).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { handler.sendEmptyMessage(1); } }); findViewById(R.id.bt_end).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { handler.removeMessages(1); } }); }
- 重要结论
- 子线程向主线程发送消息,必须有主线程的Handler实例,该Hnadler绑定了主线程的Looper.
- 主线程向子线程发送消息,必须有子线程的Looper实例,为了防止Looper没有初始化,所以通过HandlerThread类,来保证子线程的Looper在被主线程调用时已经初始化.
-
更新UI的四种方式
-
post
new Thread(new Runnable() { @Override public void run() { // post中sendMessageDelayed(getPostMessage(r), 0) // get中调用通过Message.obtain()、callback(Runnable对象) handler.post(new Runnable() { @Override public void run() { ... } }); } }).start();
-
sendMessage:最常用
-
runOnUiThread():非UI线程就用Handler的post发送消息
-
View对象的post方法
View.post(new Runnable() { run() { ... } })
-
-
常见BUG:子线程创建Handler对象
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); // 创建Handler之前必须创建Looper对象 Handler handler = new Handler(); } }).start();
若未创建Looper对象,而直接创建Handler对象,则会抛出异常
java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare() -
非UI线程真的不能进行更新UI的操作吗?(否)
更新UI时会调用invalidate() --> viewParent.invalidateChild() --> invalidateChildInParent().
viewRootImpl()方法会检测当前是否为非UI线程,而该方法在Activity的onResume()中执行. 所以在onCreate()的子线程中更新UI速度很快时,viewRootImpl()方法还未执行,所以就不会报异常.
PART-C_Handler源码简介
Looper创建MessageQueue消息队列,然后无限轮询该MessageQueue中的消息,而消息的创建者即为Handler.
-
Looper源码
-
构造函数
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
创建了一个MessageQueue对象(消息实例).
-
prepare()
public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(true)); }
将Looper实例存储在线程中,并保证线程中只有一个Looper实例.
-
loop()
public static void loop() { final Looper me = myLooper(); if (me == null) { // loop方法必须在prepare方法之后运行 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // 拿到消息队列 // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); /** * 无限循环 */ for (;;) { Message msg = queue.next(); // 取消息,若没有则阻塞 if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } // 将消息交给target(handler对象)的dispatchMessage方法处理 msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); // 释放消息占据的资源 } }
Looper的主要作用如下
- 绑定当前线程,保证一个线程仅有一个Looper实例,同时一个Looper实例中仅有一个MessageQueue实例.
- loop()方法,轮询MessageQueue中的消息,交给消息的target属性的dispatchMessage处理.
-
-
Handler源码
- 构造函数
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } // 获取当前线程保存的Looper实例 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // 获取Looper实例中保存的MessageQueue()消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } . . . /** sendMessage() */ public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } . public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } . public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } ... // 最后均调用sendMessageAtTime()方法 // 方法内获取MessageQueue后调用enqueueMessage()方法 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } . . . private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; // 将当前handler作为target属性 if (mAsynchronous) { msg.setAsynchronous(true); } // 将handler发送的消息最终保存到消息队列中 return queue.enqueueMessage(msg, uptimeMillis); } . . . /** * 上面说道Looper会不断轮询Handler发送到MessageQueue中的消息,然后回调发送该消息的handler中的dispatchMessage方法,下面请看 */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 该方法为空,即我们自己复写的handleMessage方法,然后根据msg.what进行消息处理 handleMessage(msg); } }
- 构造函数
-
PART-C_小结
- Looper.prepare()在本线程中保存一个Looper实例,且该实例中仅保存一个MessageQueue对象.
- Looper.loop()会让当前线程无限轮询MessageQueue中的消息,然后回调msg.target.dispatchMessage(msg)方法.
- Hanlder的构造方法中,先拿到当前线程中保存的Looper实例,然后与实例中的MessageQueue关联.
- Hanlder的sendMessage方法,会给msg的target赋值为handler自身并加入到MessageQueue中.
- 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法.