本文主要总结一下AIDL的基本使用和底层实现,对这方面感兴趣的可以看一下。
基本用法:
1、创建.aidl文件:IMyAidlInterface.aidl
// IMyAidlInterface.aidl
package aidl;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
String getRemoteMessage();
}
aidl文件主要描述了服务端提供了哪些功能,为简单起见这里的服务端进程只有返回一条信息的功能。
2、编写服务端代码RemoteService.java
package demo.zkp.com.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import aidl.IMyAidlInterface;
/**
* Created by zkp on 2017/2/14.
*/
public class RemoteService extends Service {
private IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {
@Override
public String getRemoteMessage() throws RemoteException {
return "hello,i am remote!";
}
};
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return stub;
}
}
自定义服务需要继承自Service,可以看到服务端拥有一个IMyAidlInterface.Stub类型的成员变量stub,在onBind方法中返回了这个stub,stub是个什么东西后面会介绍(可以看出stub是个IBinder类型)。
3、编写客户端代码MainActivity.Java
package demo.zkp.com.aidldemo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import aidl.IMyAidlInterface;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btnBindService, btnUnBindService, btnCallMethod;
private IMyAidlInterface iMyAidlInterface;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
btnBindService = (Button) findViewById(R.id.bind);
btnUnBindService = (Button) findViewById(R.id.unbind);
btnCallMethod = (Button) findViewById(R.id.call);
btnBindService.setOnClickListener(this);
btnUnBindService.setOnClickListener(this);
btnCallMethod.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind:
bindService();
break;
case R.id.unbind:
unbindService();
break;
case R.id.call:
call();
break;
}
}
private void bindService() {
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
private void unbindService() {
unbindService(serviceConnection);
iMyAidlInterface = null;
}
private void call() {
try {
String remoteMsg = iMyAidlInterface.getRemoteMessage();
Toast.makeText(this, remoteMsg, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
Log.d("dddddddddddddddddd", "dddddddddddddddddddd");
super.onDestroy();
}
}
客户端主要是绑定远程服务端然后调用服务端提供的方法。
aidl的基本用法介绍完了,下面深入了解下aidl的底层是如何实现进程间通信的。
原理浅析:
编译一下我们写的.aidl文件,会自动生成一个.java文件IMyAidlInterface.java,代码如下所示
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: G:\\AidlDemo\\app\\src\\main\\aidl\\aidl\\IMyAidlInterface.aidl
*/
package aidl;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements aidl.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "aidl.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an aidl.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof aidl.IMyAidlInterface))) {
return ((aidl.IMyAidlInterface) iin);
}
return new aidl.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getRemoteMessage: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getRemoteMessage();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements aidl.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String getRemoteMessage() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getRemoteMessage, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getRemoteMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String getRemoteMessage() throws android.os.RemoteException;
}
我们主要看一下Stub这个内部抽像类里面的代码
public static abstract class Stub extends android.os.Binder implements aidl.IMyAidlInterface
抽象类Stub继承自Binder并且实现了IMyAidlInterface里面的方法(不是真正的实现),服务端进程需要有一个Stub类型的对象并且实现它的方法。
接着看下asInterface方法:
public static aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof aidl.IMyAidlInterface))) {
return ((aidl.IMyAidlInterface) iin);
}
return new aidl.IMyAidlInterface.Stub.Proxy(obj);
}
asInterface方法需要一个IBinder类型的参数,这里分两种情况:
1、当调用asInterface方法的进程和服务端Service进程是同一进程时,不涉及进程间通信问题,此时传入的是BBinder类型的参数,最后直接将参数转化成IMyAidlInterface接口返回,即:
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof aidl.IMyAidlInterface))) {
return ((aidl.IMyAidlInterface) iin);
}
2、当调用asInterface方法的进程和服务端Service进程不是同一进程时,涉及进程间通信问题,此时传入的是BpBinder类型的参数,最后返回Stub的代理,即:
return new aidl.IMyAidlInterface.Stub.Proxy(obj);
看一下这个代理类:
private static class Proxy implements aidl.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String getRemoteMessage() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getRemoteMessage, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
这个代理类也实现了IMyAidlInterface接口,我们看下他是怎么实现的:
@Override
public java.lang.String getRemoteMessage() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getRemoteMessage, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
方法中定义了两个Parcel类型的变量_data、_reply,前一个是客户端进程往服务端传递参数用的,后一个是服务端往客户端返回数据用的,函数最终调用的是:
mRemote.transact(Stub.TRANSACTION_getRemoteMessage, _data, _reply, 0);
其中第一个参数用来表示要调用服务端进程中的哪个方法。
这个方法经过Binder驱动最终会调用到服务端stub的onTransact方法:
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getRemoteMessage: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getRemoteMessage();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
在onTransact方法里面根据code来决定调用服务端的哪个方法,并将要返回的数据写到reply里面。这个过程比较复杂,感兴趣的可以看下老罗的这一系列的文章Android进程间通信(IPC)机制Binder简要介绍和学习计划。
回想下最上面aidl的基本用法,我们在远程service进程里面new了一个Stub类型的变量并实现了它里面的方法,当客户端绑定远程service时服务端将这个stub返回给客户端进程,客户端拿到这个stub以后将它作为参数调用了Stub的静态方法asInterface,最终得到了一个服务端Stub的代理对象,所以客户端才能够像调用自己的方法一样调用服务端的方法。
演示Demo下载链接
如果大家有什么疑问或文章中有讲的不对的地方,欢迎大家提出来讨论,谢谢大家。