【问题标题】:Android: how to communicate from worker thread to a serviceAndroid:如何从工作线程到服务通信
【发布时间】:2023-04-08 16:08:01
【问题描述】:

我创建了一个服务类和一个工作类,它们在单独的线程中执行。我想在它们之间建立通信,以便工作人员可以将一些状态发送回服务。

我尝试将我的工作人员 Thread 转换为 HandlerThread 并在服务端设置一个 Handler,但是我不知道如何实际从工作人员发送消息。好像没搞懂这个概念。

这是我的课程(没有通信逻辑):

服务类

package com.example.app;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;


public class ConnectionService extends Service {

    protected ConnectionWorker thread;

    @Override
    public void onCreate() {

        super.onCreate();

        // Creating a connection worker thread instance.
        // Not starting it yet.
        this.thread = new ConnectionWorker();

        Log.d(this.getClass().getName(), "Service created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.d(this.getClass().getName(), "Service started");

        // Checking if worker thread is already running.
        if (!this.thread.isAlive()) {

            Log.d(this.getClass().getName(), "Starting working thread");

            // Starting the worker thread.
            this.thread.start();
        }

        return Service.START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {

        Log.d(this.getClass().getName(), "Stopping thread");

        // Stopping the thread.
        this.thread.interrupt();

        Log.d(this.getClass().getName(), "Stopping service");

        super.onDestroy();

        Log.d(this.getClass().getName(), "Service destroyed");
    }
}

工人阶级

package com.example.app;

import android.os.SystemClock;
import android.util.Log;


public class ConnectionWorker extends Thread {

    public ConnectionWorker() {
        super(ConnectionWorker.class.getName());
    }

    @Override
    public void run() {

        super.run();

        Log.d(this.getClass().getName(), "Thread started");

        // Doing the work indefinitely.
        while (true) {

            if (this.isInterrupted()) {
                // Terminating this method when thread is interrupted.
                return;
            }

            // @todo: send a message to the service

            Log.d(this.getClass().getName(), "Doing some work for 3 seconds...");
            SystemClock.sleep(3 * 1000);
        }
    }
}

如何实现HandlerThread,从工作线程发送消息并在我的服务中接收它们?

我将非常感谢一个代码示例。

更新

实际上,我似乎不能在我的线程内使用Looper,因为它会阻塞线程执行,我不希望这样。是否可以在不使用Looper的情况下从线程发送消息?

【问题讨论】:

  • 能否请您提示您希望如何处理“在您的服务中”收到的消息。严格来说你不能向你的服务发送消息,你可以向一个线程循环器发送消息,如果你的服务这将是主线程循环器。
  • @Okas,感谢您的回复。我将在其中一个片段中显示来自工作人员的一些状态信息。
  • 那么您不想向服务发送消息。你要做的是从后台线程更新 ui,事实上这个线程是由服务启动的并不重要。看看这个例子developer.android.com/training/multiple-threads/…
  • 我不知道你是否知道,但是服务在你的活动、片段、广播接收器、视图运行的线程上运行,所以如果你想更新一些东西,请在处理程序上使用 post(runnable)并直接在那里调用您的更新,它对您来说足够食用吗?
  • 感谢您的建议@Elitz。我只是很难弄清楚如何正确实现这个HandlerThread。据我了解,Handler 应该在接收方,就我而言,在服务中。我可以在那里实现它,但我不确定如何实际将消息从线程发送到它。线程与服务通信的示例将非常有帮助。

标签: java android multithreading android-service


【解决方案1】:

看来我终于成功了!

为了将数据从线程传递回服务,您需要这样做:

  1. 在您的服务中子类化 Handler 类(例如称为 LocalHandler)。您必须将其设为静态。重写一个handleMessage 方法,它将接收来自线程的消息。

  2. Handler 参数添加到Thread 构造函数。在服务中实例化您的LocalHandler 类并通过构造函数将其注入您的线程。

  3. 保存对Handler 的引用,并在适当的时候使用它发送消息。

这是完整的例子:

服务类

package com.example.app;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;


public class ConnectionService extends Service {

    protected ConnectionWorker thread;

    static class LocalHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Log.d(this.getClass().getName(), "Message received: " + (String) msg.obj);
        }
    }
    protected Handler handler;


    @Override
    public void onCreate() {

        super.onCreate();

        // Instantiating overloaded handler.
        this.handler = new LocalHandler();

        // Creating a connection worker thread instance.
        // Not starting it yet.
        // Injecting our handler.
        this.thread = new ConnectionWorker(this.handler);

        Log.d(this.getClass().getName(), "Service created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.d(this.getClass().getName(), "Trying to start the service");

        // Checking if worker thread is already running.
        if (!this.thread.isAlive()) {

            Log.d(this.getClass().getName(), "Starting working thread");

            // Starting the worker thread.
            this.thread.start();

            Log.d(this.getClass().getName(), "Service started");
        }

        return Service.START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {

        Log.d(this.getClass().getName(), "Stopping thread");

        // Stopping the thread.
        this.thread.interrupt();

        Log.d(this.getClass().getName(), "Stopping service");

        super.onDestroy();

        Log.d(this.getClass().getName(), "Service destroyed");
    }
}

工人类(线程)

package com.example.app;

import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;


public class ConnectionWorker extends Thread {

    // Reference to service's handler.
    protected Handler handler;

    public ConnectionWorker(Handler handler) {
        super(ConnectionWorker.class.getName());

        // Saving injected reference.
        this.handler = handler;
    }

    @Override
    public void run() {

        super.run();

        Log.d(this.getClass().getName(), "Thread started");

        // Doing the work indefinitely.
        while (true) {

            if (this.isInterrupted()) {
                // Terminating this method when thread is interrupted.
                return;
            }

            Log.d(this.getClass().getName(), "Doing some work for 3 seconds...");

            // Sending a message back to the service via handler.
            Message message = this.handler.obtainMessage();
            message.obj = "Waiting for 3 seconds";
            this.handler.sendMessage(message);

            SystemClock.sleep(3 * 1000);
        }
    }
}

我希望这是一个有效的实现。如果您现在有更好的方法 - 请告诉我。

【讨论】:

  • 这行得通。无论如何,处理程序应该是静态的,只是在我匆忙中忘记了这一点。正如您的问题 getExtraType 是您想要的任何类型(int、string 等)一样,要为您的线程获取上下文,您需要将其作为参数传递给线程构造函数。很高兴你成功了。
  • 感谢您的宝贵时间,您的回答真的很有帮助 = )
  • 很高兴听到这个消息。要注意的另一件事是您可以删除 SystemClock 睡眠调用并执行 handler.postDelayed(this, 3000);这将等待 3 秒,然后执行将消息发送到处理程序的线程,而无需中断系统
  • 谢谢!我需要使用postDelayed 而不是while true 循环吗?如果我删除循环,它不会终止线程吗?
  • while 循环是您希望无限循环然后在明确告知时停止的线程的一种做法。如果你想执行单个任务,你应该查看 AsyncTask 类的实现。
【解决方案2】:

Slava,实际上,你的答案是行不通的。让我解释一下原因。

为什么 Thread 已经存在,Android 会有 HandlerThread?

这是因为正如documentation 所说,HandleThread 是一个

用于启动具有弯针的新线程的便捷类。弯针 然后可以用于创建处理程序类。注意 start() 必须 仍然被调用。

Looper是如何用来创建Handler的?

应该在 Handler 的构造函数中传递一个 Looper 来告诉 Handler 它正在使用哪个 Looper。

所以每个 HandlerThread 都有一个 Looper,并且可以有多个 Handler 处理 Looper 循环通过的消息。


当您使用默认构造函数在 Service 类中创建新的处理程序时,处理程序会与当前的 Looper 相关联,如其文档中所述。

处理程序()

默认构造函数将此处理程序与 Looper 相关联 当前线程。如果这个线程没有looper,这个handler 将无法接收消息,因此引发异常。

您可以通过打印来确认问题仍然存在

Thread.currentThread().getId()

在适当的位置 - 在服务的 onCreate() 内、处理程序的 handleMessage() 内和 ConnectionWorker 的 run() 内。那怎么解决你的问题呢?

您可以创建一个 HandlerThread,甚至在服务内,当 Looper 准备好时,使用该 Looper 创建 Handler。

new HandlerThread("ConnectionWorker") {
    @Override
    protected void onLooperPrepared() {
        new Handler(getLooper()).post(new Runnable() {
            @Override
            public void run() {
                // do your stuff

                getLooper().quitSafely();
            }
        });
    }
}.start();

希望答案还不算太晚:P

【讨论】:

  • 感谢您的详尽回答,但我很久以前只是在尝试使用 Android,现在还没有使用它。因此,我无法真正验证或完全理解您的答案。无论如何,谢谢你,我希望它会帮助那里的人;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-29
  • 2013-07-21
  • 1970-01-01
  • 2013-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多