Android: 如何从工作线程与服务通信

Android: how to communicate from worker thread to a service

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

我尝试将我的工作人员 Thread 转换为 HandlerThread 并在服务端设置 Handler,但我不知道如何实际发送消息从工人。看来我没法理解这个概念。

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

服务class

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");
    }
}

工人class

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 的情况下从线程发送消息?

看来我终于成功了!

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

  1. Subclass a Handler class 在您的服务中(例如称它为 LocalHandler)。你必须让它静态。覆盖一个handleMessage方法,它将接收来自线程的消息。

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

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

完整示例如下:

服务Class

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");
    }
}

工人Class(线程)

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);
        }
    }
}

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

Slava,其实你的回答是行不通的。让我解释一下为什么。

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

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

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

Looper如何用来创建Handler?

应在 Handler 的构造函数中传递一个 Looper,以告知 Handler 它正在使用哪个 Looper。

因此每个 HandlerThread 都有一个 Looper,并且可以有许多 Handlers 处理 Looper 循环通过的消息。


当您使用默认构造函数在您的服务 class 中创建新的处理程序时,该处理程序会关联到当前的 Looper,如其文档中所述。

Handler ()

Default constructor associates this handler with the Looper for the current thread. If this thread does not have a looper, this handler won't be able to receive messages so an exception is thrown.

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

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