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的主要内容,如果未能解决你的问题,请参考以下文章

iperf for android怎么使用

Westpac One 重设计 for Android

正确使用sqlcipher for Android

Mac 编译 XGBoost for Android

在电脑上android l for nexus 5怎么安装

Android开发:《Gradle Recipes for Android》阅读笔记(翻译)4.5——使用Android Libraries