最近在学习巩固Framework的一些东西,学完BInder后接触到AIDL,尝试简单使用一下。期间也遇到一些问题,但都解决了,在此记录一下。
本篇博客可以了解到:
1.AIDL IPC的简单使用,本demo使用同一项目下的两个Module代表C端和S端,C端简单调用S端服务
2.了解AIDL生成的java文件内容
3.解决C端bindService失败的问题
1、S端创建AIDL文件
// IMyAidlInterface.aidl
package cn.edu.swu.server;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
//业务代码
String getName(int seq);
}
2、编译生成java文件
在目录build\generated\aidl_source_output_dir\debug\out\cn\edu\swu\kttest\IMyAidlInterface.java
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package cn.edu.swu.server;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Default implementation for IMyAidlInterface.
*/
public static class Default implements cn.edu.swu.server.IMyAidlInterface {
//业务代码
@Override
public java.lang.String getName(int seq) throws android.os.RemoteException {
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements cn.edu.swu.server.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "cn.edu.swu.server.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an cn.edu.swu.server.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static cn.edu.swu.server.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof cn.edu.swu.server.IMyAidlInterface))) {
return ((cn.edu.swu.server.IMyAidlInterface) iin);
}
return new cn.edu.swu.server.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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.getName(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements cn.edu.swu.server.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 getName(int seq) 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);
_data.writeInt(seq);
boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getName(seq);
}
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static cn.edu.swu.server.IMyAidlInterface sDefaultImpl;
}
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(cn.edu.swu.server.IMyAidlInterface 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 cn.edu.swu.server.IMyAidlInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
//业务代码
public java.lang.String getName(int seq) throws android.os.RemoteException;
}
1、IMyAidlInterface接口
它声明了IMyAidlInterface.aidl定义的getName方法,继承自IInterface。
2、IMyAidlInterface.Stub抽象类
IMyAidlInterface的静态内部类,它继承自Binder,说明它是一个 Binder 本地对象,它虽然实现了IMyAidlInterface接口,但是它继续声明为一个抽象类,并没有实现IMyAidlInterface接口中的getName方法,表明子类需要实现getName方法,返回具体的信息,而服务端将会实现这个Stub抽象类。
3、IMyAidlInterface.Stub.Proxy代理类
IMyAidlInterface.stub的静态内部类,它实现了IMyAidlInterface接口,并且实现了getName方法,但是里面只是把数据装进data这个Parcel对象,通过mRemote的transact方法发送给服务端,接着用reply这个Parcel对象等待服务端数据的返回,这一切都是通过mRemote这个IBinder对象进行,mRemote代表着Binder对象的本地代理,mRemote会通过Binder驱动来完成与远程服务端的Stub的通信。
可以看到Stub类和Stub.Proxy类都实现了IMyAidlInterface接口,这就是一个典型的代理模式,它们的getName方法有着不同的实现,Stub类它将会在远程的服务端完成getName方法的具体实现,而Stub.Proxy类是本地客户端的一个代理类,它已经替我们默认的实现了getName方法,该方法里面通过mRemote这个Binder引用的transact方法把请求通过驱动发送给服务端,我们注意到mRemote发送请求时还传进了TRANSACTION_getName这个代表着getName方法的标识名,这表示客户端告诉服务端我要调用getName这个方法,当驱动把请求转发给服务端后,服务端的Stub类的onTransact方法就会回调,它里面有一个switch语句,根据code来调用不同的方法,这时它就会走到case TRANSACTION_getName这个分支,然后调用getName方法的在服务端的具体实现,如果有返回值的话,还会通过reply返回给客户端,这样就通过Binder驱动完成了一次远程方法调用(RPC)。
这里要注意的是客户端通过mRemote的transact方法把请求发送给客户端之后,这时会阻塞UI线程等待服务端的返回,而服务端的onTransact方法回调时,服务端的getUser方法会被回调,这时服务端的getName方法是运行在服务端Binder线程池中,所以如果此时有UI操作需要回到UI线程再进行UI操作。
IInterface
这是一个接口,用来表示服务端提供了哪些服务,如果服务端需要暴露调用服务的方法给客户端使用,就一定要继承这个接口,它里面有个asBinder方法,用于返回当前的Binder对象。
IBinder
这时一个跨进程通信的Base接口,它声明了跨进程通信需要实现的一系列抽象方法,实现了这个接口就说明可以进行跨进程通信,Binder和BinderProxy都继承了这个接口。
Binder
代表的是 Binder 本地对象(Binder实体),它继承自IBinder,所有本地对象都要继承Binder,Binder中有一个内部类BinderProxy,它也继承自IBinder,它代表着Binder对象的本地代理(Binder引用),Binder实体只存在于服务端,而Binder引用则遍布于各个客户端。
3、配置服务端
package cn.edu.swu.server
import android.app.Service
import android.content.Intent
import android.os.IBinder
class RemoteService : Service() {
private val mBinder = object :IMyAidlInterface.Stub() {
override fun getName(seq: Int): String {
return "server"
}
}
override fun onBind(p0: Intent?): IBinder? {
return mBinder
}
}
<service android:name=".RemoteService">
<intent-filter>
<action android:name="cn.edu.swu.server.remote"/>
intent-filter>
service>
4、客户端调用
复制S端AIDL目录下内容到C端,注意包名一样
package cn.edu.swu.aidltest
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.widget.TextView
import cn.edu.swu.server.IMyAidlInterface
class MainActivity : AppCompatActivity() {
private lateinit var mService : IMyAidlInterface
//1、创建ServiceConnection
private val serviceConnection = object :ServiceConnection {
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
Log.d("tag", "connect server success!")
//把服务端返回的Binder引用,通过Stub.asInterface方法包装成本地代理类IMyAidlInterface.Stub.Proxy
mService = IMyAidlInterface.Stub.asInterface(p1)
//通过本地代理对象远程调用服务端的方法
Log.d("tag", mService.getName(0))
}
override fun onServiceDisconnected(p0: ComponentName?) {
Log.d("tag", "connect server failed!")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//2、启动并通过ServiceConnection绑定远程服务
val intent = Intent("cn.edu.swu.server.remote")
intent.setPackage("cn.edu.swu.server")
val res = applicationContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
Log.d("tag", res.toString())
}
override fun onDestroy() {
super.onDestroy()
//3、解绑服务
unbindService(serviceConnection)
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.edu.swu.aidltest">
<queries>
<package android:name="cn.edu.swu.server" />
queries>
<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/Theme.AidlTest">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
application>
manifest>
Log:
2022-05-23 11:22:44.749 12084-12084/cn.edu.swu.aidltest D/tag: true
2022-05-23 11:22:45.479 12084-12084/cn.edu.swu.aidltest D/tag: connect server success!
2022-05-23 11:22:45.482 12084-12084/cn.edu.swu.aidltest D/tag: server
ps:终于连上了,因为SdkVersion问题,bindService返回false,找了半天的bug
注意:
感谢:Android 解决AIDL bindService异常_看我大华夏的博客-CSDN博客_aidl bindservice失败
感谢:AIDL报错,bindService一直连接不上、不起作用。_马占柱的博客-CSDN博客_aidl bindservice失败