【问题标题】:Android: Implementing a synchronous/blocking API using Messengers for IPCAndroid:使用 Messengers for IPC 实现同步/阻塞 API
【发布时间】: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有过“反”的感觉,但现在我来看看
  • 谢谢,这显然是一个更好的方法

标签: java android ipc android-handler


【解决方案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;
    }

顺序如下,

  1. 客户端准备Message并将其arg2设置为某个随机整数 (这个整数将是同步的消息 ID)。
  2. 客户准备新的CountDownLatch并把它放到LockArray
  3. 客户端用sendAndWaitResponse()发送消息。它通过Messenger 向服务发送消息并调用latch.await()
  4. 服务进程接收消息并准备回复消息。此回复消息的arg2 应与收到的消息相同。
  5. 服务通过Messenger在replyTo中向客户端发送回复消息。
  6. 客户端消息处理程序使用storeResponseAndNotify 处理消息。
  7. 当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
猜你喜欢
  • 2015-05-05
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 2013-07-20
相关资源
最近更新 更多