AIDL可以生成C++ 文件,哈哈,不仅仅是可以生成java文件哦,当然,要在android的比较新的版本上面才能够实现这点,目前需要在api-29之后。
概述
aidl是常用的android IPC方式,本文将根据一个demo来解析下AIDL的原理。
为了便于读者理解,本文不会探究Binder的实现细节,可以认为Binder在此文的分析中被看做是一个"黑盒"。有一定经验的读者可以直接到文末看总结,最终流程图如下:
Demo讲解一下AIDL
demo实现内容:MainActivity通过AIDL实现IPC来调用另一个进程中RemoteTestService的一个方法。主要有以下几个文件:
- MainActivity.java
自定义ServiceConnection,然后将ServiceConnection传入bindService,获取到IBinder后实现远程调用。
- RemoteTestService.java在ITestServer.Stub中实现需要远程调用的方法testFunction(),在onBind中返回。
- IServer.aidl定义一个aidl文件和需要远程调用的方法
- AndroidManifest.xml在此设置RemoteTestService在remote进程。
先看MainActivity.java
java
public class MainActivity extends AppCompatActivity {
private IServer iServer;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iServer = IServer.Stub.asInterface(service);
System.out.println("onServiceConnected");
try {
int a = iServer.testFunction("test string");
Log.i("test", "after testFunction a:" + a);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test", "onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this, RemoteTestService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
接下来看RemoteTestService.java
scala
public class RemoteTestService extends Service {
private IServer.Stub serverBinder = new IServer.Stub() {
@Override public int testFunction(String s) throws RemoteException {
Log.i("test","testFunction s= " + s);
return 0;
}
};
@Nullable @Override
public IBinder onBind(Intent intent) {
return serverBinder;
}
}
再看IServer.aidl
arduino
interface IServer {
int testFunction(String s);
}
再看 AndroidManifest.xml
ini
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bindservicetest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".RemoteTestService"
android:process=":remote" />
</application>
</manifest>
aidl自动生成的文件定义完aidl文件,编译会自动生成一个java接口文件。这个接口文件在build目录下,具体路径如下:
打开文件,我们就可以看到aidl自动生成的代码。
java
public interface IServer extends android.os.IInterface
{
/** Default implementation for IServer. */
public static class Default implements com.example.bindservicetest.IServer
{
@Override public int testFunction(java.lang.String s) throws android.os.RemoteException
{
return 0;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.bindservicetest.IServer
{
private static final java.lang.String DESCRIPTOR = "com.example.bindservicetest.IServer";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.bindservicetest.IServer interface,
* generating a proxy if needed.
*/
public static com.example.bindservicetest.IServer asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.bindservicetest.IServer))) {
return ((com.example.bindservicetest.IServer)iin);
}
return new com.example.bindservicetest.IServer.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_testFunction:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
int _result = this.testFunction(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.bindservicetest.IServer
{
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 int testFunction(java.lang.String s) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(s);
boolean _status = mRemote.transact(Stub.TRANSACTION_testFunction, _data,
_reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().testFunction(s);
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.bindservicetest.IServer sDefaultImpl;
}
static final int TRANSACTION_testFunction =
(android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.example.bindservicetest.IServer impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.bindservicetest.IServer getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public int testFunction(java.lang.String s) throws android.os.RemoteException;
}
上面的代码有些乱七八糟的,毕竟是aidl工具生成的,大家看核心部分就好了,那些try,那些if else 大可不必关注。所以核心代码就在下面:
Stub 里面的关键函数1-asInterface:
typescript
public static com.example.testservice.IServer asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.testservice.IServer))) {
return ((com.example.testservice.IServer)iin);
}
return new com.example.testservice.IServer.Stub.Proxy(obj);//核心代码 1
}
该核心代码就是创建一个Proxy对象,同时将IBinder对象的obj传值给Proxy。
Stub类中的关键函数2-onTransact():
java
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_testFunction:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
int _result = this.testFunction(_arg0); //核心代码2
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
onTransact函数功能主要是接收来自binder底层的调用,通过binder底层调用onTransact函数,将函数执行到核心代码2,具体的流程请看后面的小结。
Prox类里面的关键函数1 testFunction:
ini
public int testFunction(java.lang.String s) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(s);
boolean _status = mRemote.transact(Stub.TRANSACTION_testFunction, _data,
_reply, 0); // 核心代码3
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().testFunction(s);
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这个函数是和aidl文件里面定义的函数名字是一样的,后面进行跨进程通信会引用到这个函数,这个的功能是创建一个Parcel 数据_data,为什么必须是Parcel数据了,因为android跨进程传递的数据必须序列化,而序列化所采用的方式就是Parcel。大家如果研究过Parcel序列化,你们会发现它的主要实现是在native层,而Binder通信最主要的部分是在native层。另外,如果跨进程的调用testFunction有返回值,那么这个返回值讲以 _replay来存储和传递。
类的结构图如下:
以上是aidl生成的java代码的介绍,其实在android api 29之后,aidl可以生成C++的代码,具体的生成的代码的逻辑和java代码的逻辑一模一样,只是语言有修改而已。
小结
在运用AIDL通信的过程中,首先Client端(某个acitivty)先会发起bindService的请求,此时Server端(某service)会将自己的binder给Client端,也就是如下代码:
首先,Activity会执行下面的代码:
scss
Intent intent = new Intent(MainActivity.this, RemoteTestService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);//核心代码
bindServcie会去绑定服务"RemoteTestService",在执行bindService的时候会回调到connection中去,而connection的代码如下:
typescript
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = IServer.Stub.asInterface(service);//proxy
Log.d(TAG, "onServiceConnected: ");
}
@Override
public void onServiceDisconnected(ComponentName name) {
// unbindService(connection);
Log.d(TAG, "onServiceDisconnected: ");
}
};
当绑定成功就会执行onServiceConnected 回调函数,回调函数就会有一个IBinder对象service,此时通过asInterface函数,会将这个IBinder对象(service)转换成为一个Proxy对象。所以,我们Client去进行跨进程函数调用的时候就是使用这个proxy对象进行。
总结
binder基于AIDL的通信流程图如下:
Client端:MainActivity;
Server端:Service;
Service的对外aidl接口如上面的案例所示,
MainActivity已经通过BindService拿到了Service的IBinder对象。
aidl通信的基本步骤如下:
- Client通过ServiceConnection获取到Server的Binder,并且封装成一个Proxy。
- 通过Proxy来同步调用IPC方法(testFunction),同时通过Parcel将参数传给Binder,最终触发Binder的transact方法。
- Binder的transact方法最终会触发到Server上Stub的onTransact方法。
- Server上Stub的onTransact方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将结果写入Parcel后传回。
- 请注意:Client的Ipc方法中,执行Binder的transact时,是阻塞等待的,一直到Server逻辑执行结束后才会继续执行。当然,如果IPC方法是oneWay的方式,那么就是非阻塞的等待。
- 当Server返回结果后,Client从Parcel中取出返回值,于是实现了一次IPC调用。
所以,aidl 文件会生成一个java文件,这个java文件的意义在于将核心的binder驱动封装成为java层可以直接调用的代码,同时也处理了将java层的数据格式转换为Parcel格式数据进行跨进程传递的一个功能。所以,aidl是一个使用binder的标准方案,该方案的代码同样的可以通过用户自己编写的方式完成。
今日分享到此结束,对你有帮助的话,点个赞再走呗,如遇侵权联系删除
关注公众号:Android老皮解锁 《Android十大板块文档》 ,让学习更贴近未来实战。已形成PDF版
内容如下:
1.Android车载应用开发系统学习指南(附项目实战)
2.Android Framework学习指南,助力成为系统级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到精通,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360°全方面性能调优
10.Android零基础入门到精通,高手进阶之路