Android开发之线程间通信

当我们的软件启动的时候,计算机会分配进程给到我们运行的程序,在进程中包含多个线程用于提高软件运行速度。

在android网络请求中,我们知道在日常开发中不能在子线程中跟新ui,否则报错Only the original thread that created a view hierarchy can touch its views.,那么我们怎么判断是否是在子线程呢,可以通过log打印在控制台中找到打印信息,这里面就有线程信息。

在MainActivity类onCreate方法中通过

new Thread(new Runnable() {
@Override
public void run() {
Log.d("TAG", "run: ");
}
}).start();

可以在AndroidStudio下面的Logcat中看到打印信息,这其中就包含了线程id,每次启动软件所拿到的线程和进程id是可能不同的。

11372是系统分配给我们的进程id,-后面的数字就是线程id,每次启动都会重新分配。除此之外还有个uid,是软件安装时系统分配给我们的,卸载软件重装会重新分配,跟新软件覆盖是不会重新分配的。

也可以通过android.os.Process的方式调出查看

android.os.Process.myPid();//进程id
android.os.Process.myUid();//用户id
android.os.Process.myTid();//线程id,在哪个线程中调用就是哪个线程的id

线程间通信的作用

线程通信是为了不同线程互相传递信息,能够在将子线程的数据传递到主线程中,方便调用。

线程通信的方式

目前android主流的线程通信的方式有

1、调用Handler类

2、调用Activity类的runOnUiThread方法

3、调用View类中的post方法

4、通过新建一个继承AsyncTask父类的子类来实现

5、使用EventBus等工具

调用Handler类

创建Handler类,当他被创建的时候他就会开始一直监听是否有消息传递过来,我们通过在子线程中调用该Handler的消息传递方法sendMessage可以向主线程的Handler的消息监听方法handleMessage发送消息,实现线程通信。

示例代码

		Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
System.out.println(message.obj+"线程id"+android.os.Process.myTid());
return true;
}
});
new Thread(new Runnable() {
@Override
public void run() { Message msg = new Message();
System.out.println("线程id"+android.os.Process.myTid());
msg.obj = "子线程发送的消息Message";
handler.sendMessage(msg);
}
}).start();

打印结果:

可以看到子线程的id是12028,主线程id是11977,而且子线程在Handler下方执行并且当子线程发送消息时,主线程的Handler执行了handleMessage监听方法,这样就可以实现在主线程handleMessage方法中进行ui操作等无法在子线程中执行的操作了。

调用Activity类的runOnUiThread方法

在网络请求(一中说过用法)Android网络请求(1) - 高同学,你好 - 博客园 (cnblogs.com)

示例代码

new Thread(new Runnable() {
@Override
public void run() {
String name = "android";
System.out.println(android.os.Process.myTid());
runOnUiThread(new Runnable() {
@Override
public void run() {
System.out.println(android.os.Process.myTid());
//TODO 执行ui操作
}
});
}
}).start();

打印线程id可以看到,在子线程调用了runOnUiThread方法后,成功切换到了主线程

打印结果

调用View类中的post方法

它其实和调用Activity类的runOnUiThread方法很像,都是但是一个是调用activity的方法,另一个时调用View的方法,使用方式也是一样的。但是要通过对应的View调用post方法。

示例代码

new Thread(new Runnable() {
@Override
public void run() {
String name = "android";
System.out.println(Process.myTid());
textView.post(new Runnable() {
@Override
public void run() {
System.out.println(Process.myTid()+name);
textView.setText(name);
}
});
}
}).start();

打印截图

通过新建一个继承AsyncTask父类的子类来实现

AsyncTask时通过重写doInBackground和onPostExecute方法来实现线程的通信,onPostExecute可以直接使用参数,参数时doInBackground时的返回值。

示例代码

新建子类

private class MyAsyncTask extends AsyncTask {
private TextView tv; public MyAsyncTask(TextView tv) {
this.tv = tv;
} //子线程进行请求返回数据
@Override
protected Object doInBackground(Object[] objects) {
System.out.println(Process.myTid()+"doInBackground打印id");
return "name";
} //直接调用子线程返回的o
//切换到主线程进行操作
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
System.out.println(Process.myTid()+"onPostExecute打印id");
tv.setText(String.valueOf(o));
}
}

MainActivity调用

TextView tv = findViewById(R.id.text);
System.out.println(Process.myTid()+"主线程打印id");
new MyAsyncTask(tv).execute("aaaaa");

打印结果

使用EventBus等工具

EventBus是一个消息总线,以观察者模式实现,用于简化程序的组件、线程通信,可以轻易切换线程、开辟线程,包括后台线程、UI线程、异步线程。

示例代码

先导入EventBus在项目文件下build.gradle(app)的dependencies中导入所需要的库

implementation group: 'org.greenrobot', name: 'eventbus', version: '3.0.0'

新建EventBus所需要接受的实体类,也可以使用String直接发送消息。不知到为什么好像使用Integer、int会报错,其他的我也没具体测试过。

public class Event{
private int code;
private String msg; public Event(int code, String msg) {
this.code = code;
this.msg = msg;
} public int getCode() {
return code;
} public String getMsg() {
return msg;
}
}

创建一个监听方法,方法名自定义,参数类型为你希望接收到的参数类型。假如有两个监听,我发送的是String类型的消息,那么就只有接受值为String类型的监听方法才会触发监听。在监听方法上面加上注解@Subscribe,也可以设置注解的模式,不设置就是使用的默认模式,默认模式就是你在子线程发送的数据,那么监听方法也是在子线程内,同样不能设置ui,默认模式根据你发送数据时所在的线程决定。ThreadMode.MAIN是在主线程执行

 @Subscribe(threadMode = ThreadMode.MAIN)
public void msg(Event event){
System.out.println(Process.myTid()+"msg打印id");
System.out.println(event.msg);
}

使用EventBus时要在OnCreate方法中注册,在onDestroy方法中销毁

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
EventBus.getDefault().unregister(this);
super.onDestroy();
}

子线程中就可以直接通过EventBus发送消息了。

new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Process.myTid()+"Thread打印id");
Event event = new Event(200,"成功");
EventBus.getDefault().post(event);
}
}).start();

打印结果

总结

线程间通信是Android开发中较为重要的知识点,如果不牢记,很容易出现在子线程中直接操作ui报错却不知道哪里错了的事情。身边的老有人问我这种错误。希望大家能够牢记知识点。高同学祝你步步高升!

Android开发之线程间通信的更多相关文章

  1. Android中线程间通信原理分析:Looper,MessageQueue,Handler

    自问自答的两个问题 在我们去讨论Handler,Looper,MessageQueue的关系之前,我们需要先问两个问题: 1.这一套东西搞出来是为了解决什么问题呢? 2.如果让我们来解决这个问题该怎么 ...

  2. Android线程间通信机制——深入理解 Looper、Handler、Message

    在Android中,经常使用Handler来实现线程间通信,必然要理解Looper , Handler , Message和MessageQueue的使用和原理,下面说一下Looper , Handl ...

  3. 源码分析Android Handler是如何实现线程间通信的

    源码分析Android Handler是如何实现线程间通信的 Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的.开发者从一开始就被告知必须在主线程中进行UI操作.但H ...

  4. iOS开发NSOperation 三:操作依赖和监听以及线程间通信

    一:操作依赖和监听 #import "ViewController.h" @interface ViewController () @end @implementation Vie ...

  5. Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

    一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...

  6. Java 里如何实现线程间通信(转载)

    出处:http://www.importnew.com/26850.html 正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程 ...

  7. Java 如何实现线程间通信?(notify、join、CountdownLatch、CyclicBarrier、FutureTask、Callable )

    转自:https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247486499&idx=1&sn=d3f2d6959df ...

  8. Java 里如何实现线程间通信

    正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了. 本文涉及到的知识点:thread.join(), object.w ...

  9. Java 如何实现线程间通信

    正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了. 本文涉及到的知识点: thread.join(), object. ...

  10. 说说Java线程间通信

    序言 正文 [一] Java线程间如何通信? 线程间通信的目标是使线程间能够互相发送信号,包括如下几种方式: 1.通过共享对象通信 线程间发送信号的一个简单方式是在共享对象的变量里设置信号值:线程A在 ...

随机推荐

  1. 【Vue学习笔记】—— vue的基础语法 { }

    学习笔记 作者:oMing vue v-on: 简称 @ <div id='app'> <button v-on:click='Show1'> </button> ...

  2. [开源]React/Vue通用的状态管理框架,不好用你来打我👀

    为了防止被打,有请"燕双鹰"镇楼️‍♀️️‍️‍...o... 话说新冠3年,"状态管理框架"豪杰并起.群雄逐鹿,ReduxToolkit.Mobx.Vuex. ...

  3. 将 Docker Engine 节点从 dockershim 迁移到 cri-dockerd

    官网地址:https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/migrating-from-dockershim/migrate-doc ...

  4. 部署一个生产级别的 Kubernetes 应用(以Wordpress为例)

    文章转载自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247487811&idx=1&sn=67b39b73 ...

  5. 在CentO7系统上配置Springboot项目jar包开机自启动

    官方文档地址:https://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html#deployment-ins ...

  6. Elasticsearch:定制分词器(analyzer)及相关性

    转载自:https://elasticstack.blog.csdn.net/article/details/114278163 在许多的情况下,我们使用现有的分词器已经足够满足我们许多的业务需求,但 ...

  7. SECS半导体设备通讯-4 GEM通信标准

    一 概述 GEM标准定义了通信链路上的半导体设备的行为. SECS-II标准定义了在主机和设备之间交换的消息和相关数据项.GEM标准则定义了在哪种情况下应该使用哪些SECS-II消息以及由此产生的结果 ...

  8. 一个 dubbo 和 springboot 的兼容性问题

    背景介绍 最近把dubbo的版本从2.7.3升级到2.7.15时,遇到一个报错 No application config found or it's not a valid config! ,对应的 ...

  9. .NET平台下一个你不知道的框架,我只想说两个字:“牛逼”

    框架内容 零度框架是一套基于微服务和领域模型驱动设计的企业级快速开发框架,基于微软 .NET 6 + React 最新技术栈构建,容器化微服务最佳实践,零度框架的搭建以开发简单,多屏体验,前后端分离, ...

  10. 什么是Scrum?Scrum的核心要点和精髓

    有点长,期望你能通过本文彻底了解 Scrum. 上一篇文章<研发效能组织能力建设之特性团队FeatureTeam(上)>,我们介绍了一个非常有意思且高效的组织模式-特性团队.我们首先介绍了 ...