Android AIDL in out inout tag 理解

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 方法:默认是同步的,客户端调用时会阻塞等待服务端返回。

    arduino 复制代码
           AIDL 默认情况下是同步的,客户端在调用远程方法时会阻塞,
           直到服务端完成操作并返回结果。换句话说,
           客户端必须等待服务端处理完毕并返回数据后,才能继续执行后续代码。
           这与 Java 中的 synchronized 方法类似,确保了方法调用的顺序性和数据一致性。
         
  • "in" 标签:客户端 → 服务端,服务端修改副本不影响客户端原始值(应为"in",而非"int tag")。

  • "out" 标签:服务端 → 客户端,服务端填充或修改的值会更新客户端的变量。

  • "inout" 标签:客户端 ↔ 服务端,双向传递,服务端修改后的值会更新客户端的变量。

相关推荐
每次的天空6 小时前
Android学习总结之算法篇四(字符串)
android·学习·算法
x-cmd7 小时前
[250331] Paozhu 发布 1.9.0:C++ Web 框架,比肩脚本语言 | DeaDBeeF 播放器发布 1.10.0
android·linux·开发语言·c++·web·音乐播放器·脚本语言
tangweiguo0305198710 小时前
Android BottomNavigationView 完全自定义指南:图标、文字颜色与选中状态
android
遥不可及zzz11 小时前
Android 应用程序包的 adb 命令
android·adb
无知的前端11 小时前
Flutter 一文精通Isolate,使用场景以及示例
android·flutter·性能优化
_一条咸鱼_11 小时前
Android Compose 入门之字符串与本地化深入剖析(五十三)
android
ModestCoder_12 小时前
将一个新的机器人模型导入最新版isaacLab进行训练(以unitree H1_2为例)
android·java·机器人
robin_suli13 小时前
Spring事务的传播机制
android·java·spring
鸿蒙布道师14 小时前
鸿蒙NEXT开发对象工具类(TS)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
Harrison_zhu15 小时前
Ubuntu18.04 编译 Android7.1代码报错
android