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
的情况下从线程发送消息?
看来我终于成功了!
为了将数据从线程传回服务,您需要这样做:
Subclass a Handler
class 在您的服务中(例如称它为 LocalHandler
)。你必须让它静态。覆盖一个handleMessage
方法,它将接收来自线程的消息。
向您的 Thread
构造函数添加一个 Handler
参数。在服务中实例化您的 LocalHandler
class 并通过构造函数将其注入您的线程。
保存对线程内 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
我创建了一个服务 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
的情况下从线程发送消息?
看来我终于成功了!
为了将数据从线程传回服务,您需要这样做:
Subclass a
Handler
class 在您的服务中(例如称它为LocalHandler
)。你必须让它静态。覆盖一个handleMessage
方法,它将接收来自线程的消息。向您的
Thread
构造函数添加一个Handler
参数。在服务中实例化您的LocalHandler
class 并通过构造函数将其注入您的线程。保存对线程内
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