1,UI只能在主线程中进行操作,否则程序异常。
以下就是一个错误的用法
public class DemoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service_1);
Button button = (Button) findViewById(R.id.service_button);
final TextView textView = (TextView) findViewById(R.id.service_text);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
textView.setText("改变的结果");//子线程中修改了UI,错误!
}
}).start();
}
});
}
}
android提供正确方式之一是使用Handler,或者AsyncTask。
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
//此处在主线程中执行,可以更新UI
switch (msg.what) {
case UPDATE_TEXT:
//在此处进行UI更新
textView.setText("has changed!");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service_1);
Button button = (Button) findViewById(R.id.service_button);
button.setOnClickListener(this);
textView = (TextView) findViewById(R.id.service_text);
}
@Override
public void onClick(View v) {
Log.d("Activity1", "click!");
switch (v.getId()) {
case R.id.service_button:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
//发送信息
handler.sendMessage(message);
}
})
}
}
}
或者使用AsyncTask进行异步任务,UI更新重写onProgressUpdate函数即可,
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
private ProgressDialog progressDialog = new ProgressDialog(null);
@Override
protected void onPreExecute() {
//任务之前的准备
progressDialog.show();
}
@Override
protected void onProgressUpdate(Integer... values) {
//任务同步更新UI
progressDialog.setMessage("Downloaded"+values[0]+"%");
}
@Override
protected Boolean doInBackground(Void... voids) {
//具体任务
return null;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
//任务完成后的工作
//关闭进度条
progressDialog.dismiss();
}
}
2,Handler是什么?咱们来研究实现原理,并且尝试手写一个Handler的实现。
(1) Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。
(2) Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。
(3) MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
(4) Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。
简而言知,Java层Handler Api是给某个线程处理消息的,一般主线程处理UI消息。而消息的处理需要消息队列,即MessageQueue,本质是一个优先级队列,根据Message.when字段排序。而消息的提取需要Looper,该类线程唯一,通过ThreadLocal<Looper>保证。该类调用MessageQueue的next方法,如果有消息则发给对应handler处理,否则阻塞(有消息后唤醒)。
ThreadLocal实现很简单,通过唯一的ThreadLocal对象,将其保存在各个线程的Map中,ThreadLocal.this作为key,即保证了线程唯一。因为每个线程的map都不同,这点可通过get方法知道。
那么如何正确使用Handler呢?查看ActivityThread.main方法(prepareMainLooper本质还是调用了prepare方法),先在线程中准备Looper,然后运行Looper.loop()进入消息循环,此处就是用户进程的创建入口,也是UI线程。该入口是从Zygote进程fork出来的,具体可以查看我写的Activity-R启动流程。
ActivityThread中有一个handler H,该类重写handelrMessage方法,用于处理特定的消息。同时,Looper.perpareMainLooper静态变量mainLooper保存了主线程的Looper,即可与主线程通信,实现相应逻辑。
咱们来手写实现一个简单的Handler机制吧!首先Handler机制包含四个核心类:
Handler、Looper、Message、MessageQueue。
Handler即处理消息的具体执行者,一个Looper下可以有多个Handler哈;
package _AndroidFramework.Handler机制;
//手写一个android handler
public abstract class Handler {
private Message mMessage;
private Looper mLooper;
public Handler() {
Looper looper = Looper.looperThreadLocal.get();
if (looper == null) {
throw new Error("looper not exist where current thread !");
}
this.mLooper = looper;
}
public Handler(Looper looper) {
if (looper == null) {
throw new NullPointerException("");
}
this.mLooper = looper;
}
public void post(Runnable runnable) {
this.mMessage = Message.obtain();
mMessage.callback = runnable;
mMessage.when = 0L;
mMessage.what = null;
this.sendMessage(mMessage);
}
public void sendMessage(Message message) {
message.sendWhen = System.currentTimeMillis();
message.target = this;
this.mLooper.getMessageQueue().offer(message);
}
protected abstract void handleMessage(Message message);
public Looper getLooper() {
return mLooper;
}
public void setLooper(Looper mLooper) {
this.mLooper = mLooper;
}
}
Looper需要保持线程唯一,通过ThreadLocal实现;
package _AndroidFramework.Handler机制;
import java.util.ArrayList;
import java.util.List;
//循环读取者
public class Looper {
public static ThreadLocal<Looper> looperThreadLocal;
private static Looper mMainLooper;
static {
looperThreadLocal = new ThreadLocal<>();
}
private Message mMessage;
private MessageQueue mMessageQueue = new MessageQueue();
private volatile boolean quit = false;
private Looper() {
}
public static Looper getLooper() {
return looperThreadLocal.get();
}
public static Looper getMainLooper() {
return mMainLooper;
}
public static void prepare() {
if (looperThreadLocal.get() != null) {
throw new Error("current thread already has prepared!");
}
looperThreadLocal.set(new Looper());
}
public static void prepareMain() {
prepare();
mMainLooper = getLooper();
}
public static void loop() {
//do something
Looper looper = looperThreadLocal.get();
if (looper == null) {
throw new Error("please use prepare() to create looper!");
}
while (true) {
looper.mMessage = looper.mMessageQueue.peek();
if (looper.mMessage == null || !looper.mMessage.doHandleMessage()) {
continue;
}
//开始处理消息
looper.mMessage = looper.mMessageQueue.poll();
looper.mMessage.target.handleMessage(looper.mMessage);
//处理完消息后,放入缓存池
Message.addCache(looper.mMessage);
looper.mMessage = null;
if (looper.quit) return;
}
}
public void quit() {
this.quit = true;
}
public MessageQueue getMessageQueue() {
return mMessageQueue;
}
}
处理的具体实例就是Message,我们通过享元模式实现;
package _AndroidFramework.Handler机制;
import java.util.LinkedList;
//消息
public final class Message {
private static LinkedList<Message> cache;
private static final int CACHE_SIZE = 30;
static {
cache = new LinkedList<>();
for (int i = 0; i < CACHE_SIZE; i++) {
cache.addLast(new Message());
}
}
public String what;
public Long when;
public Runnable callback;
public Long sendWhen;
public Object arg1;
public Object arg2;
public Handler target;
//是否可以处理当前消息?
public boolean doHandleMessage() {
return System.currentTimeMillis() >= sendWhen + when;
}
/**
* 享元模式
**/
public static Message obtain() {
if (cache.size() >= CACHE_SIZE) {
return new Message();
}
return cache.removeFirst();
}
public static void addCache(Message message) {
if (message == null) return;
if (cache.size() >= CACHE_SIZE) return;
message.when = null;
message.sendWhen = null;
message.what = null;
message.callback = null;
message.target = null;
cache.addLast(message);
}
@Override
public String toString() {
return "Message{" +
"what='" + what + '\'' +
", when=" + when +
", sendWhen=" + sendWhen +
", arg1=" + arg1 +
", arg2=" + arg2 +
'}';
}
}
存放消息的地方就是消息队列,也是Looper观察读取的地方,和Looper一样保持线程唯一,并且保持消息插入的有序性,本质就是优先级队列。
package _AndroidFramework.Handler机制;
//优先级队列
public class MessageQueue {
//队列大小
private int size;
//队列头
private Node head;
//队列尾
private Node tail;
public MessageQueue() {
this.size = 0;
this.head = null;
this.tail = null;
}
/**
* 维持的有序列表节点
**/
private static class Node {
Node pre;
Node next;
Message message;
public Node(Message message) {
this.message = message;
}
}
private boolean insertToLinkTail(Node node) {
if (this.head == null) {
this.head = this.tail = node;
return true;
}
//顺序插入
Node p = head;
while (p != null) {
if (p.message.when > node.message.when) {
break;
}
p = p.next;
}
if (p == head) {
//头部
p.pre = node;
node.next = p;
head = node;
return true;
} else if (p == null) {
//尾部
tail.next = node;
node.pre = tail;
tail = node;
return true;
} else {
//中间
Node pre = p.pre;
pre.next = node;
node.next = p;
node.pre = pre;
p.pre = node;
return true;
}
}
private Node removeFromLinkHead() {
if (head == null) return null;
Node p = head;
Node newHead = head.next;
if (newHead == null) {
head = tail = null;
} else {
newHead.pre = null;
head = newHead;
}
return p;
}
/**
* 消息api
**/
public boolean offer(Message message) {
synchronized (this) {
if (this.insertToLinkTail(new Node(message))) {
this.size++;
return true;
}
return false;
}
}
public Message poll() {
synchronized (this) {
Node node = this.removeFromLinkHead();
if (node == null) {
return null;
}
this.size--;
return node.message;
}
}
public Message peek() {
synchronized (this) {
if (head == null) return null;
return head.message;
}
}
public int getSize() {
synchronized (this){
return this.size;
}
}
}
咱们测试一下吧!测试代码如下。
package _AndroidFramework.Handler机制;
public class Test {
public static void main(String[] args) {
Looper.prepareMain();
sendMessageInOtherThread();
//主线程创建完毕,开始接听new Loop消息
Looper.loop();
}
public static void sendMessageInOtherThread() {
new Thread(() -> {
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
if (message.callback != null) {
message.callback.run();
}
}
};
Message message = Message.obtain();
message.callback = () -> System.out.println(Thread.currentThread().getName() + " handler1 我是消息1---" + System.currentTimeMillis());
message.when = 1000L;
Message message2 = Message.obtain();
message2.callback = () -> System.out.println(Thread.currentThread().getName() + " handler1 我是消息2---" + System.currentTimeMillis());
message2.when = 3000L;
Message message3 = Message.obtain();
message3.callback = () -> {
System.out.println(Thread.currentThread().getName() + " handler1 我是消息3---我要销毁主程序" + System.currentTimeMillis());
Looper.getMainLooper().quit();
};
message3.when = 7000L;
handler.sendMessage(message);
handler.sendMessage(message2);
handler.sendMessage(message3);
System.out.println(Thread.currentThread().getName() + " handler1 发送时间---" + System.currentTimeMillis());
}).start();
new Thread(() -> {
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
if (message.callback != null) {
message.callback.run();
}
}
};
Message message = Message.obtain();
message.callback = () -> System.out.println(Thread.currentThread().getName() + " handler2 我是消息1---" + System.currentTimeMillis());
message.when = 4000L;
Message message2 = Message.obtain();
message2.callback = () -> System.out.println(Thread.currentThread().getName() + " handler2 我是消息2---" + System.currentTimeMillis());
message2.when = 2000L;
Message message3 = Message.obtain();
message3.callback = () -> {
System.out.println(Thread.currentThread().getName() + " handler2 我是消息3---我要销毁主程序" + System.currentTimeMillis());
Looper.getMainLooper().quit();
};
message3.when = 6000L;
handler.sendMessage(message);
handler.sendMessage(message2);
handler.sendMessage(message3);
System.out.println(Thread.currentThread().getName() + " handler2 发送时间---" + System.currentTimeMillis());
}).start();
}
}
结果:
ok,本次的实现就写到这,注意上述代码只是对Java层Handler的模拟,Android的Looper使用的监听机制是epoll,通过native层实现。
-------------------------------------------------------------------------
补充一个知识点,消息屏障
消息屏障本质是没有target属性的message,当消息队列遇到屏障消息后,阻塞所有同步消息,优先执行异步消息,以下是messageQueue相关代码,方法名postSyncBarrier就名副其实,异步消息的屏障呢,
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
//根据时间,找到插入队列位置,保存到prev
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
// 插入屏障消息
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
//队头,直接放到消息队列头部
msg.next = p;
mMessages = msg;
}
//返回mNextBarrierToken++ 结果
return token;
}
}
那么,进入next函数,咱看看屏障消息如何使用。
如果target字段为null,代表这是个屏障消息,此后迭代消息队列,找到最近异步消息,满足条件后就执行,
那么如何移除屏障?通过removeSyncBarrier方法,很简单的,
//传入屏障消息唯一识别taken,
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
//从队头迭代消息,找到屏障消息
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
//没有屏障,抛错
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
//移除屏障的链表操作
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
那么,如何传入异步消息?获取message后,通过调用setAsyncchronous方法,设置异步即可,
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
注意,MessageQueue中,消息屏障相关方法被标记为@hide,可以通过反射调用,