一.什么是Handler?
1.如何在子线程更新UI?
1.在viewRootImpl初始化之前更新
我们知道ViewRootImpl是在onResume之后初始化完成,view也没有被attacthtoWindow,所以也不会去检测线程,那下面两种都可以更新UI,如果在线程中做个几百毫秒的延时,就会做线程检测,但是也不完全,比如TextView如果设置了固定的高度,我们参考源码就可以看到,它在调用重绘后就结束了。
TextView textView;
ImageView img;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
img = findViewById(R.id.img);
new Thread(new Runnable() {
@Override
public void run() {
// textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
textView.setLayoutParams(new LinearLayout.LayoutParams(20, 20));
textView.setText("onCreate");
/** textView.getLayout()获取的是空
* ...
* if (mLayout != null) {
* checkForRelayout();
* }
* ...
*/
System.out.println("attach:"+textView.isAttachedToWindow());
System.out.println("parent:"+textView.getParent());
System.out.println("layout:"+textView.getLayout());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
/*
只要TextView设置的为固定值 依然可以更新
private void checkForRelayout() {
// If we have a fixed width, we can just swap in a new text layout
// if the text height stays the same or if the view height is fixed.
if ((mLayoutParams.width != ViewGroup.LayoutParams.WRAP_CONTENT
|| (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
&& (mHint == null || mHintLayout != null)
&& (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
// Static width, so try making a new text layout.
int oldht = mLayout.getHeight();
int want = mLayout.getWidth();
int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
*//*
* No need to bring the text into view, since the size is not
* changing (unless we do the requestLayout(), in which case it
* will happen at measure).
*//*
makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
false);
if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
// In a fixed-height view, so use our new text layout.
if (mLayoutParams.height != ViewGroup.LayoutParams.WRAP_CONTENT
&& mLayoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
autoSizeText();
invalidate();
return;
}
// Dynamic height, but height has stayed the same,
// so use our new text layout.
if (mLayout.getHeight() == oldht
&& (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
//走到这里return 了
autoSizeText();
invalidate();
return;
}
}
// We lose: the height has changed and we have a dynamic height.
// Request a new view layout using our new text layout.
requestLayout();
invalidate();
} else {
// Dynamic width, so we have no choice but to request a new
// view layout with a new text layout.
nullLayouts();
requestLayout();
invalidate();
}
}*/
textView.setText("onCreate2");
System.out.println("attach222:"+textView.isAttachedToWindow());
System.out.println("parent222:"+textView.getParent());
System.out.println("layout222:"+textView.getLayout());
}
}).start();
}
@Override
protected void onResume() {
super.onResume();
new Thread(new Runnable() {
@SuppressLint("SetTextI18n")
@Override
public void run() {
/**
* final AttachInfo ai = mAttachInfo;
* final ViewParent p = mParent;
* if (p != null && ai != null && l < r && t < b) {
* final Rect damage = ai.mTmpInvalRect;
* damage.set(l, t, r, b);
* p.invalidateChild(this, damage);
* }
*/
img.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
img.setImageResource(R.drawable.ic_launcher_background);
System.out.println("attach333:"+img.isAttachedToWindow());
System.out.println("parent333:"+img.getParent());
}
}).start();
2.surfaceview
这个就不用说了,独立于view,有自己单独的surface。
3.toast
在子线程中显示前,调用下Looper.prepare(),后面开启looper循环即可,这个可以查看源码。下面贴下使用,源码部分就不贴了,也很简单,就是它会做下looper检测,现实的时候会通过handler将现象发送出去,中间还有进程间通讯,这里就不展开了。
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Looper.prepare();
Toast.makeText(MainActivity.this,"it's toast!!!",Toast.LENGTH_SHORT).show();
Looper.loop();
}
}).start();
2.使用handler切换线程
这个就是在子线程中通过handler将消息发送到主线程去更新UI,这个Handler可以是在子线程中创建,但是Looper必须指定主线程中的
private Handler handler =new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this,"handler"+msg.what,Toast.LENGTH_SHORT).show();
break;
}
return false;
}
});
new Thread(new Runnable() {
@Override
public void run() {
//1.通过主线程的Handler发送
//handler.sendEmptyMessage(1);
//2.新建一个Handler,传入主线程的Looper更新
new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 2:
System.out.println("handler inner thread used main looper"+ msg.what);
Toast.makeText(MainActivity.this,"handler"+msg.what,Toast.LENGTH_SHORT).show();
break;
}
}
}.sendEmptyMessage(2);
}
}).start();
二.Handler以及HandlerThread
1.Handler机制及源码解析
前面我们已经了解到,我们的UI只能在主线程中更新,这个是一个不完全正确的结论,但是正常情况下,我们都会通过主线程去更新UI,所以就绕不开Handler,那我们接下来就分析下Handler的源码及其工作流程,它主要由三大流程,消息的发送、消息队列循环以及消息的分发处理。
1.Handler发送消息流程:
Handler.java
//构造方法
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;//我们传过来的是主线程的looper
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public final boolean post(Runnable r)
{
//把runnable包装成Message对象
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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;//注意这个里面是把this,也就是当前Handler给target了,后面消息处理会拿出这个target执行的
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue.java
插入到Message中
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
2.消息循环
前面已经说到Handler中需要有个Looper对象,当我们收到消息的时候,就会通过Handler中的额Looper中的MessageQueue将消息加入到其中,那我们来看看这个Looper:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. See also: {@link #prepare()}
*
* @deprecated The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
*/
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们可以看到Looper并未给我们提供公开的构造方法去创建,而是通过Looper.prepare或looper.prepareManLooper去创建Looper,并且针对每个线程这个looper是唯一的,那创建的MessageQueue对应线程也是唯一 。消息已经发送了并且加入队列了那是怎么
消费的呢?答案还是在Looper中的Looper这个方法中,这个方法会一直循环检测是否由消息,如果由就会丢给Handler执行:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
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(); // might block
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);//还记得target是什么吗?前面说到的Handler
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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.recycleUnchecked();
}
}
3.消息处理
其实我们可以看到,决定我们代码在哪里执行的并非是Handler在哪里创建的,而是Looper在哪里创建的,如果你在主线程创建了Looper,那么这个looper循环消息拿到消息分发给message.target.dispathMessage就是在主线程,如果是在子线程这段消息循环就在子线程中执行,并且Handler可以是任意个,对应线程的Looper以及MessageQueue就只有一个。然后我们在来解析下Handler中的dispatchMessage这个方法:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static void handleCallback(Message message) {
message.callback.run();
}
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
//1.postRunnable执行
handleCallback(msg);
} else {
if (mCallback != null) {
//2.传入回调
if (mCallback.handleMessage(msg)) {
return;
}
}
//3.执行方法重写
handleMessage(msg);
}
}
我们从源码里面可以看到,它首先会判断msg.callBack是否为空,这里的callback其实就是handler.postRunnable中runnable,如果没有就会调用mCallBack,那这个其实就是new Handler(CallBack callBack)中的接口回调,如果你没有传入这个回调,那就执行handlerMessage,也就是你需要重写这个方法。
2.HandlerThread的用法
HandlerThread其实就是一个Thread,只不过它在start方法时,会自动帮助我们手动创建一个looper并且开启looper循环,相比较一般Thread而言,他自带了消息队列,任务按顺序执行,无需关心线程同步, ,并且提供了退出循环的接口。下面我们就来看看它的用法:
private void testHandlerThread(){
//1.创建后台工作线程,其实就是一个线程
HandlerThread handlerThread=new HandlerThread("handlerThread1");
//2.开启looper循环,会创建当前线程的looper,然后循环,会一直循环消息,相比较普通线程的while(xx)
handlerThread.start();
//3.new一个Handler,传入HandlerThread中的looper,重写Handler的handlerMessage就可以发送并接受
//handlerThread中的Message了,并且它是子线程的
Handler workHandler=new Handler(
handlerThread.getLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
System.out.println("testHandlerThread---"+msg);
}
};
//4.发送消息
workHandler.sendEmptyMessage(22);
//5.使用完后要安全退出,避免内存泄漏
handlerThread.quitSafely();
}
三.手写Handler
1.消息体
从缓存链表中获取消息体,如果没有则创建一个消息体。
插入缓存,是由MessageQueue操作的,当使用消息时会设置标记位,如果不在使用则将其加入到消息体中的缓存池中:
使用单向链表和复用池,最多可以缓存50个,使用完成后放置对象池中,避免对象的重复创建。
public class MyMessage {
public MyHandler target;
public Runnable callBack;
public int what;
private int useFlag = 0; //使用标志位
static final int FLAG_IN_USE = 1 ; //正在使用
public static final Object sPoolSync = new Object(); //同步锁
private static MyMessage sPool; //链表头 即当前正在使用的 静态的
private static int sPoolSize = 0; //当前单向链表长度
private static final int MAX_POOL_SIZE = 50;//链表最大长度
private MyMessage next;//当前链表的下一个结点
/**
* {@link MyHandler#obtain()}
*
* @param myHandler 绑定的回调
* @return
*/
public static MyMessage obtain(MyHandler myHandler) {
MyMessage myMessage = obtain();
myMessage.target = myHandler;
return myMessage;
}
/**
* {@link MyHandler#obtain()}
*
* @return
*/
public static MyMessage obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
System.out.println("hit it from cache ---->");
//说明有数据,返回
MyMessage myMessage = sPool;//返回当前头部数据
sPool = myMessage.next;//将当前头部指向头部的下一个结点
myMessage.what = -1;//清除消息内容
myMessage.useFlag = 0;//清除使用标记
sPoolSize--;
return myMessage;
}
}
//链表中并没有
return new MyMessage();
}
/**
* 放入链表池 整个退出时 如果正在使用该消息则return
*/
public void recycle() {
if (isInUse()) { //是否正在使用 如果使用不许放到回收池中
return;
}
unCheckRecycle();
}
/**
* looper 中遍历过这条消息并且执行过操作了 如msg.target.dispatchMessage()之后
*/
public void unCheckRecycle() {
useFlag = FLAG_IN_USE;//放置多次recycle,放入后,当我们通过obtain获取时又会置为
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
System.out.println("recycle it to cache ---->");
next = sPool;//将当前的next设置成上一个头 这里sPool是上一个头 next是当前message
sPool = this;//将当前头设置成当前回收的对象
sPoolSize++;
}
}
}
/**
* {@link MyMessage#recycle()} 判断是否在用
* {@link MyMessageQueue#enqueueMessage(MyMessage)} }
*
* @return
*/
public boolean isInUse() {
return ((useFlag & FLAG_IN_USE) == FLAG_IN_USE);
}
/**
* {@link MyMessageQueue#enqueueMessage(MyMessage)} } 标记在用
*/
public void markInUse() {
useFlag |= FLAG_IN_USE;
}
}
2.消息队列
存储消息队列,并提供取出和存入消息的接口,当消息取出时,标记已使用,当消息被取出使用后放入消息池中。
public class MyMessageQueue {
//消息阻塞队列
private BlockingQueue<MyMessage> myMessages = new ArrayBlockingQueue<>(50);
MyMessage myMessage;
private boolean mQuiting = false;
/**
* 存入到消息队列
* 1.判断是否正在使用
* 2.判断是否退出looper
* 3.设置使用标记
* @param myMessage
*/
public void enqueueMessage(MyMessage myMessage) {
try {
if(myMessage.isInUse()){
//如果正在使用
System.out.println("msg is using------>>>");
return;
}
if(mQuiting){
//如果全局退出了,就不再加入到
System.out.println("queue has quit--->>>");
myMessage.recycle();
return;
}
myMessage.markInUse();//标记正在使用
myMessages.put(myMessage);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 取出消息 阻塞队列 直到取到消息
*
* @return
*/
public MyMessage next() {
try {
return myMessages.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public void quit() {
mQuiting = true;
}
}
3.发动机-Looper
消息循环的发动机,并通过ThreadLocal保存起来,让单个线程只拥有一个发动机,并且提供发动和取消发动的接口。
public class MyLooper {
private static ThreadLocal<MyLooper> threadLocal = new ThreadLocal();
private static MyMessageQueue myMessageQueue;
public static void prepareLooper() {
prepare();
}
private static void prepare() {
if (threadLocal.get() != null) {
throw new RuntimeException("my looper is not null can not recreate!!!");
}
//当前线程只允许创建1次
threadLocal.set(new MyLooper());
}
private MyLooper() {
System.out.println("create MyLooper---->>>>>>");
myMessageQueue = new MyMessageQueue();
}
//线程唯一
public static MyLooper myLooper() {
System.out.println("getMyLooper---" + threadLocal.get());
return threadLocal.get();
}
//循环消息
public static void looper() {
for (; ; ) {
MyMessage next = myMessageQueue.next();//拿不到消息就会阻塞在这里
next.target.dispatchMessage(next);
next.unCheckRecycle();//放到链表池中
}
}
public void quit(){
synchronized (this){
myMessageQueue.quit();
}
}
public MyMessageQueue getMessageQueue() {
return myMessageQueue;
}
}
4.中间人-Handler
往发动机中发送消息,可以初始化发动机,但是在单个线程中,发动机只能初始化1次,他也是发动机消息执行回调的承载,但是回调执行的线程是由发动机在那个线程创建决定的,并非是完全由Handler决定。
public class MyHandler {
private final MyLooper myLooper;
private MyMessageQueue myMessageQueue;
private CallBack callBack;
public void handleMessage(MyMessage myMessage) {
}
/**
* 发动机的回调
*/
public void dispatchMessage(MyMessage next) {
if (null != next.callBack) {
next.callBack.run();
} else {
if (callBack != null) {
if (callBack.handlerMessage(next)) {
return;
}
}
handleMessage(next);
}
}
public interface CallBack {
boolean handlerMessage(MyMessage myMessage);
}
public MyHandler() {
this(null, MyLooper.myLooper());
}
public MyHandler(CallBack callBack) {
this(callBack, MyLooper.myLooper());
}
public MyHandler(MyLooper myLooper) {
this(null, myLooper);
}
//在xx个线程使用handler前,需要确保该线程已经创建了looper
public MyHandler(CallBack callBack, MyLooper looper) {
if (looper == null) {
System.out.println("");
this.myLooper = MyLooper.myLooper();
} else {
this.myLooper = looper;
}
if (myLooper == null) {
throw new RuntimeException(
"Can't create MyHandler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
this.callBack = callBack;
myMessageQueue = myLooper.getMessageQueue();
}
public void post(Runnable runnable) {
MyMessage myMessage = new MyMessage();
myMessage.callBack = runnable;
sendMessage(myMessage);
}
public void sendMessage(MyMessage myMessage) {
myMessage.target = this;
myMessageQueue.enqueueMessage(myMessage);
}
/**
* 提供获取消息的api
*
* @return
*/
public MyMessage obtain() {
return MyMessage.obtain(this);
}
}
5.测试
1.handler 消息发送和接收的流程
/**
* 测试handler 消息发送和接收的流程
*/
@Test
public void myHandlerTest() {
//创建当前线程的looper
MyLooper.prepareLooper();
//MyLooper.prepareLooper();
MyHandler myHandler = new MyHandler() {
@Override
public void handleMessage(MyMessage myMessage) {
super.handleMessage(myMessage);
System.out.println("handleMessage:" + myMessage.what);
}
};
MyMessage myMessage = new MyMessage();
myMessage.what = 1;
myHandler.sendMessage(myMessage);
MyMessage myMessage1 = myHandler.obtain();//从消息池中获取
myMessage1.what = 2;
myHandler.sendMessage(myMessage1);
myHandler.post(new Runnable() {
@Override
public void run() {
System.out.println("runnable is run--->>>");
}
});
//开启消息循环
MyLooper.looper();
}
2.消息池
我们修改消息池大小为10后测试。
/**
* 测试消息池
*/
@Test
public void testMessagePool() {
MyLooper.prepareLooper();
//MyLooper.prepareLooper();
MyHandler myHandler = new MyHandler() {
@Override
public void handleMessage(MyMessage myMessage) {
super.handleMessage(myMessage);
System.out.println("handleMessage:" + myMessage.what);
}
};
for (int i = 0; i < 20; i++) {
MyMessage myMessage = new MyMessage();//放入消息池中
myMessage.unCheckRecycle();
}
for (int i = 0; i < 20; i++) {
myHandler.obtain();//从消息池中获取
}
}