前面说了,transact() 执行了一个远程调用,如果说 transact() 是远程调用的发起,那么 onTransact() 就是远程调用的响应。真实过程是客户端发器远程方法调用,android 系统通过底层代码对这个调用进行响应和处理,之后回调服务端的 onTransact() 方法,从数据包裹中取出方法参数,交给服务端实现的同名方法调用,最后将返回值打包返回给客户端。
需要注意的是 onTransact() 是在服务端进程的 Binder 线程池中进行的,这就意味着如果我们的要在 onTransact() 方法的中更新 UI,就必须借助 Handler。
这两个方法的第一个参数的含义是 AIDL 接口方法的标识码,在 Stub 中,定义了两个常量作为这两个方法的标示:
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
如果 code == TRANSACTION_addBook,那么说明客户端调用的是 addBook();如果 code == TRANSACTION_getBookList,那么客户端调用的是 getBookList(),然后交由相应的服务端方法处理。 用一张图来表示整个通信过程:
了解了 AIDL 的整个过程,接下来就是 AIDL 在安卓程序中的应用了。
AIDL 的使用相信大家应该都和清楚 Service 的使用了吧,Service 虽然称作“服务”,并且运行于后台,但是它们默认还是运行在默认进程的主线程中。其实让 Service 运行在默认进程中,有点大材小用了。android 的很多系统服务都运行于单独的进程中,供其他应用调用,比如窗口管理服务。这样做的好处是可以多个应用共享同一个服务,节约了资源,也便于集中管理各个客户端,要注意问题的就是线程安全问题。
那么接下来我们就用 AIDL 实现一个简单的 CS 架构的图书管理系统。
首先我们定义服务端:
BookManagerService
public class BookManagerService extends Service { private final List<Book> mLibrary = new ArrayList<>(); private IBookManager mBookManager = new IBookManager.Stub() { @Override public void addBook(Book book) throws RemoteException { synchronized (mLibrary) { mLibrary.add(book); Log.d("BookManagerService", "now our library has " + mLibrary.size() + " books"); } } @Override public List<Book> getBookList() throws RemoteException { return mLibrary; } }; @Override /* */ public IBinder onBind(Intent intent) { return mBookManager.asBinder(); } }
<service android:process=":remote" android:name=".BookManagerService"/>
服务端我们定义了 BookManagerService 这个类,在它里面我们创建了服务端的 Stub 对象,并且实现了需要实现的两个 AIDL 接口方法来定义服务端的图书管理策略。在 onBind() 方法中我们将 IBookManager 对象作为 IBinder 返回。我们知道,当我们绑定一个服务时,系统会调用 onBinder() 方法得到服务端的 IBinder 对象,并将其转换成客户端的 IBinder 对象传给客户端,虽然服务端的 IBinder 和 客户端的 IBinder 是两个 IBinder 对象,但他们在底层都是同一个对象。我们在 xml 中注册 Service 时给它指定了进程名,这样 Service 就能运行在单独的进程中了。
接下来看看客户端的实现:
Client
public class Client extends AppCompatActivity { private TextView textView; private IBookManager bookManager; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.library_book_manager_system_client); Intent i = new Intent(Client.this, BookManagerService.class); bindService(i, conn, BIND_AUTO_CREATE); Button addABook = (Button) findViewById(R.id.button); addABook.setOnClickListener(v -> { if (bookManager == null) return; try { bookManager.addBook(new Book(0, "book")); textView.setText(getString(R.string.book_management_system_book_count, String.valueOf(bookManager.getBookList().size()))); } catch (RemoteException e) { e.printStackTrace(); } }); textView = (TextView) findViewById(R.id.textView); } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d("Client -->", service.toString()); bookManager = IBookManager.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.d("Client", name.toString()); } }; }