当前位置:首页 > 技术 > 正文内容

Android中主线程与子线程双向通信实现

访客 技术 2026年6月16日 1

在Android开发中,线程间通信是常见的需求。虽然大多数场景下我们关注的是子线程完成任务后通知主线程更新UI,但有时也需要从主线程向子线程传递指令,比如触发后台数据同步、配置刷新或任务调度等操作。本文将介绍如何使用Handler和Looper机制实现主线程与子线程之间的双向消息传递。

为了在子线程中接收消息,必须为其创建独立的Looper,这样才能保证Handler能够正常处理消息队列。Android提供了HandlerThread类来简化这一过程,它是一个带有Looper的线程,非常适合用于执行长时间运行的后台任务。

实现步骤

  1. 在主线程中定义一个Handler用于发送消息到子线程;
  2. 创建并启动HandlerThread,并获取其Looper;
  3. <3>基于该Looper创建子线程的Handler,用于接收和处理来自主线程的消息;
  4. 通过按钮或其他用户交互触发消息发送。

代码示例

public class CommunicationActivity extends AppCompatActivity implements View.OnClickListener {

    private Button startBtn, stopBtn;
    private Handler mainHandler;
    private Handler backgroundHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_communication);

        mainHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                Log.d("MainHandler", "Running on thread: " + Thread.currentThread().getName());
                Message nextMsg = Message.obtain();
                backgroundHandler.sendMessageDelayed(nextMsg, 1000); // 每隔1秒发消息给子线程
            }
        };

        setupBackgroundThread();

        findViews();
    }

    private void setupBackgroundThread() {
        HandlerThread workerThread = new HandlerThread("WorkerThread");
        workerThread.start();

        backgroundHandler = new Handler(workerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                Log.d("BackgroundHandler", "Received message on thread: " + Thread.currentThread().getName());
                // 模拟处理耗时任务
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                // 回送消息给主线程
                mainHandler.sendEmptyMessage(0);
            }
        };
    }

    private void findViews() {
        startBtn = findViewById(R.id.btn_start);
        stopBtn = findViewById(R.id.btn_stop);
        startBtn.setOnClickListener(this);
        stopBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_start) {
            mainHandler.sendEmptyMessage(1);
        } else if (v.getId() == R.id.btn_stop) {
            mainHandler.removeMessages(1);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (backgroundHandler != null) {
            backgroundHandler.getLooper().quitSafely();
        }
    }
}

布局文件(activity_communication.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Sending" />

    <Button
        android:id="@+id/btn_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop Sending" />

</LinearLayout>

上述代码展示了如何建立两个方向的消息通道:主线程通过点击"Start"按钮开始周期性地向子线程发送消息,而子线程在接收到消息后进行简单处理,并反向发送消息回主线程,形成闭环通信。同时,在页面销毁时正确退出子线程的Looper,避免内存泄漏。

标签: Android

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。