1. 自定义 Parcelable 类:Person
首先,定义一个实现了 Parcelable 接口的 Person 类,以便在 AIDL 中传递。
java
// Person.java
package com.example.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
protected Person(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
2. AIDL 接口定义
定义 AIDL 接口 IPersonManager.aidl,包含三个方法,分别使用 "in"、"out" 和 "inout" 标签。
java
// IPersonManager.aidl
package com.example.aidl;
import com.example.aidl.Person;
interface IPersonManager {
// 使用 "in" 标签:客户端传递 Person 对象,服务器端读取但不修改
void printPerson(in Person person);
// 使用 "out" 标签:服务器端创建一个新的 Person 对象并返回给客户端
void getPerson(out Person person);
// 使用 "inout" 标签:客户端传递 Person 对象,服务器端修改并返回
void updatePersonAge(inout Person person);
}
注意:需要在同一包下定义 Person.aidl,声明 Person 为 Parcelable 类型:
java
// Person.aidl
package com.example.aidl;
parcelable Person;
3. 服务器端实现
在服务器端,创建一个服务类 PersonService,实现 IPersonManager 接口。
java
// PersonService.java
package com.example.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class PersonService extends Service {
private static final String TAG = "PersonService";
private final IPersonManager.Stub binder = new IPersonManager.Stub() {
@Override
public void printPerson(Person person) throws RemoteException {
Log.d(TAG, "Received person: " + person.toString());
// 尝试修改 person,但由于是 "in",修改不会影响客户端
person.setAge(100);
Log.d(TAG, "Modified person in server: " + person.toString());
}
@Override
public void getPerson(Person person) throws RemoteException {
// 创建一个新的 Person 对象并赋值
person.setName("New Person");
person.setAge(25);
Log.d(TAG, "Created new person: " + person.toString());
}
@Override
public void updatePersonAge(Person person) throws RemoteException {
Log.d(TAG, "Received person: " + person.toString());
// 修改 person 的年龄
person.setAge(person.getAge() + 10);
Log.d(TAG, "Updated person age: " + person.toString());
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
别忘了在 AndroidManifest.xml 中注册服务:
xml
<service android:name=".PersonService">
<intent-filter>
<action android:name="com.example.aidl.PersonService" />
</intent-filter>
</service>
4. 客户端实现
在客户端,实现绑定服务并调用 AIDL 方法的逻辑。这里以一个简单的 Activity 为例。
java
// MainActivity.java
package com.example.aidlclient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import com.example.aidl.IPersonManager;
import com.example.aidl.Person;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private IPersonManager personManager;
private boolean isBound = false;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
personManager = IPersonManager.Stub.asInterface(service);
isBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
personManager = null;
isBound = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bindButton = findViewById(R.id.bind_button);
bindButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.aidl", "com.example.aidl.PersonService"));
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});
Button testButton = findViewById(R.id.test_button);
testButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isBound) {
try {
// 测试 "in" 标签
Person personIn = new Person("Alice", 30);
Log.d(TAG, "Before printPerson: " + personIn.toString());
personManager.printPerson(personIn);
Log.d(TAG, "After printPerson: " + personIn.toString());
// 测试 "out" 标签
Person personOut = new Person("", 0); // 占位符
Log.d(TAG, "Before getPerson: " + personOut.toString());
personManager.getPerson(personOut);
Log.d(TAG, "After getPerson: " + personOut.toString());
// 测试 "inout" 标签
Person personInOut = new Person("Bob", 40);
Log.d(TAG, "Before updatePersonAge: " + personInOut.toString());
personManager.updatePersonAge(personInOut);
Log.d(TAG, "After updatePersonAge: " + personInOut.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isBound) {
unbindService(connection);
}
}
}
对应的布局文件 activity_main.xml:
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/bind_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bind Service" />
<Button
android:id="@+id/test_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test AIDL" />
</LinearLayout>
5. 运行结果
运行服务器端和客户端应用,点击"Bind Service"绑定服务后,点击"Test AIDL"按钮,你会在日志中看到类似以下输出:
- "in" 标签:
js
- **客户端**:Before printPerson: Person{name='Alice', age=30}
- **服务器端**:Received person: Person{name='Alice', age=30}
- **服务器端**:Modified person in server: Person{name='Alice', age=100}(修改不影响客户端)
- **客户端**:After printPerson: Person{name='Alice', age=30}(未修改)
"out" 标签:
js
客户端 :Before getPerson: Person{name='', age=0}
服务器端:Created new person: Person{name='New Person', age=25}`
客户端 :After getPerson: Person{name='New Person', age=25}(被更新)`
"inout" 标签:
js
- 客户端:
Before updatePersonAge: Person{name='Bob', age=40}
After updatePersonAge: Person{name='Bob', age=50}(被更新)
服务器端:
Received person: Person{name='Bob', age=40}
Updated person age: Person{name='Bob', age=50}
6. 总结
通过这个案例,我们可以看到:
-
AIDL 方法:默认是同步的,客户端调用时会阻塞等待服务端返回。
arduinoAIDL 默认情况下是同步的,客户端在调用远程方法时会阻塞, 直到服务端完成操作并返回结果。换句话说, 客户端必须等待服务端处理完毕并返回数据后,才能继续执行后续代码。 这与 Java 中的 synchronized 方法类似,确保了方法调用的顺序性和数据一致性。
-
"in" 标签:客户端 → 服务端,服务端修改副本不影响客户端原始值(应为"in",而非"int tag")。
-
"out" 标签:服务端 → 客户端,服务端填充或修改的值会更新客户端的变量。
-
"inout" 标签:客户端 ↔ 服务端,双向传递,服务端修改后的值会更新客户端的变量。