message.obj = “I am message from work thread”;
mHandler.sendMessage(message);
}
}.start();
}
/*
- 通常我们在主线程中创建一个Handler,
- 然后重写该Handler的handlerMessage方法,可以看到该方法传入了一个参数Message,
- 该参数就是我们从其他线程传递过来的信息。
- 我们在来看下子线程中如何传递的信息,子线程通过Handler的obtainMessage()方法获取到一个Message实例,
- 我们来看看Message的几个属性:
- 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;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
欢迎大家一起交流讨论啊~
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
4c3ab8389e65ecb71ac0)
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算