Android 面试题 线程间通信 六

🔥 主线程向子线程发送消息 Thread+handler🔥

子线程中定义Handler,Handler定义在哪个线程中,就跟那个线程绑定,在线程中绑定Handler需要调用Looper.prepare(); 方法,主线程中不调用是因为主线程默认帮你调用了 : 

 

public class LoopThread implements Runnable {  public Handler mHandler = null;  @Override  public void run() {  Looper.prepare();  mHandler = new Handler() {  public void handleMessage(Message msg) {  String result = NetUtil.getJsonContent("北京");  //完成了获取北京天气的操作;  Log.i("test", "handler"+result);  }  };  Looper.loop();  }  } 

其中Looper.prepare();和Looper.loop();维护了一个消息队列,等待消息注入并在子线程中执行;

主线程中这样调用

lThread.mHandler.sendEmptyMessage(0);   

主线程向子线程发消息,让子线程执行指定的操作,在Android中还有一种方法,即:HandlerThread,看下面的例子:

HandlerThread handlerThread = new HandlerThread("jerome");  
handlerThread.start();  /** * 这里要将HandlerThread创建的looper传递给threadHandler,即完成绑定; */  
threadHandler = new Handler(handlerThread.getLooper()) {  @Override  public void handleMessage(Message msg) {  super.handleMessage(msg);  switch (msg.what) {  case 0:  
这儿可以做耗时的操作;  Log.i("jerome", "hello,I am sub thread");  break;  default:  break;  }  }  
};  

🔥 子线程向主线程发送消息 Thread+handler 🔥 

主线程中定义Handler: 

Handler mHandler = new Handler(){  @Override  public void handleMessage(Message msg) {  super.handleMessage(msg);  switch (msg.what) {  case 0:  //do something,refresh UI;  break;  default:  break;  }  }  };

子线程处理完耗时操作之后发消息给主线程,更新UI: 

mHandler.sendEmptyMessage(0);  

这样在子线程与主线程任务分工的条件下完成了消息交互;

🔥 子线程向子线程发送消息 🔥 

创建线程 ThreadA 用来接受线程 ThreadB 传递的消息

class ThreadA implements Runnable{private Handler mHandler;//run运行后才不为null在main里判断public Handler getHandler(){return mHandler;}@SuppressLint("HandlerLeak")@Overridepublic void run() {Looper.prepare();mHandler=new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what){case 1:Log.e("线程A","线程B发过来消息了--"+msg.obj);break;}}};Looper.loop();}}

创建线程 ThreadB 发送消息到线程 ThreadA 中 

class ThreadB implements Runnable{@Overridepublic void run() {Message mess=Message.obtain();mess.what=1;mess.obj= "线程B"+System.currentTimeMillis();handler.sendMessage(mess);}}

在activity onCreate方法里 , 分别调用线程 ThreadA 和 ThreadB

        ThreadA threadA = new ThreadA();ThreadB threadb = new ThreadB();new Thread(threadA).start();if(threadA.getHandler() == null) {try {Thread.sleep(1000);handler = threadA.getHandler();} catch (InterruptedException e) {e.printStackTrace();}}new Thread(threadb).start();

🔥 runOnUiTherd() 🔥

主线程主要来完成UI绘制和响应用户的操作 , 大多数情况都习惯在 onCreate()、onResume()、onCreateView() 中启动我们的逻辑 ,  导致代码运行在主线程中,容易导致ANR(Application Not Responding),这些逻辑包括文件读写, 数据库读写, 网络查询等。

开启一个子线程来完成一个耗时操作,以避免阻塞主线程而出现卡顿甚至ANR导致闪退。

子线程执行完要更新UI的时候,我们又必须回到主线程来更新,实现这一功能常用的方法是执行
Activity的runOnUiThread() 方法:

runOnUiThread(new Runnable() {void run() {// do something}
});

Fragment/Presentation/Dialog中使用:

((MainActivity)getActivity()).runOnUiThread(new Runnable() {@Overridepublic void run() {//在此进行更新UI的操作}});

深入理解runOnUiThread() 

 🔥 AsyncTask 🔥

一个 Android 已封装好的轻量级异步类。属于抽象类,即使用时需实现子类。

public abstract class AsyncTask<Params, Progress, Result> { ... }

作用 

  • 实现多线程:在工作线程中执行任务,如 耗时任务
  • 异步通信、消息传递:实现工作线程 & 主线程(UI线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作,保证线程安全。

 优点

  • 方便实现异步通信
    不需使用 “任务线程(如继承Thread类) + Handler”的复杂组合
  • 节省资源
    采用线程池的缓存线程 + 复用线程,避免了频繁创建 & 销毁线程所带来的系统资源开销

类定义

public abstract class AsyncTask<Params, Progress, Result> { ... 
}// 类中参数为3种泛型类型
// 整体作用:控制AsyncTask子类执行线程任务时各个阶段的返回类型
// 具体说明:// a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数// b. Progress:异步任务执行过程中,返回下载进度值的类型// c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致
// 注:// a. 使用时并不是所有类型都被使用// b. 若无被使用,可用java.lang.Void类型代替// c. 若有不同业务,需额外再写1个AsyncTask的子类
}

核心方法  execute()

作用 : 触发执行异步线程任务

调用时刻 : 手动调用

使用场景 : 必须在UI线程调用 ,  运行在主线程

核心方法  onPreExecute()

作用 : 执行 线程任务前的任务操作

调用时刻 : 执行 线程任务前 自动调用 , 即 execute() 执行前调用

使用场景 : 用于界面的初始化操作 , 如 : 显示进度条的对话框 

核心方法  doInBackground()

作用 : 接收输入参数 , 执行任务中的耗时操作 , 返回任务中执行的结果 

调用时刻 : 执行 线程任务时 自动调用 , 即 onPreExecute() 执行后 自动调用

使用场景 :  不能更改UI组件的信息 , 执行过程中可以调用 publishProgress() 更新进度信息

核心方法  onProgressUpdate()

作用 : 在主线程中显示线程任务执行的进度

调用时刻 : 调用publishProgress(Progress ... values)  时 自动调用

核心方法  onPostExecute()

作用 : 接受线程任务执行的结果 ,  将执行结果显示到UI组件上

调用时刻 : 线程任务结束时 自动调用

 核心方法  onCancelled()

作用 : 将异步任务设置为取消状态

调用时刻 : 异步任务被取消时  即自动调用

使用场景 : 该方法调用时 onPostExecute() 就不会被调用

 🔥 View.post 🔥 

View.post方法可以在UI线程上安排一个Runnable,确保在某些情况下,如视图大小改变后,执行特定操作。然而,使用View.post时,可能会遇到一些问题。以下是一些可能遇到的坑及其解决方法:

生命周期问题:在使用View.post时,如果Activity或Fragment的生命周期发生变化,如onDestroy或onDetach,可能导致内存泄漏。解决方法是在生命周期方法中移除所有已post的Runnable。

@Override
protected void onDestroy() {super.onDestroy();yourView.removeCallbacks(yourRunnable);
}
  1. 延迟执行:View.post可能会在稍后执行Runnable,这意味着如果您希望立即执行操作,可能会遇到问题。解决方法是使用View.post()之外的其他方法,如在onLayout或onSizeChanged中执行操作。
  2. 视图不可见:如果使用View.post时视图不可见(例如,它已被隐藏或从窗口分离),Runnable可能无法执行。解决方法是在执行操作前检查视图的可见性和附加状态。
  3. 多次执行:如果使用View.post多次调度相同的Runnable,可能会导致多次执行。为避免这种情况,请在调度新Runnable之前移除旧的Runnable。
  4. 视图未初始化:View.post在视图初始化之前可能无法正常工作。解决方法是确保在视图完全初始化之后再调用View.post。

总之,在使用View.post时要注意生命周期问题、视图可见性、多次执行以及视图初始化等潜在问题。了解这些问题并采取相应的解决方法,可以帮助您更有效地使用View.post

view.post()主要有两个作用:更新UI、获取view的实际宽高。 

🔥 View.postDelayed() 🔥 

UI控件延迟显示 View.postDelayed()  

@SuppressLint("NewApi")
public class TestFragment extends DialogFragment {private Button mButton;//默认初始值为trueprivate boolean isSucess = true;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//NetworkUtil.requestNet是开启了一个异步线程做请求任务//执行网络请求,根据返回成功与否(true or false) 来设定 button上的文字//如下为 伪代码isSucess = NetworkUtil.requestNet();}@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {super.onCreateView(inflater, container, savedInstanceState);View viewLayout = inflater.inflate(R.layout.fragment_test, container);mButton = viewLayout.findViewById(R.id.button2);return viewLayout;}@Overridepublic void onResume() {super.onResume();//存在bug的代码/* if (isSucess) {mButton.setText("返回结果成功");} else {mButton.setText("返回结果失败");}*///可以用 View.postDelayed(Runnable action, long delayMillis)方法来解决此问题mButton.postDelayed(new Runnable() {@Overridepublic void run() {if (isSucess) {mButton.setText("返回结果成功");} else {mButton.setText("返回结果失败");}}}, 1000); //这里延时时间根据网络环境的好坏设置}
}

 自定义view中,postDelayed执行失败

private boolean mAttached;@Override
protected void onAttachedToWindow() {super.onAttachedToWindow();  mAttached = true;
}@Override
protected void onDetachedFromWindow() {mAttached = false;super.onDetachedFromWindow();
}public void show() {//....if (mAttached) {postDelayed(mDelayedShow, MIN_DELAY);    } else {Handler handler = new Handler();handler.post(mDelayedShow);}
}

 🔥 线程间通信  eventbus 🔥

优点 : 

  • 简化组件之间的通信

  • 体积小

  • 将事件的发送者和接受者分离

  • 在activity fragment 线程之间性能优良

  • 避免复杂且容易出错的依赖关系和生命周期问题

  • 代码简单方便

粘性广播 

粘性广播有什么作用?怎么使用? 粘性广播主要为了解决,在发送完广播之后,动态注册的接收者,也能够收到广播。

举个例子首先发送一广播,我的接收者是通过程序中的某个按钮动态注册的。

如果不是粘性广播,我注册完接收者肯定无法收到广播了。

这是通过发送粘性广播就能够在我动态注册接收者后也能收到广播

EeventBus 粘性事件和普通事件的区别?

StickyEvent与普通Event的普通就在于,EventBus会自动维护被作为StickyEvent被post出来(即在发布事件时使用EventBus.getDefault().postSticky(new MyEvent())方法)的事件的最后一个副本在缓存中。

任何时候在任何一个订阅了该事件的订阅者中的任何地方(可以在任何函数中,而不仅仅是在onEventXXX方法中),都可以通过EventBus.getDefault().getStickyEvent(MyEvent.class)来取得该类型事件的最后一次缓存。

同时,即便事件已经在所有订阅者中传递完成了,如果此时再创建一个新的订阅者(如一个注册了该StickyEvent的Activity),则在订阅者启动后,会自动调用一次该订阅者的noEventXXX方法来处理该StickyEvent。也可以在需要的时候,利用removeStickyEvent方法来移除对某种StickyEvent的缓存。

Android Studio 配置的module的gradle内:

compile 'org.greenrobot:eventbus:3.0.0'

 定义一个消息事件 : 

public class User {private String name;private String pass;public User(String name, String pass) {this.name = name;this.pass = pass;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPass() {return pass;}public void setPass(String pass) {this.pass = pass;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", pass='" + pass + '\'' +'}';}
}

普通事件(a--->b---值-->a),相当于数据回传

 1、注册和取消订阅事件(PuTong1Activity .java):

public class PuTong1Activity extends AppCompatActivity implements View.OnClickListener {private Button putong1_btn;private TextView putong1Text;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pu_tong1);initView();//注册事件EventBus.getDefault().register(this);}private void initView() {putong1_btn = (Button) findViewById(R.id.putong1_btn);putong1Text = (TextView) findViewById(R.id.putong1Text);putong1_btn.setOnClickListener(this);}@Overridepublic void onClick(View v) {startActivity(new Intent(PuTong1Activity.this,PuTong2Activity.class));}//事件订阅者处理事件@Subscribe(threadMode = ThreadMode.MAIN)public void onMoonEvent(User user){putong1Text.setText(user.getName()+"---接收到的值----"+user.getPass());}@Overrideprotected void onDestroy() {super.onDestroy();//取消注册事件EventBus.getDefault().unregister(this);}
}

2、事件发布者发布事件PuTong2Activity 类 

public class PuTong2Activity extends AppCompatActivity implements View.OnClickListener {private Button putong2_btn;private EditText nameEdit;private EditText passEdit;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pu_tong2);initView();}private void initView() {putong2_btn = (Button) findViewById(R.id.putong2_btn);nameEdit = (EditText) findViewById(R.id.nameEdit);passEdit = (EditText) findViewById(R.id.passEdit);putong2_btn.setOnClickListener(this);}@Overridepublic void onClick(View v) {String nameStr = nameEdit.getText().toString().trim();String passStr = passEdit.getText().toString().trim();//普通时间发送消息给putong1用post方法EventBus.getDefault().post(new User(nameStr,passStr));finish();}
}

粘性事件(相当于直接从a—值—>b,与intent跳转传值类似)

 1.发送黏性事件(MainActivity .java)

public class MainActivity extends AppCompatActivity implements View.OnClickListener {private EditText nameEdit;private EditText passEdit;private Button login;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}private void initView() {nameEdit = (EditText) findViewById(R.id.name);passEdit = (EditText) findViewById(R.id.pass);login = (Button) findViewById(R.id.login);login.setOnClickListener(this);}@Overridepublic void onClick(View v) {String nameStr = nameEdit.getText().toString().trim();String passStr = passEdit.getText().toString().trim();if(null==nameStr&&nameStr.equals("")||null==passStr&&passStr.equals("")){Toast.makeText(this,"用户名密码不能为空",Toast.LENGTH_SHORT).show();}else{//2.发送消息粘性事件用postStickyEventBus.getDefault().postSticky(new User(nameStr,passStr));//跳转Intent intent = new Intent(MainActivity.this, ResultActivity.class);startActivity(intent);}}}

2.订阅粘性事件(ResultActivity.java)

public class ResultActivity extends AppCompatActivity implements View.OnClickListener {private Button getResult;private TextView text;private String nameStr;private String passStr;//在接收消息的页面  注册EventBus@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_result);initView();}private void initView() {getResult = (Button) findViewById(R.id.getResult);text = (TextView) findViewById(R.id.text);getResult.setOnClickListener(this);}@Overridepublic void onClick(View v) {//注册EventBusEventBus.getDefault().register(this);}//订阅者处理粘性事件@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)public void onEventMainThread(User user) {String msg = "账号:" + user.getName()+"---密码:"+user.getPass();Log.d("ResultActivity", msg);text.setText(msg);Toast.makeText(ResultActivity.this, msg, Toast.LENGTH_LONG).show();}@Overrideprotected void onDestroy() {super.onDestroy();//取消注册EventBus.getDefault().unregister(this);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/13540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么在线修改图片?分享一个图片修改工具

无论是在个人或商业领域&#xff0c;我们都需要使用高质量的图片来传达信息或提高品牌形象。大尺寸的图片也会占据大量的存储空间和带宽&#xff0c;影响网站的加载速度和用户体验。因此&#xff0c;我们需要一种高效的工具来解决这个问题。今天向大家介绍一款非常实用的图片处…

Abaqus 导出单元刚度矩阵和全局刚度矩阵

Abaqus 导出单元刚度矩阵和全局刚度矩阵 首次创建&#xff1a;2023.7.29 最后更新&#xff1a;2023.7.29 如有什么改进的地方&#xff0c;欢迎大家讨论&#xff01; 详细情况请查阅&#xff1a;Abaqus Analysis User’s Guide 一、Abaqus 导出单元刚度矩阵 1.生成单元刚度矩阵…

Linux CentOS快速安装VNC并开启服务

以下是在 CentOS 上安装并开启 VNC 服务的步骤&#xff1a; 安装 VNC 服务器软件包。运行以下命令&#xff1a; sudo yum install tigervnc-server 输出 $ sudo yum install tigervnc-server Loaded plugins: fastestmirror, langpacks Repository epel is missing name i…

教雅川学缠论04-笔

笔由3部分组成&#xff1a; 顶分型K线底分型&#xff0c;或者 底分型K线顶分型 注意&#xff1a;笔加一起至少7根K线&#xff0c;因为一个底分型至少3根&#xff0c;K先至少1个&#xff0c;顶分型至少3根 下图中红色线段就是一个标准的笔&#xff0c;它始于一个底分型&#xff…

Visual C++中的虚函数和纯虚函数(以外观设计模式为例)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天来说说Visual C中的虚函数和纯虚函数。该系列帖子全部使用我本人自创的对比学习法。也就是当C学不下去的时候&#xff0c;就用JAVA实现同样的代码&#xff0c;然后再用对比的方法把C学会。 直接说虚函数…

【SpringCloud Alibaba】(四)使用 Feign 实现服务调用的负载均衡

在上一文中&#xff0c;我们实现了服务的自动注册与发现功能。但是还存在一个很明显的问题&#xff1a;如果用户微服务和商品微服务在服务器上部署多份的话&#xff0c;之前的程序无法实现服务调用的负载均衡功能。 本文就带着大家一起实现服务调用的负载均衡功能 1. 负载均衡…

LayUi 树形组件tree 实现懒加载模式,展开父节点时异步加载子节点数据

如题。 效果图&#xff1a; //lazy属性为true&#xff0c;点开时才加载 引用代码&#xff1a; <link href"~/Content/layui-new/css/layui.css" rel"stylesheet" /><form id"form" class"layui-form" style"margin-to…

AC695-按键处理-带UI

AC695-按键修改 消息发出 对应界面处理

【算法和数据结构】257、LeetCode二叉树的所有路径

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;首先看这道题的输出结果&#xff0c;是前序遍历。然后需要找到从根节点到叶子节点的所有路径&#xff…

【图论】树上差分(点差分)

一.题目 输入样例&#xff1a; 5 10 3 4 1 5 4 2 5 4 5 4 5 4 3 5 4 3 4 3 1 3 3 5 5 4 1 5 3 4 输出样例&#xff1a;9 二 .分析 我们可以先建一棵树 但我们发现&#xff0c;这样会超时。 所以&#xff0c;我们想到树上差分 三.代码 /* 5 10 3 4 1 5 4 2 5 4 5 4 5 4 3 5 …

IOS UICollectionView 设置cell大小不生效问题

代码设置flowLayout.itemSize 单元格并没有改变布局大小&#xff0c; 解决办法如下图&#xff1a;把View flow layout 的estimate size 设置为None&#xff0c;上面设置的itemSize 生效了。

物联网阀控水表计量准确度如何?

物联网阀控水表是一种新型的智能水表&#xff0c;它采用了先进的物联网技术&#xff0c;可以通过远程控制和监测水表的运行情况&#xff0c;实现更加精准的水量计量和费用结算。那么&#xff0c;物联网阀控水表的计量准确度如何呢&#xff1f;下面我们将从以下几个方面进行详细…

PHP 3des加解密新旧方法可对接加密

一、旧3des加解密方法 <?php class Encrypt_3DES {//加密秘钥&#xff0c;private $_key;private $_iv;public function __construct($key, $iv){$this->_key $key;$this->_iv $iv;}/*** 对字符串进行3DES加密* param string 要加密的字符串* return mixed 加密成…

【SpringⅢ】Spring 的生命周期

目录 &#x1f96a;1 Bean 的作用域 &#x1f969;1.1 singleton&#xff1a;单例模式 &#x1f359;1.2 prototype&#xff1a;原型模式 &#x1f371;1.3 Bean 的其他作用域 &#x1f35c;2 Spring 生命周期(执行流程) &#x1f958;2.1 启动容器 &#x1f372; 2.2 读…

[小尘送书-第二期]《从零开始读懂量子力学》由浅入深,解释科学原理;从手机到超导,量子无处不在;从微观到宏观,遐想人生的意义!

大家好&#xff0c;我是小尘&#xff0c;欢迎关注&#xff0c;一起交流学习&#xff01;欢迎大家在CSDN后台私信我&#xff01;一起讨论学习&#xff0c;讨论如何找到满意的工作&#xff01; 本文目录 一、前言二、作者简介三、内容简介四、抽奖方式五、名家推介写在最后 一、前…

[containerd] 在Windows上使用IDEA远程调试containerd, ctr, containerd-shim

文章目录 1. containerd安装2. 源码编译3. 验证编译的二进制文件是否含有调试需要的信息3.1. objdump工具验证3.2. file工具验证3.3. dlv工具验证 4. debug 1. containerd安装 [Ubuntu 22.04] 安装containerd 2. 源码编译 主要步骤如下&#xff1a; 1、从github下载containe…

[Java] 单例设计模式详解

模式定义&#xff1a;保证一个类只有一个实例&#xff0c;并且提供一个全局访问点&#xff0c;时一种创建型模式 使用场景&#xff1a;重量级的对象&#xff0c;不需要多个实例&#xff0c;如线程池&#xff0c;数据库连接池 单例设计模式的实现 1.懒汉模式&#xff1a;延迟…

第一次后端复习整理(JVM、Redis、反射)

1. JVM 文章仅为自身笔记 详情查看一篇文章掌握整个JVM&#xff0c;JVM超详细解析&#xff01;&#xff01;&#xff01; 1.1 什么是JVM jvm是Java虚拟机 1.2 Java文件的编译过程 程序员编写代码形成.java文件经过javac编译成.class文件再通过JVM的类加载器进入运行时数据…

【141. 环形链表】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#x…

JVM | 基于类加载的一次完全实践

引言 我在上篇文章&#xff1a;JVM | 类加载是怎么工作的 中为你介绍了Java的类加载器及其工作原理。我们简单回顾下&#xff1a;我用一个易于理解的类比带你逐步理解了类加载的流程和主要角色&#xff1a;引导类加载器&#xff0c;扩展类加载器和应用类加载器。并带你深入了解…