在一个线程中序列化传入的aidl调用

Posted

技术标签:

【中文标题】在一个线程中序列化传入的aidl调用【英文标题】:Serialized incoming aidl calls in one Thread 【发布时间】:2016-10-07 19:11:33 【问题描述】:

我有一个应用程序(客户端),它使用 AIDL 对第二个应用程序(服务器)执行远程调用。根据 AIDL 解决方案的设计,通过 Binder 进行的每次调用都在服务器应用程序中的不同线程 (TID) 中执行。

是否可以使在服务器应用程序中执行的所有调用都在一个线程中执行?我们可以控制所有调用者(客户端应用程序),它们将以串行模式执行调用,我们不需要服务器应用程序以多线程方式执行调用。

因此,如果客户端应用程序 1 对需要 30 秒且在此之前的方法执行远程调用,则第二个客户端应用程序 2 执行对相同方法(甚至其他方法)的调用,我们希望执行第二次调用在第一次调用的同一线程中。

Messenger 暂时不可用。

=== 更新 ====

消息不是一个选项(目前)。这里有更多细节:我们有一个带有 2 种类型绑定器的服务:a) TransacionManager (tm) 和 DAOImpl (dao)。

我们首先在客户端调用 tm.begin() ,甚至同步处理它,在服务端它在线程池的线程中执行(androidaidl 代码)。此线程 TID #1 在 SQLite 数据库中执行开始事务命令。

然后我们调用 dao.selectNextId() - 同步 - 在服务中它在 TID #2 中执行。在 selectNextId() 方法中,我们检查数据库是否为 inTransaction 并返回 false。

为了确认线程是问题所在,我们将所有内容放在对另一个绑定器 (allDAO) 的一次调用中。因此,当我们调用 allDAO.do() 时,它会在另一个线程 TID #3 的 Service 端运行,并很好地执行 begin transc 和 insert。

不确定问题是否出在 SQLite 将不同线程作为单独的请求进行管理(如何处理)...我们只希望服务(使用aidl)每次都在同一个线程中执行来自任何客户端的每个调用。

【问题讨论】:

所以在Service 一侧使用HandlerMessage 发送到特定线程,但老实说它没有回报...... @pskink 请阅读我的更新。提前致以最诚挚的问候和感谢 【参考方案1】:

我正在与 Mario 合作解决这个问题,并使用 @pskink 的 code sn-p 我们解决了多线程问题。

问题已解决,将所有aidl 调用重定向到主线程。为此,我们使用了一个接收 MainLooper 的 Handler 和一个扩展 CountDownLatch 的 Runnable。

我们的解决方案代码如下:

// SyncHandler.class

public class SyncHandler 
    private SyncRunnable mRunnable;

    public SyncHandler() 
        super();
    

    public SyncHandler start(@NonNull SyncRunnable runnable) 
        mRunnable = runnable;
        final Looper looper = Looper.getMainLooper();
        Handler handler = new Handler(looper);
        handler.post(mRunnable);

        try 
            mRunnable.await();
         catch (InterruptedException e) 
            Log.e(this, "Error when SyncHandler was awaiting.", e);
        

        return this;
    

    public static class ReturnValue<T> 
        public T value;
    


// SyncRunnable.class
public final class SyncRunnable extends CountDownLatch implements Runnable 
    private Runnable mRunnable;

    public static SyncRunnable create(Runnable runnable) 
        return new SyncRunnable(runnable);
    

   private SyncRunnable(Runnable runnable) 
        super(1);
        mRunnable = runnable;
    

    @Override
    public void run() 
        Log.d(this, "SyncRunnable.run() executed on thread: " + Thread.currentThread());
        mRunnable.run();
        countDown();
    


//And the database call:

// TransactionManager.class
public synchronized void begin(final int ownerHashCode, String ownerName) throws RemoteException 
    SyncHandler handler = new SyncHandler().start(SyncRunnable.create(new Runnable() 
        @Override
        public void run() 
            if (mOwner == null) 
                mOwner = ownerHashCode;

                for (Database database : mDatabases) 
                    database.beginTransaction();
                
             else if (mOwner == ownerHashCode) 
                throw new DbTransactionException("Error: TransactionOwner == owner");
            
        
    ));



// DaoHelper.class
public synchronized long insert(Dao dao) 
    final SyncHandler.ReturnValue<Long> value = new SyncHandler.ReturnValue<>();
    SyncHandler handler = new SyncHandler().start(SyncRunnable.create(new Runnable() 
        @Override
        public void run() 
            Log.d(DaoHelper.this, "db.inTransaction: " + mManagerDb.getDatabase().inTransaction());    
            value.value = mManagerDb.getDatabase().insert(mTable, null, mContentValues);
        
    ));

    return value.value;

【讨论】:

看来你错过了我的意思,我的代码是一个通用代理 Binder,它将你所有的 AIDL 调用重定向到一些预定义的 Thread / Looper,请参阅 this 以获得一些增强使用小对象池来减少 GC 活动并使用 wait / notify 调用而不是 CountDownLatch 的版本

以上是关于在一个线程中序列化传入的aidl调用的主要内容,如果未能解决你的问题,请参考以下文章

AIDL用法详解

IPC机制总结

Dubbo协议长连接

Linux 系统调用流程序列

74CallContext线程数据缓存-调用上下文 System.Runtime.Remoting.Messaging,JOIN序列化过程中日期的处理

Android开发——进程间通信之AIDL