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

时间:2016-10-07 19:11:33

标签: android multithreading ipc aidl

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

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

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

Messenger现在不是一个选择。

===更新了====

消息不是一个选项(暂时)。更多细节:我们有一种服务,有两种类型的粘合剂:a)TransacionManager(tm)和DAOImpl(dao)。

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

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

为了确认线程是问题,我们将所有内容都放在一个调用另一个绑定器(allDAO)中。因此,当我们调用allDAO.do()时,它在另一个线程TID#3中的服务端运行,并执行begin transc并插入非常好。

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

1 个答案:

答案 0 :(得分:0)

我正在与Mario合作解决此问题并使用@ pskink的code代码段解决了多线程问题。

问题解决了将所有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;
}