共计 5172 个字符,预计需要花费 13 分钟才能阅读完成。
AIDL(Android Interface Definition Language)是一种 IDL 语言,用于生成能够在 Android 设施上两个过程之间进行过程间通信(IPC)的代码。通过 AIDL,能够在一个过程中获取另一个过程的数据和调用其裸露进去的办法,从而满足过程间通信的需要。通常,裸露办法给其余利用进行调用的利用称为服务端,调用其余利用的办法的利用称为客户端,客户端通过绑定服务端的 Service 来进行交互。
官网文档中对 AIDL 有这样一段介绍:
Using AIDL is necessary only if you allow clients from different
applications to access your service for IPC and want to handle
multithreading in your service. If you do not need to perform
concurrent IPC across different applications, you should create your
interface by implementing a Binder or, if you want to perform IPC, but
do not need to handle multithreading, implement your interface using a
Messenger. Regardless, be sure that you understand Bound Services
before implementing an AIDL.
第一句很重要,“只有当你容许来自不同的客户端拜访你的服务并且须要解决多线程问题时你才必须应用 AIDL”,其余状况下你都能够抉择其余办法,如应用 Messenger,也能跨过程通信。可见 AIDL 是解决多线程、多客户端并发拜访的,而 Messenger 是单线程解决。
上面介绍 AIDL 的应用办法。
1 创立 AIDL 文件
AIDL 文件能够分为两类。一类用来申明实现了 Parcelable 接口的数据类型,以供其余 AIDL 文件应用那些非默认反对的数据类型。还有一类是用来定义接口办法,申明要裸露哪些接口给客户端调用。在 AIDL 文件中须要明确表明援用到的数据类型所在的包名,即便两个文件处在同个包名下。
默认状况下,AIDL 反对下列数据类型:
八种根本数据类型:byte、char、short、int、long、float、double、boolean
String,CharSequence
List 类型。List 承载的数据必须是 AIDL 反对的类型,或者是其它申明的 AIDL 对象
Map 类型。Map 承载的数据必须是 AIDL 反对的类型,或者是其它申明的 AIDL 对象
客户端和服务端都须要创立,咱们先在服务端中创立,而后复制到客户端即可。在 Android Studio 中右键点击新建一个 AIDL 文件,如图所示:
创立实现后,零碎就会默认创立一个 aidl 文件夹,文件夹下的目录构造即是工程的包名,AIDL 文件就在其中。如图所示:
文件中会有一个默认办法,能够删除掉,也能够新增其余办法。
2 实现接口
创立或批改过 AIDL 文件后须要 build 下工程,Android SDK 工具会生成以 .aidl 文件命名的 .java 接口文件(例如,IRemoteService.aidl 生成的文件名是 IRemoteService.java),在过程间通信中真正起作用的就是该文件。生成的接口蕴含一个名为 Stub 的子类(例如,IRemoteService.Stub),该子类是其父接口的形象实现,并且会申明 AIDL 文件中的所有办法。
如要实现 AIDL 生成的接口,请实例化生成的 Binder 子类(例如,IRemoteService.Stub),并实现继承自 AIDL 文件的办法。
以下是应用匿名外部类实现 IRemoteService 接口的示例:
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int getPid(){return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {// Does nothing}
};
复制代码
当初,binder 是 Stub 类的一个实例(一个 Binder),其定义了服务端的 RPC 接口。
3 服务端公开接口
在为服务端实现接口后,须要向客户端公开该接口,以便客户端进行绑定。创立 Service 并实现 onBind(),从而返回生成的 Stub 的类实例。以下是服务端的示例代码:
public class RemoteService extends Service {
private final String TAG = "RemoteService";
@Override
public void onCreate() {super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
Log.d(TAG, "onBind");
return binder;
}
private final IRemoteService.Stub binder = new IRemoteService.Stub() {public int getPid() {return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {Log.d(TAG, "basicTypes anInt:" + anInt + ";aLong:" + aLong + ";aBoolean:" + aBoolean + ";aFloat:" + aFloat + ";aDouble:" + aDouble + ";aString:" + aString);
}
};
}
复制代码
咱们还须要在 Manefest 文件中注册咱们创立的这个 Service,否则客户端无奈绑定服务。
<service
android:name=".RemoteService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.aidl"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
复制代码
4 客户端调用 IPC 办法
当客户端(如 Activity)调用 bindService() 以连贯此服务时,客户端的 onServiceConnected() 回调会接管服务端的 onBind() 办法所返回的 binder 实例。
客户端还必须领有接口类的拜访权限,因而如果客户端和服务端在不同利用内,则客户端利用的 src/ 目录内必须蕴含 .aidl 文件(该文件会生成 android.os.Binder 接口,进而为客户端提供 AIDL 办法的拜访权限)的正本。所以咱们须要把服务端的 aidl 文件夹整个复制到客户端的 java 文件夹同个层级下,不须要改变任何代码。
当客户端在 onServiceConnected() 回调中收到 IBinder 时,它必须调用 IRemoteService.Stub.asInterface(service),以将返回的参数转换成 IRemoteService 类型。例如:
IRemoteService iRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
iRemoteService = IRemoteService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {Log.e(TAG, "Service has unexpectedly disconnected");
iRemoteService = null;
}
};
复制代码
取得了 iRemoteService 对象,咱们就能够调用 AIDL 中定义的办法了。如要断开连接,能够调用 unbindService() 办法。以下是客户端的示例代码:
public class MainActivity extends AppCompatActivity {
private final String TAG = "ClientActivity";
private IRemoteService iRemoteService;
private Button mBindServiceButton;
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBindServiceButton = findViewById(R.id.btn_bind_service);
mBindServiceButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {String text = mBindServiceButton.getText().toString();
if ("Bind Service".equals(text)) {Intent intent = new Intent();
intent.setAction("com.example.aidl");
intent.setPackage("com.example.aidl.server");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
} else {unbindService(mConnection);
mBindServiceButton.setText("Bind Service");
}
}
});
}
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {Log.d(TAG, "onServiceDisconnected");
iRemoteService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {Log.d(TAG, "onServiceConnected");
iRemoteService = IRemoteService.Stub.asInterface(service);
try {int pid = iRemoteService.getPid();
int currentPid = Process.myPid();
Log.d(TAG, "currentPID:" + currentPid + ", remotePID:" + pid);
iRemoteService.basicTypes(12, 123, true, 123.4f, 123.45,
"服务端你好,我是客户端");
} catch (RemoteException e) {e.printStackTrace();
}
mBindServiceButton.setText("Unbind Service");
}
};
}