如果你是普通app开发,有可能遇不到跨进程访问数据。在车厂,智能座舱开发,跨进程访问数据是很平常的事,比如车机账户系统,很多模块都要获取当前车机账户信息,那账户系统就会提供相应数据给其他模块,账户系统和其他模块不属于同一个进程,这个时候就出现跨进程场景,Android系统为我们提供了一种技术方案:AIDL
1.基本使用
服务端创建
AIDL文件
interface IDemoInterface {
void demo();
int getDemo();
void setDemo(int i);
}
具体服务实现
public class DemoServer extends IDemoInterface.Stub{
private int value=0;
@Override
public void demo() throws RemoteException {
Log.e("DemoServer","demo");
}
@Override
public int getDemo() throws RemoteException {
return value;
}
@Override
public void setDemo(int i) throws RemoteException {
value=i;
Log.e("DemoServer","setDemo"+i);
}
}
服务
public class DemoService extends Service {
private DemoServer demoServer;
public DemoService() {
}
@Override
public void onCreate() {
super.onCreate();
demoServer=new DemoServer();
}
@Override
public IBinder onBind(Intent intent) {
return demoServer;
}
}
以上是服务端代码,基本功能就是写入值、取出值。
客户端代码
public class MainActivity extends AppCompatActivity {
private TextView startService,demo,setDemo,getDemo;
IDemoInterface idemo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService=findViewById(R.id.startService);
demo=findViewById(R.id.demo);
setDemo=findViewById(R.id.setDemo);
getDemo=findViewById(R.id.getDemo);
startService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("com.roxmotor.aidlservice.DemoService");
intent.setPackage("com.roxmotor.aidlservice");
boolean result = getApplicationContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Log.d("MainActivity",result+"");
}
});
demo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
idemo.demo();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
setDemo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Log.d("MainActivity","设置了200");
idemo.setDemo(200);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
getDemo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
int value=idemo.getDemo();
Log.e("MainActivity","获取值:"+value);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//成功连接
Log.d("MainActivity","pushManager ***************成功连接***************");
idemo=IDemoInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//断开连接调用
Log.d("MainActivity","pushManager ***************连接已经断开***************");
}
};
}
执行结果
上面基本使用方式就不详细说了,这里就不详细说adil如何使用了,我们直接来看aidl是如何帮助我们跨进程处理数据的。
public interface IDemoInterface extends android.os.IInterface
{
/** Default implementation for IDemoInterface. */
public static class Default implements com.roxmotor.aidlservice.IDemoInterface
{
@Override public void demo() throws android.os.RemoteException
{
}
@Override public int getDemo() throws android.os.RemoteException
{
return 0;
}
@Override public void setDemo(int i) throws android.os.RemoteException
{
}
@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.roxmotor.aidlservice.IDemoInterface
{
private static final java.lang.String DESCRIPTOR = "com.roxmotor.aidlservice.IDemoInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.roxmotor.aidlservice.IDemoInterface interface,
* generating a proxy if needed.
*/
public static com.roxmotor.aidlservice.IDemoInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.roxmotor.aidlservice.IDemoInterface))) {
return ((com.roxmotor.aidlservice.IDemoInterface)iin);
}
return new com.roxmotor.aidlservice.IDemoInterface.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_demo:
{
data.enforceInterface(descriptor);
this.demo();
reply.writeNoException();
return true;
}
case TRANSACTION_getDemo:
{
data.enforceInterface(descriptor);
int _result = this.getDemo();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_setDemo:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
this.setDemo(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.roxmotor.aidlservice.IDemoInterface
{
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 void demo() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_demo, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().demo();
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int getDemo() 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);
boolean _status = mRemote.transact(Stub.TRANSACTION_getDemo, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getDemo();
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void setDemo(int i) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(i);
boolean _status = mRemote.transact(Stub.TRANSACTION_setDemo, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().setDemo(i);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.roxmotor.aidlservice.IDemoInterface sDefaultImpl;
}
static final int TRANSACTION_demo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getDemo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setDemo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
public static boolean setDefaultImpl(com.roxmotor.aidlservice.IDemoInterface impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.roxmotor.aidlservice.IDemoInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public void demo() throws android.os.RemoteException;
public int getDemo() throws android.os.RemoteException;
public void setDemo(int i) throws android.os.RemoteException;
}
结构如下
代码初看比较长,我们一个个来看。
1.我们从客户端开始看,客户端是如何拿到服务端句柄的,客户端和服务端是不同的进程,不同进程在不同的用户空间,不同用户空间,为了数据安全不能相互访问、修改数据。
看客户端代码,我们是通过idemo对象访问服务端接口的。idemo是怎么来的?
我们可以看到,我们是通过bindeService绑定DemoService ,绑定操作完成后会收到一个回调serviceConnection ,这个回调函数我们可以拿到一个binder,我们记住这个binder
我们再看下服务端Service,我们可以看到onBind函数里,返回了一个binder,这个binder就是Stub(DemoServer),回头来看,客户端绑定服务拿到的binder就是这个Stub
有没有发现,客户端里面有个IDemoInterface ,这个就涉及到客户端也定义了IDemoInterface.aidl文件,而且文件路径包名和服务端一样。这个是为什么?
我们先不关心这个包名为什么需要一样,假设可以不一样。
点击make project,会生成aidl类
我们再客户端代码可以看到idemo=IDemoInterface.Stub.asInterface(service);
我们进入asInterface可以看到我们拿到了proxy,(这个接口实现了IDemoInterface)
会看服务端定义了几个接口,我们需要无感访问服务端,那起码这个proxy也能提供一摸一样的接口吧,那我们在客户端定义的IDemoInterface是不是也要提供一样的接口,是的,必须一样,(也可以不一样,那aild类自己写也可以)。
我们在看看aidl类中proxy,remote对象,就是服务端中的binder,就是asInterface中的对象。
我们在看看proxy接口。每个接口中都有 _data.writeInterfaceToken(DESCRIPTOR);在调用remote的时候需要带入_data参数,这个参数里面设定了一个DESCRIPTOR,这个什么,应该是一个身份验证需要的数据。看代码,DESCRIPTOR
是aidl的包名+aidl名称,就是说在拿到服务端binder,调用服务端接口时,需要提供这个DESCRIPTOR,如果这个DESCRIPTOR和服务端不一样,那肯定校验不过(后面来看如何校验),所以包名和aidl名称必须和服务端一样。
现在我们知道了客户端aidl包名必须和服务端一样,我们继续看下proxy接口时具体如何访问服务端接口的。
boolean _status = mRemote.transact(Stub.TRANSACTION_demo, _data, _reply, 0);
拿到mRemote后,调用它的transact方法,执行binder内部操作(后面看transact如何执行)
这样就调用了服务端接口。
如果不用代理proxy也可以,自己收到写aidl类。
我们看下总结下客户端如何通过aidl访问服务端
,
我们先看下Default这个类,他只是实现了com.roxmotor.aidlservice.IDemoInterface这个接口,而且都是空实现,同时并没有继承Binder,asBinder返回是null,我个人一般不用Default。
Stub构造函数,当我们bindService时,在onBind返回一个IBind类型接口,因为所有的IBinder aidl 远程调用都要继承自IDemoInterface.Stub,所以必然会调用到
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
Stub 的空构造函数。而Stub 本身又是Binder 的子类。调用了Binder 的attachInterface 方法。
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
分别保存了当前类的实例对象(This,也就是当前的binder)和当前类接口描述符(DESCRIPTOR),后面会通过DESCRIPTOR查找对应的binder
private static final java.lang.String DESCRIPTOR = "com.roxmotor.aidlservice.IDemoInterface";
public static com.roxmotor.aidlservice.IDemoInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.roxmotor.aidlservice.IDemoInterface))) {
return ((com.roxmotor.aidlservice.IDemoInterface)iin);
}
return new com.roxmotor.aidlservice.IDemoInterface.Stub.Proxy(obj);
}
我们知道这里的obj是bindService拿到的远程Binder,即BinderProxy(这里为什么是BinderProxy,后面文章会说到。)
那我们看下BinderProxy的queryLocalInterface(DESCRIPTOR)
public IInterface queryLocalInterface(String descriptor) {
return null;
}
这里返回null,asInterface方法直接返回com.roxmotor.aidlservice.IDemoInterface.Stub.Proxy(obj)。如果obj不是远程的BinderProxy,那就是本进程Binder,其方法如下
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
这里的mOwner,就是本地的一个Stub
这里的asInterface,就是判断是拿本地binder还是拿远程binder即BinderProxy
private static class Proxy implements com.roxmotor.aidlservice.IDemoInterface
{
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 void demo() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_demo, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().demo();
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int getDemo() 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);
boolean _status = mRemote.transact(Stub.TRANSACTION_getDemo, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getDemo();
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void setDemo(int i) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(i);
boolean _status = mRemote.transact(Stub.TRANSACTION_setDemo, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().setDemo(i);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.roxmotor.aidlservice.IDemoInterface sDefaultImpl;
}
调用远程binder会走Proxy这里。
我们可以大概看看出,Proxy实现了IDemoInterface的方法,内部实现是通过mRemote去调用的。这个mRemote就是bindService拿到的IBinder service,也就是BinderProxy.
我们拿getDemo方法来看看
_data是传参,_reply是返回值,他们是Parcel类型(进程间通信由于资源不共享,因此无法直接传递对象,只能通过序列化在不同的空间拷贝两份相同的资源)
我们可以看出,不管哪个方法,_data都要存储DESCRIPTOR,也就是描述符。现在我们知道为什么客户端adil为什么要和服务端一样了。他们一样的话,DESCRIPTOR也就一样,服务端可以通过这个DESCRIPTOR可以在onTransact里面找到对应的方法。
getDemo没有参数,所以_data里面没有存储给服务端参数。setDemo里面存储了一个参数,调用了_data.writeInt(i)。
接下来执行
mRemote.transact(Stub.TRANSACTION_getDemo, _data, _reply, 0);
这里的mRemote,我们现在知道,他是BinderProxy.调用了transact方法,最终还是执行了Stub中的onTransact
@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_demo:
{
data.enforceInterface(descriptor);
this.demo();
reply.writeNoException();
return true;
}
case TRANSACTION_getDemo:
{
data.enforceInterface(descriptor);
int _result = this.getDemo();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_setDemo:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
this.setDemo(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
我们看下TRANSACTION_getDemo,这里我们把this.getDemo方法的结果存储到reply中。这里this.getDemo是那里实现呢?
这里的Stub实现了IDemoInterface接口,但是并没有真正的实现,那在那里实现了?我们回看代码,发现了一个叫DemoServer的类,继承了IDemoInterface.Stub,实现了所有方法。
@Override
public int getDemo() throws RemoteException {
return value;
}
this.getDemo就是返回value。
我们继续往下看。
执行mRemote.transact方法后,_result = _reply.readInt();会取出_reply值,然后return _result。最终拿到返回值。
整个过程完成。
Proxy调用getDemo,实际上是通过调用mRemote.transact()来触发远端Stub的onTransact()
一般流程:
1.binderService,获得一个远程binder,即BinderProxy
2.在Proxy,拿到BinderProxy,同时通过Parcel将参数传给BinderProxy,最终触发BinderProxy的transact方法。
3.Binder的transact方法最终会触发到Server上Stub的onTransact方法。
4.Server上Stub的onTransact方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将结果写入Parcel后传回。
5.Client的Ipc方法中,执行Binder的transact时,是阻塞等待的。一直到Server逻辑执行结束后才会继续执行。
6.当Server返回结果后,Client从Parcel中取出返回值,于是实现了一次IPC调用。
大概流程如下:
1.ServiceConnection是如何获取到Binder的而且是BinderProxy?
2.transact方法最终是如何触发Stub中的onTransact的?
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。