1

我有一个MainActivity它将向远程AutoCompleteService服务发出 IPC 调用。

在执行AutoCompleteService的 IPC 功能期间,服务将向 发出另一个 IPC 回调MainActivity

MainActivity.java

// Receive IPC call from AutoCompleteService.
private StockInfoObserver.Stub stockInfoObserver = new StockInfoObserver.Stub() {

    @Override
    public void update(StockInfo stockInfo) throws RemoteException {
        // TODO Auto-generated method stub
        Log.i(TAG, android.os.Process.myPid() + " : MainActivity receive ipc call : " + Thread.currentThread().getId());
    }

};

...
...
...

// Issue IPC call to AutoCompleteService.
button.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View arg0) {
        // Test on API.
        try {
            Log.i(TAG, android.os.Process.myPid() + " : MainActivity start issue IPC call to remote service : " + Thread.currentThread().getId());
            // autoCompleteApi.handle will issue IPC call to remote service.
            autoCompleteApi.handle("abc");
            Log.i(TAG, android.os.Process.myPid() + " : MainActivity end issue IPC call to remote service : " + Thread.currentThread().getId());
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

});

自动完成服务.java

private AutoCompleteApi.Stub autoCompleteApi = new AutoCompleteApi.Stub() {    
    private List<StockInfoObserver> stockInfoObservers = new ArrayList<StockInfoObserver>();

    @Override
    public void handle(String string) {
        Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService start receive ipc call : " + Thread.currentThread().getId());
        try {
            for (StockInfoObserver stockInfoObserver : stockInfoObservers) {    
                Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService start IPC call to MainActivity : " + Thread.currentThread().getId());
                // stockInfoObserver.update will issue IPC call back to MainActivity
                stockInfoObserver.update(null);
                Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService end IPC call to MainActivity : " + Thread.currentThread().getId());
            }
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService end receive ipc call : " + Thread.currentThread().getId());
    }

    @Override
    public void attachStockInfoObserver(StockInfoObserver stockInfoObserver)
            throws RemoteException {
        if (stockInfoObservers.contains(stockInfoObserver) == false) {
            stockInfoObservers.add(stockInfoObserver);
        }
    }
};

我最初的预期是,会发生死锁。这是由于我的观察。发出 IPC 调用时,发出者只会在 IPC 接收者完成其 IPC 函数执行后从 IPC 调用返回。

  1. MainActivityAutoCompleteService通过发出 IPC 调用autoCompleteApi.handle
  2. MainActivity现在将等到AutoCompleteService完成执行。
  3. AutoCompleteServiceMainActivity通过发出 IPC 调用stockInfoObserver.update
  4. AutoCompleteService现在将等到MainActivity完成执行。
  5. 但是,MainActivity的线程仍在等待,没有线程将执行update功能。
  6. 两个进程一直在等待对方。

但是,上述情况不会发生。这是我得到的日志。一切都完美无瑕。

// Log in MainActivity TAG
3930 : MainActivity start issue IPC call to remote service : 1
3930 : MainActivity receive ipc call : 1
3930 : MainActivity end issue IPC call to remote service : 1

// Log in AutoCompleteService TAG
3961 : AutoCompleteService start receive ipc call : 494
3961 : AutoCompleteService start IPC call to MainActivity : 494
3961 : AutoCompleteService end IPC call to MainActivity : 494
3961 : AutoCompleteService end receive ipc call : 494

但我真的不明白。如果 MainActivity 线程(Id 为 1)没有从函数调用(autoCompleteApi.handle)返回,它如何“跳转”到执行另一个函数(update(StockInfo stockInfo))?

我期待MainActivity 接收由不同线程打印的 ipc 调用。不是 ID 为 1 的线程。如果不是,应该会发生死锁。

如果您有兴趣尝试,请在此处下载完整的源代码:https ://www.dropbox.com/s/8hd7v5acjd213l1/jstock-android2.zip

4

1 回答 1

2

一个有趣的问题。我的第一个想法是传入的 IPC 调用是在不同的线程上处理的(因为它通常会发生在传入的 IPC 调用中)在这种特定情况下结果是错误的。

查看传入调用到达时的执行堆栈(最近的堆栈帧在顶部):

MainActivity$1.update
MainActivity$1.onTransact
MainActivity$1.execTransact         <- this gets called by the incoming IPC call
BinderProxy.transact                <- this is where the outgoing IPC call is made
AutoCompleteApi$Stub$Proxy.handle
MainActivity$3.onClick
...

所以一切都发生在同一个线程中。呼入看起来像呼出的子程序调用。使这成为可能的魔法发生在本机代码中(或者可能在内核中)。Thorsten Schreiber有一篇有趣的论文解释了 Binder 机制的一些内部结构。不幸的是,它没有讨论对与本示例中发生的相同进程的反向调用。

在这篇博文中有一个关于这个特性的小评论:

请注意,原始线程在等待回复时也可能会收到 BR_TRANSACTION 命令。这表示跨进程的递归,接收线程调用原始进程中的对象。驱动程序负责跟踪所有活动事务,因此它可以在递归发生时将事务分派到正确的线程。

也许还有更多可以在OpenBinder 网站上找到,它是 Android Binder 实现的根目录。

于 2013-01-11T16:06:00.273 回答