Android:使用 Messengers for IPC 实现同步/阻塞 API
Posted
技术标签:
【中文标题】Android:使用 Messengers for IPC 实现同步/阻塞 API【英文标题】:Android: Implementing a synchronous/blocking API using Messengers for IPC 【发布时间】:2014-09-11 14:59:55 【问题描述】:我有一个后台服务,它使用
在自己的单独进程中运行android:process=":deamon"
在服务的清单条目中。我想通过我的活动与服务(远程进程)通信并从中接收数据。 我通过在http://developer.android.com/guide/components/bound-services.html#Messenger 中描述的向远程进程发送消息和从远程进程发送消息来做到这一点,正如他们建议的那样,我遵循了
如果您希望服务响应,那么您还需要在客户端中创建一个 Messenger。 > 然后当客户端收到 onServiceConnected() 回调时,它会向服务发送消息,在 send() 方法的 replyTo 参数中包含客户端的 Messenger。
问题是,我需要提供一个阻塞/同步 API 来从我的远程服务获取数据,我的“get”函数如何阻止调用者,然后返回在我传入的 Handler 中接收到的数据? 最好的方法是什么?
【问题讨论】:
你应该试试AIDL,你定义方法然后调用,一切都是同步的,定义协议不再麻烦developer.android.com/guide/components/aidl.html 我对aidl有过“反”的感觉,但现在我来看看 谢谢,这显然是一个更好的方法 【参考方案1】:这是客户端消息传递部分的代码
SparseArray<CountDownLatch> lockArray = new SparseArray<>();
SparseArray<Bundle> msgDataArray = new SparseArray<>();
public Bundle sendAndWaitResponse(Message msg) throws
RemoteException, InterruptedException
int msgId = msg.arg2;
Log.d("PlatformConnector", "Sending message to service, Type: "
+ msg.what + ", msgId: " + msg.arg2);
CountDownLatch latch = new CountDownLatch(1);
lockArray.put(msgId, latch);
platformMessenger.send(msg);
latch.await();
Bundle response = msgDataArray.get(msgId);
lockArray.delete(msgId);
msgDataArray.delete(msgId);
return response;
void storeResponseAndNotify(Message msg)
int msgId = msg.arg2;
// Because the message itself is recycled after Handler returns,
// we should store only the data of message
msgDataArray.put(msgId, msg.getData());
lockArray.get(msgId).countDown();
private class ClientMessageHandler extends Handler
@Override
public void handleMessage(Message msg)
storeResponseAndNotify(msg);
这是使用上述代码的示例。
RandomInt.getNextInt()
是我自定义的静态方法,用Random.nextInt()
生成随机整数。
public JSONObject doSomething(JSONObject object)
Message msg = Message.obtain(null, Constants.MESSAGE_SOMETHING, 0, RandomInt.getNextInt());
Bundle bundle = new Bundle();
bundle.putString(Constants.MESSAGE_DATA_SOMETHING, object.toString());
msg.setData(bundle);
try
Bundle responseData = sendAndWaitResponse(msg);
return new JSONObject(responseData.getString(Constants.MESSAGE_DATA_RETURN));
catch (RemoteException e)
Log.e(TAG, "Failed to send message to platform");
e.printStackTrace();
catch (InterruptedException e)
Log.e(TAG, "Interrupted while waiting message from platform");
e.printStackTrace();
catch (JSONException e)
e.printStackTrace();
return null;
顺序如下,
-
客户端准备
Message
并将其arg2
设置为某个随机整数
(这个整数将是同步的消息 ID)。
客户准备新的CountDownLatch
并把它放到LockArray
。
客户端用sendAndWaitResponse()
发送消息。它通过Messenger
向服务发送消息并调用latch.await()
。
服务进程接收消息并准备回复消息。此回复消息的arg2
应与收到的消息相同。
服务通过Messenger
在replyTo中向客户端发送回复消息。
客户端消息处理程序使用storeResponseAndNotify
处理消息。
当Client线程的阻塞结束时,响应数据已经准备好在msgDataArray
中了。
CountDownLatch
是阻塞和解除阻塞线程的简单开关。
(http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html)
SparseArray
类似于HashMap
,但对于较小的集合更节省内存。
(http://developer.android.com/reference/android/util/SparseArray.html)
注意不要阻塞Messenger
的线程。 Messenger
在单线程中运行,如果你从 handleMessage()
中阻塞,它将阻塞所有其他消息并导致死锁问题。
【讨论】:
好一个。也许您可以避免等待答案的稀疏消息数组,因为文档说服务信使收到的消息是排队的;您可以更简单地设置一个同步的响应队列。 “[Messenger] 是执行进程间通信 (IPC) 的最简单方法,因为 Messenger 将所有请求排队到单个线程中,因此您不必将服务设计为线程安全的。” developer.android.com/guide/components/bound-services.html以上是关于Android:使用 Messengers for IPC 实现同步/阻塞 API的主要内容,如果未能解决你的问题,请参考以下文章
Android开发:《Gradle Recipes for Android》阅读笔记(翻译)4.5——使用Android Libraries