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

相关推荐
花花鱼1 小时前
android studio 设置让开发更加的方便,比如可以查看变量的类型,参数的名称等等
android·ide·android studio
alexhilton3 小时前
为什么你的App总是忘记所有事情
android·kotlin·android jetpack
AirDroid_cn6 小时前
OPPO手机怎样被其他手机远程控制?两台OPPO手机如何相互远程控制?
android·windows·ios·智能手机·iphone·远程工作·远程控制
尊治6 小时前
手机电工仿真软件更新了
android
xiangzhihong89 小时前
使用Universal Links与Android App Links实现网页无缝跳转至应用
android·ios
车载应用猿9 小时前
基于Android14的CarService 启动流程分析
android
没有了遇见10 小时前
Android 渐变色实现总结
android
雨白13 小时前
Jetpack系列(四):精通WorkManager,让后台任务不再失控
android·android jetpack
mmoyula14 小时前
【RK3568 驱动开发:实现一个最基础的网络设备】
android·linux·驱动开发
sam.li15 小时前
WebView安全实现(一)
android·安全·webview