1.AIDL是什么?
AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此而生成的一个IInterface的实例代码,AIDL其实是为了避免我们重复编写代码而出现的一个模板.
2.简单的Demo
1.定义aidl文件,IPersonAidlInterface.aidl,之后执行Build->Rebuild Project
interface IPersonAidlInterface {
/**
* 在 AIDL 中可以通过可带参数以及返回值的一个或多个方法来声明接口,参数和返回值可以是任意类型,AIDL 中支持的数据类型如下:
*
* java 的 8 种数据类型:byte、short、int、long、float、double、boolean、char
* 除此之外支持 String、charSequence、List、Map
* 自定义数据类型
*
* 如果参数或返回值类型为 List 或 Map 时:
* List 中的所有元素都必须是 AIDL 支持的数据类型、其他 AIDL 生成的接口或自己声明的可打包类型。可选择将 List 用作“通用”类(例如,List)。
* 另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。
*
* Map 中的所有元素都必须是 AIDL 支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map<String,Integer> 形式的 Map)。
* 另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。
*/
//具体的业务
void setName(String name);
void setAge(int age);
String getInfo();
}
会在app\build\generated\aidl_source_output_dir\debug\out路径下生成模板接口如下
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.myapplication2;
// Declare any non-default types here with import statements
public interface IPersonAidlInterface extends android.os.IInterface
{
/** Default implementation for IPersonAidlInterface. */
public static class Default implements com.example.myapplication2.IPersonAidlInterface
{
@Override public void setName(java.lang.String name) throws android.os.RemoteException
{
}
@Override public void setAge(int age) throws android.os.RemoteException
{
}
@Override public java.lang.String getInfo() 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 com.example.myapplication2.IPersonAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.myapplication2.IPersonAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.myapplication2.IPersonAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.myapplication2.IPersonAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.myapplication2.IPersonAidlInterface))) {
return ((com.example.myapplication2.IPersonAidlInterface)iin);
}
return new com.example.myapplication2.IPersonAidlInterface.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_setName:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_setAge:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
this.setAge(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getInfo:
{
data.enforceInterface(descriptor);
java.lang.String _result = this.getInfo();
reply.writeNoException();
reply.writeString(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.myapplication2.IPersonAidlInterface
{
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 setName(java.lang.String name) 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.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().setName(name);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void setAge(int age) 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(age);
boolean _status = mRemote.transact(Stub.TRANSACTION_setAge, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().setAge(age);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.lang.String getInfo() 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);
boolean _status = mRemote.transact(Stub.TRANSACTION_getInfo, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getInfo();
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.myapplication2.IPersonAidlInterface sDefaultImpl;
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getInfo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
public static boolean setDefaultImpl(com.example.myapplication2.IPersonAidlInterface 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.example.myapplication2.IPersonAidlInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public void setName(java.lang.String name) throws android.os.RemoteException;
public void setAge(int age) throws android.os.RemoteException;
public java.lang.String getInfo() throws android.os.RemoteException;
}
2.实现我们自己的远程服务业务逻辑
public class IPersonImpl extends IPersonAidlInterface.Stub {
private String name;
private int age;
@Override
public void setName(String name) throws RemoteException {
this.name = name;
}
@Override
public void setAge(int age) throws RemoteException {
this.age = age;
}
@Override
public String getInfo() throws RemoteException {
return "My name is " + name + ", age is " + age + "!";
}
}
3.注册具体的服务,将Binder对象返回可使用远程服务
android:process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote
- 当使用android:process属性时,查看日志:
2023-05-28 00:47:50.480 28717-28717 xiao com.example.myapplication I 绑定服务…
2023-05-28 00:47:50.494 28717-28717 xiao com.example.myapplication I 具体的业务对象:com.example.myapplication2.IPersonAidlInterface S t u b Stub StubProxy@db3f713
2023-05-28 00:47:52.351 28717-28717 xiao com.example.myapplication I 调用具体服务…
2023-05-28 00:47:52.356 28717-28717 xiao com.example.myapplication I 调用的服务信息:My name is Tom, age is 10!
可以看出获得的Binder对象为代理对象
通过命令adb shell ps -ef "| grep com.example.myapplication"获取当前进程
u0_a145 28717 344 0 16:27:54 ? 00:00:03 com.example.myapplication
u0_a145 28766 344 0 16:27:57 ? 00:00:00 com.example.myapplication:remote
可以看到service存在于不同的进程 - 当不使用android:process属性时,查看日志:
2023-05-28 00:53:35.721 30654-30654 xiao com.example.myapplication I 绑定服务…
2023-05-28 00:53:35.735 30654-30654 xiao com.example.myapplication I 具体的业务对象:com.example.myapplication.aidl.IPersonImpl@3ecaf38
2023-05-28 00:53:37.211 30654-30654 xiao com.example.myapplication I 调用具体服务…
2023-05-28 00:53:37.211 30654-30654 xiao com.example.myapplication I 调用的服务信息:My name is Tom, age is 10!
获得的Binder对象为同一进程对象
u0_a145 30654 344 3 16:53:26 ? 00:00:01 com.example.myapplication
只存在一个进程
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
...
<service
android:name=".service.PersonService"
android:process=":remote"
android:enabled="true"
android:exported="true">
</service>
</application>
</manifest>
public class PersonService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IPersonImpl();
}
}
4.在MainActivity中使用
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "xiao";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bind_service).setOnClickListener(this);
findViewById(R.id.unbind_service).setOnClickListener(this);
findViewById(R.id.use_service).setOnClickListener(this);
}
private IPersonAidlInterface iPersonAidlInterface;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 根据实际情况返回 IBinder 的本地对象或其代理对象
iPersonAidlInterface = IPersonAidlInterface.Stub.asInterface(service);
Log.i(TAG, "具体的业务对象:" + iPersonAidlInterface);
}
@Override
public void onServiceDisconnected(ComponentName name) {
// Service 意外中断时调用
}
};
public void bindServiceClick(View view) {
Log.i(TAG, "绑定服务...");
Intent intent = new Intent(this, PersonService.class);
// 绑定服务时自动创建服务
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
public void unbindServiceClick(View view) {
Log.i(TAG, "解绑服务...");
unbindService(conn);
}
public void callRemoteClick(View view) {
Log.i(TAG, "调用具体服务...");
try {
iPersonAidlInterface.setName("Tom");
iPersonAidlInterface.setAge(10);
String info = iPersonAidlInterface.getInfo();
Log.i(TAG, "调用的服务信息:" + info);
} catch (RemoteException | NullPointerException e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.bind_service:
bindServiceClick(v);
break;
case R.id.unbind_service:
unbindServiceClick(v);
break;
case R.id.use_service:
callRemoteClick(v);
break;
}
}
}
5.activity布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/bind_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="绑定服务"
app:layout_constraintBottom_toTopOf="@+id/unbind_service"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.756" />
<Button
android:id="@+id/unbind_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="解绑服务"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.587" />
<Button
android:id="@+id/use_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="使用服务"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/unbind_service"
app:layout_constraintVertical_bias="0.286" />
</androidx.constraintlayout.widget.ConstraintLayout>
3.in out inout区别
- tag in: 服务端修改不会同步到客户端传递的对象
- tag out: 客户端传递对象的值无效,但服务端可修改,且客户端会同步
- tag inout: 服务端可读取客户端传递的完整数据,且修改数据会同步到客户端对象
1.Book.java 传输的数据
public class Book implements Parcelable {
private String name;
private int price;
public Book() {}
public Book(String name, int price) {
this.name = name;
this.price = price;
}
protected Book(Parcel in) {
name = in.readString();
price = in.readInt();
}
public void setName(String name) {
this.name = name;
}
public void setPrice(int price) {
this.price = price;
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
public void readFromParcel(Parcel in) {
this.name = in.readString();
this.price = in.readInt();
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
2.Book.aidl
package com.xiao.aidldemo.aidl;
import com.xiao.aidldemo.aidl.Book;
parcelable Book;
3.IBookManager.aidl
package com.xiao.aidldemo;
import com.xiao.aidldemo.Book;
interface IBookManager {
List<Book> getBooks();
Book addBookIn(in Book book);
Book addBookOut(out Book book);
Book addBookInout(inout Book book);
}
4.MyService.java 服务端
public class MyService extends Service {
private static final String TAG = "xiao";
private List<Book> books = new ArrayList<>();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IBookManagerStub();
}
public class IBookManagerStub extends IBookManager.Stub {
@Override
public List<Book> getBooks() {
return books;
}
@Override
public Book addBookIn(Book book) {
// 修改book属性,看客户端是否会有变化
Log.d(TAG, "addBookIn-->service book = " + book);
book.setName("大侠一枝花");
book.setPrice(888);
return book;
}
@Override
public Book addBookOut(Book book) {
// 修改book属性,看客户端是否会有变化
Log.d(TAG, "addBookOut-->service book = " + book);
book.setName("大侠一枝花");
book.setPrice(888);
return book;
}
@Override
public Book addBookInout(Book book) {
// 修改book属性,看客户端是否会有变化
Log.d(TAG, "addBookInout-->service book = " + book);
book.setName("大侠一枝花");
book.setPrice(888);
return book;
}
}
}
5.MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "xiao";
private IBookManager bookManager;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
bookManager = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.getBooks).setOnClickListener(this);
findViewById(R.id.addBookIn).setOnClickListener(this);
findViewById(R.id.addBookOut).setOnClickListener(this);
findViewById(R.id.addBookInout).setOnClickListener(this);
bindService(new Intent(this, MyService.class), connection, BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.getBooks:
try {
Log.d(TAG, "getBooks: " + bookManager.getBooks());
} catch (RemoteException e) {
}
break;
case R.id.addBookIn:
try {
Book book = new Book("addBookIn", 16);
bookManager.addBookIn(book);
Log.d(TAG, "addBookIn: client = " + book);
} catch (RemoteException e) {
}
break;
case R.id.addBookOut:
try {
Book book = new Book("addBookOut", 17);
bookManager.addBookOut(book);
Log.d(TAG, "addBookOut: client = " + book);
} catch (RemoteException e) {
}
break;
case R.id.addBookInout:
try {
Book book = new Book("addBookInout", 18);
bookManager.addBookInout(book);
Log.d(TAG, "addBookInout: client = " + book);
} catch (RemoteException e) {
}
break;
}
}
}
6.点击不同按钮,输出的log
2023-06-09 23:09:23.306 32501-32520 xiao D addBookIn-->service book = Book{name='addBookIn', price=16}
2023-06-09 23:09:23.306 32460-32460 xiao D addBookIn: client = Book{name='addBookIn', price=16}
2023-06-09 23:09:36.327 32501-32520 xiao D addBookOut-->service book = Book{name='null', price=0}
2023-06-09 23:09:36.327 32460-32460 xiao D addBookOut: client = Book{name='大侠一枝花', price=888}
2023-06-09 23:09:47.114 32501-32520 xiao D addBookInout-->service book = Book{name='addBookInout', price=18}
2023-06-09 23:09:47.115 32460-32460 xiao D addBookInout: client = Book{name='大侠一枝花', price=888}
参考链接:
https://blog.csdn.net/ly0724ok/article/details/117450121
https://blog.csdn.net/luoyanglizi/article/details/51958091