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" 标签:客户端 ↔ 服务端,双向传递,服务端修改后的值会更新客户端的变量。

相关推荐
安卓理事人5 小时前
安卓LinkedBlockingQueue消息队列
android
万能的小裴同学6 小时前
Android M3U8视频播放器
android·音视频
q***57747 小时前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober7 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
城东米粉儿8 小时前
关于ObjectAnimator
android
zhangphil9 小时前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
火柴就是我10 小时前
从头写一个自己的app
android·前端·flutter
lichong95111 小时前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端
用户693717500138411 小时前
14.Kotlin 类:类的形态(一):抽象类 (Abstract Class)
android·后端·kotlin
火柴就是我11 小时前
NekoBoxForAndroid 编译libcore.aar
android