FrameWork内核解析之Handler消息机制(二),android基础面试题及答案

  • Message.what------------------>用来标识信息的int值,通过该值主线程能判断出来自不同地方的信息来源
  • Message.arg1/Message.arg2----->Message初始定义的用来传递int类型值的两个变量
  • Message.obj------------------->用来传递任何实例化对象
  • 最后通过sendMessage将Message发送出去。
  • Handler所在的线程通过handlerMessage方法就能收到具体的信息了,如何判断信息的来源呢?当然是通过what值啦。
  • 怎么样很简单吧
    */

文章的开头说过,Handler不仅仅是能过将子线程的数据发送给主线程,它适用于任意两个线程之间的通信。
####2、两个子线程之间通信
在一个线程创建Handler,另外一个线程通过持有该Handler的引用调用sendMessage发送消息。
代码如下:

private Handler handler;
private void handlerDemoByTwoWorkThread() {
Thread hanMeiMeiThread = new Thread() {
@Override
public void run() {
// Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "hanMeiMei receiver message: " + ((String) msg.obj));
Toast.makeText(MainActivity.this, ((String) msg.obj), Toast.LENGTH_SHORT).show();
}
};
// Looper.loop();
}
};
Thread liLeiThread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = handler.obtainMessage();
message.obj = “Hi MeiMei”;
handler.sendMessage(message);
}
};
hanMeiMeiThread.setName(“韩梅梅 Thread”);
hanMeiMeiThread.start();
liLeiThread.setName(“李雷 Thread”);
liLeiThread.start();

/*

  • 搞定,我们创建了两个Thread,liLeiThread和hanMeiMeiThread两个线程,很熟悉的名字啊!
  • 跟之前的代码没太大区别hanMeiMeiThread创建了Handler,liLeiThread通过Handler发送了消息。
  • 只不过此处我们只发送一个消息,所以没有使用what来进行标记
  • 运行看看,我们的李雷能拨通梅梅吗?
  • 啊哦,出错了
  • 05-13 17:08:17.709 20673-20739/? E/AndroidRuntime: FATAL EXCEPTION: 韩梅梅 Thread
    Process: design.wang.com.designpatterns, PID: 20673
    java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
    at android.os.Handler.(Handler.java:200)
    at android.os.Handler.(Handler.java:114)
    *Can’t create handler inside thread that has not called Looper.prepare()
  • -----------》它说我们创建的handler没有调用Looper.prepare();
  • 好的,我们在实例化Handler之前调用下该方法,看一下。加上是不是没有报错了呢。
  • 等等,虽然没有报错,但是hanMeiMeiThread也没有接到消息啊,消息呢?别急。
  • 我们在Handler实例化之后加上Looper.loop();看一看,运行一下,是不是收到消息了呢。
  • 这是为什么呢?
  • 接下来我们就去看看Handler是怎么实现的发消息呢,弄清楚了原理,这里的原因也就明白了。
    */

}

接下来我们看下,为什么在子线程里实例化的时候不调用Looper.prepare()就会报错呢?

//我们先来看看new Handler();时出错的原因。后续讲解源码分析只贴出关键部分。
//如下是Handler构造函数里抛出上文异常的地方,可以看到,由于mLooper对象为空才抛出的该异常。
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
“Can’t create handler inside thread that has not called Looper.prepare()”);
}
/*
异常的原因看到了,接下来我们看看Looper.prepare()方法都干了些什么?
*/
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));
}
/*
可以看到,该方法在当前thread创建了一个Looper(), ThreadLocal主要用于维护线程的本地变量,
*/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//而Looper的构造函数里面又为我们创建了一个MessageQueue()对象。

了解到此,我们已经成功引出了Handler机制几个关键的对象了,Looper、MessageQueue、Message。
为什么在主线程中创建Handler不需要要用Looper.prepare()和Looper.loop()方法呢?
其实不是这样的,App初始化的时候都会执行ActivityThread的main方法,我们可以看看ActivityThread的main()方法都做了什么!

Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, “ActivityThread”));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
/*
真相只有一个,是的在创建主线程的时候Android已经帮我们调用了Looper.prepareMainLooper()
和Looper.loop()方法,所以我们在主线程能直接创建Handler使用。
*/

我们接着来看Handler发送消息的过程:

//调用Handler不同参数方法发送Message最终都会调用到该方法
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);
}

sendMessage的关键在于enqueueMessage(),其内部调用了messageQueue的enqueueMessage方法

boolean enqueueMessage(Message msg, long when) {

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 {
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;
}
/*从代码可以看出Message被存入MessageQueue时是将Message存到了上一个Message.next上,
形成了一个链式的列表,同时也保证了Message列表的时序性。
*/

Message的发送实际是放入到了Handler对应线程的MessageQueue中,那么,Message又是如何被取出来的呢?
细心的朋友可能早早就发现了,之前抛出异常的地方讲解了半天的Loop.prepare()方法,一直没有说到Loop.loop()方法。同时,在之前的例子中也看到了,如果不调用Looper.loop()方法,Handler是接受不到消息的,所以我们可以大胆的猜测,消息的获取肯定和它脱不了关系!当然关怀疑还不行,我们还必须找出真相来证明我们的猜想?那还等什么,先看看loop()方法吧。

public static void loop() {
//可以看到,在调用Looper.prepare()之前是不能调用该方法的,不然又得抛出异常了
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 traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

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();
}
}
/*
这里我们看到,mLooper()方法里我们取出了,当前线程的looper对象,然后从looper对象开启了一个死循环
不断地从looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用
dispatchMessage去分发消息,通过代码可以看出target就是我们创建的handler。我们再继续往下分析。Message的分发
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/*好了,到这里已经能看清晰了
可以看到,如果我们设置了callback(Runnable对象)的话,则会直接调用handleCallback方法
*/

最后

这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容

如果你有需要的话,可以私信我【进阶】发给你

除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

如果你有需要的话,可以点击我的GitHub免费获取!

分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~

/a120464/Android-P7/blob/master/Android%E5%BC%80%E5%8F%91%E4%B8%8D%E4%BC%9A%E8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)**

[外链图片转存中…(img-ooHAO7Vq-1646231853854)]

分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~

喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值