1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 多线程通信---Handler机制

多线程通信---Handler机制

时间:2023-02-09 02:52:46

相关推荐

多线程通信---Handler机制

目录

作者简介:

1. 概述:

1.1 如何把子线程中计算的结果传给主线程?

2.handler异步通信系统

2.1 多线程通信过程

3.代码演示

3.1 子线程部分

3.2 主线程handler部分

3.3 MainActivity代码

3.4 布局代码

4.效果演示

5.拓展

作者简介:

👨‍🎓一位20级的计科专业的新手,请各位大佬多多指教

🏡个人主页:XiaoChen_Android

📚学习专栏:Android专栏

🕒发布日期:/8/7

1. 概述:

进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。

线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

为什么要用多线程:由于Android的主线程不能执行耗时操作,如果要进行耗时操作那么必须放在子线程当中,并且子线程中只能进行一些耗时操作,不能去操作我们的UI,更新界面等操作必须在主线程中进行。

1.1 如何把子线程中计算的结果传给主线程?

本文将介绍handler机制来实现多线程中的通信。

2.handler异步通信系统

Handler机制主要的几个角色:Handler、Message、Looper、MessageQueue(主要用到前两个)主线程在一开始就建立了这么一套系统

流程:

handler通过SendMessage()方法发送一个消息到消息队列里面(注意不是直接发送给主线程);Looper则充当从MessageQueue中循环拿消息,它不停的在循环,只要队列里还有消息它就会不断地从里面拿,拿出来之后把这个消息又传给了handler;handler收到之后就会调用handleMessage方法来处理消息

可能会有这个疑问:消息循环一圈又回到了原点,那么为什么不直接发送给handler呢?

可以这么解释:主线程一直在往下运行的过程中,子线程不知道主线程什么时间点有空去处理消息,主线程只能不停的去看有没有消息,它就只能去消息队列里看,而Looper的作用就是帮主线程拿新的消息,这样就不会耽误主线程的运行。

打个比方:消息队列相当于我们家里的邮箱,每条消息相当于邮件,我们就是Looper,邮递员就相当于子线程,因为我们不知道什么时候会有邮件,所以我们只有每天去邮箱里查看有没有新邮件。我们需要一个中转站----MessageQueue。

所以说这个系统很重要,但是我们其实感受不到这个系统,因为Android底层已经帮我们建立好了,在Activity创建时甚至创建之前就已经建立好了,我们只需要使用handler就好了

2.1 多线程通信过程

了解了上面的系统之后,这个流程也就容易理解了

S1:子线程首先拿到主线程的handler

S2:用主线程的handler给主线程发消息---调用SendMessage

S3:主线程通过Looper拿到消息然后调用handleMessage就可以处理消息了

3.代码演示

3.1 子线程部分

//这里主线程只执行了三行代码,其余的都是子线程执行public void onStart(View view){//做耗时操作//创建一个子线程new Thread(new Runnable() { //这以后主线程会执行@Overridepublic void run() { //这里的方法只有子线程才会执行,主线程不会管 子线程决定什么时候执行String str = getStringFromNet();//发送消息Message message = new Message();message.what = 0; //标识//这里传递的是一个Stirng类型的 所以类型要用obj的message.obj = str;mhandler.sendMessage(message);}})//子线程执行到该处就已经结束.start(); //主线程会执行Toast.makeText(this,"任务成功!",Toast.LENGTH_SHORT).show(); //这一行主线程也会执行,并且不需要等待}

3.2 主线程handler部分

private Handler mhandler = new Handler(Looper.myLooper()){ //这里需要传一个参数Looper.myLooper()否则会有内存泄漏警告 这里必须传主线程自己的looper@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);//handler收到消息执行此处if(msg.what == 0){ //这里就好理解what的作用了String data = msg.toString();textView.setText(data);Toast.makeText(MainActivity.this,"主线程已经收到消息!",Toast.LENGTH_SHORT).show();}}};

3.3 MainActivity代码

package com.example.handler;import androidx.annotation.NonNull;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends AppCompatActivity {private TextView textView;private Button button;private Handler mhandler = new Handler(Looper.myLooper()){ //这里需要传一个参数Looper.myLooper()否则会有内存泄漏警告 这里必须传主线程自己的looper@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);//handler收到消息执行此处if(msg.what == 0){String data = msg.toString();textView.setText(data);Toast.makeText(MainActivity.this,"主线程已经收到消息!",Toast.LENGTH_SHORT).show();}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = findViewById(R.id.tv_content);button = findViewById(R.id.click);}//这里主线程只执行了三行代码,其余的都是子线程执行public void onStart(View view){//做耗时操作//创建一个子线程new Thread(new Runnable() { //这以后主线程会执行@Overridepublic void run() { //这里的方法只有子线程才会执行,主线程不会管 子线程决定什么时候执行String str = getStringFromNet();//发送消息Message message = new Message();message.what = 0; //标识//这里传递的是一个Stirng类型的 所以类型要用obj的message.obj = str;mhandler.sendMessage(message);}})//子线程执行到该处就已经结束.start(); //主线程会执行Toast.makeText(this,"任务成功!",Toast.LENGTH_SHORT).show(); //这一行主线程也会执行,并且不需要等待}//为了方便就写死了 如果要网络请求也是在此处写private String getStringFromNet() {// 假装从网络获取了一个字符串String result = "";StringBuilder stringBuilder = new StringBuilder();// 模拟一个耗时操作for (int i = 0; i < 100; i++) {stringBuilder.append("字符串" + i); //拼接一百个字符串}try {Thread.sleep(5000); //让操作慢下来} catch (InterruptedException e) {e.printStackTrace();}result = stringBuilder.toString();return result;}}

3.4 布局代码

<?xml version="1.0" encoding="utf-8"?><ScrollView xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginRight="10dp"android:orientation="vertical"android:paddingLeft="10dp"><TextViewandroid:id="@+id/tv_content"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="结果"/><Buttonandroid:id="@+id/click"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:text="开始"android:onClick="onStart"android:textAllCaps="false" /></LinearLayout></ScrollView>

4.效果演示

S1:点击开始后会执行一个耗时任务,这里我设置了耗时5秒,但是立马会执行onStart方法,且Toast会立马显示,因为主线程执行的操作不耗时;

S2:经过5秒之后,主线程会收到子线程发出的消息,然后处理消息把文本设置,同时会弹出Toast显示收到消息,到这里通信就已经成功完成了!

5.拓展

进行异步请求还有其他的方式,比如比较流行的是AsyncTask,它是一种轻量级的异步任务,可以在线程池中执行后台任务,然后再把执行的进度和结果传递给主线程并在主线程中更新UI。详细介绍:AsyncTask使用及解析。

推荐一本书:Android开发艺术探索,实践和看书结合效果更好

如果文章对你有帮助就支持一下噢,新手尝试,不好的地方请各位大佬多多指教!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。