0
点赞
收藏
分享

微信扫一扫

Android线程三、Handler


PART-A_写在前面

  • Handler是什么

Handler是Android提供的:

  1. 一套更新UI的机制.
  2. 一套消息处理的机制,我们可以通过Handler发送和处理消息.
  • 为什么使用Handler

Android在设计时,就封装了一套消息的创建、传递、处理机制,如果不遵循这样的机制,就没有办法更新UI信息,就会抛出异常信息.

  • Google为什么只设计了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);
}
});
}

  • 重要结论
  1. 子线程向主线程发送消息,必须有主线程的Handler实例,该Hnadler绑定了主线程的Looper.
  2. 主线程向子线程发送消息,必须有子线程的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的主要作用如下

  1. 绑定当前线程,保证一个线程仅有一个Looper实例,同时一个Looper实例中仅有一个MessageQueue实例.
  2. 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_小结
  1. Looper.prepare()在本线程中保存一个Looper实例,且该实例中仅保存一个MessageQueue对象.
  2. Looper.loop()会让当前线程无限轮询MessageQueue中的消息,然后回调msg.target.dispatchMessage(msg)方法.
  3. Hanlder的构造方法中,先拿到当前线程中保存的Looper实例,然后与实例中的MessageQueue关联.
  4. Hanlder的sendMessage方法,会给msg的target赋值为handler自身并加入到MessageQueue中.
  5. 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法.


举报

相关推荐

0 条评论