验证 AIDL 参数方向

修改之前那个测试Binder通信的demo测试下。

定义Book类:

java 复制代码
package com.example.binderdemo;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {
    public String name;
    public int price;

    public Book() {}

    public Book(String name, int price) {
        this.name = name;
        this.price = price;
    }

    protected Book(Parcel in) {
        readFromParcel(in);
    }

    public void readFromParcel(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(price);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    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 String toString() {
        return "Book{name='" + name + "', price=" + price + "}";
    }
}
复制代码
Book.aidl:
复制代码
IDirectionTest.aidl文件如下:
java 复制代码
package com.example.binderdemo;

import com.example.binderdemo.Book;

interface IDirectionTest {
    int add(int a, int b);
    void testIn(in Book book);
    void testOut(out Book book);
    void testInout(inout Book book);
}
复制代码
服务端进程中的DirectionTestService 代码如下:
java 复制代码
package com.example.binderdemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class DirectionTestService extends Service {
    private static final String TAG = "DirectionTest";

    private final IDirectionTest.Stub binder = new IDirectionTest.Stub() {
        @Override
        public int add(int a, int b) throws RemoteException {
            Log.d(TAG, "add(" + a + "," + b + ") = " + (a + b));
            return a + b;
        }

        @Override
        public void testIn(Book book) throws RemoteException {
            Log.d(TAG, "[in] 收到: " + book);
            if (book != null) {
                book.name = "服务端偷偷改过的名字";
                book.price = 9999;
                Log.d(TAG, "[in] 服务端修改后: " + book);
            }
        }

        @Override
        public void testOut(Book book) throws RemoteException {
            Log.d(TAG, "[out] 收到: " + book);
            book.name = "服务端填充的名字";
            book.price = 42;
            Log.d(TAG, "[out] 服务端填充后: " + book);
        }

        @Override
        public void testInout(Book book) throws RemoteException {
            Log.d(TAG, "[inout] 收到: " + book);
            if (book != null) {
                book.name = "服务端改过的名字-inout";
                book.price = 8888;
                Log.d(TAG, "[inout] 服务端修改后: " + book);
            }
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return binder;
    }
}

客户端进程中的DirectionTestActivity 代码如下:

java 复制代码
package com.example.binderdemo;

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.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.ComponentActivity;

public class DirectionTestActivity extends ComponentActivity {
    private static final String TAG = "DirectionTest";
    private IDirectionTest remote;
    private final StringBuilder logBuilder = new StringBuilder();
    private TextView logView;

    private final ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            remote = IDirectionTest.Stub.asInterface(service);
            appendLog("✅ 已连接 Service: " + name);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            remote = null;
            appendLog("❌ 已断开: " + name);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LinearLayout root = new LinearLayout(this);
        root.setOrientation(LinearLayout.VERTICAL);
        int pad = dp(16);
        root.setPadding(pad, pad, pad, pad);

        Button btnBind = new Button(this);
        btnBind.setText("① 绑定 Service(新进程)");
        btnBind.setOnClickListener(v -> bindDirectionService());
        root.addView(btnBind);

        Button btnIn = new Button(this);
        btnIn.setText("② 测试 in 参数");
        btnIn.setOnClickListener(v -> testIn());
        root.addView(btnIn);

        Button btnOut = new Button(this);
        btnOut.setText("③ 测试 out 参数");
        btnOut.setOnClickListener(v -> testOut());
        root.addView(btnOut);

        Button btnInout = new Button(this);
        btnInout.setText("④ 测试 inout 参数");
        btnInout.setOnClickListener(v -> testInout());
        root.addView(btnInout);

        Button btnClear = new Button(this);
        btnClear.setText("清空日志");
        btnClear.setOnClickListener(v -> {
            logBuilder.setLength(0);
            if (logView != null) logView.setText("");
        });
        root.addView(btnClear);

        ScrollView scrollView = new ScrollView(this);
        logView = new TextView(this);
        logView.setTextSize(12f);
        logView.setPadding(0, dp(8), 0, 0);
        logView.setTextColor(0xFF333333);
        scrollView.addView(logView);
        root.addView(scrollView, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT,
                1f
        ));

        setContentView(root);
    }

    private void bindDirectionService() {
        if (remote != null) {
            appendLog("⚠️ 已经绑定过了");
            return;
        }
        Intent intent = new Intent(this, DirectionTestService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
        appendLog("🔗 bindService...");
    }

    private void testIn() {
        if (remote == null) { toast("请先绑定 Service"); return; }
        try {
            Book book = new Book("客户端原始书籍", 10);
            appendLog("===== testIn =====");
            appendLog("调用前: " + book);
            remote.testIn(book);
            appendLog("调用后: " + book);
            appendLog("🔍 in → 服务端修改丢失 ✅");
        } catch (RemoteException e) {
            Log.e(TAG, "testIn failed", e);
        }
    }

    private void testOut() {
        if (remote == null) { toast("请先绑定 Service"); return; }
        try {
            Book book = new Book("我在客户端初始化的", 1);
            appendLog("===== testOut =====");
            appendLog("调用前: " + book);
            remote.testOut(book);
            appendLog("调用后: " + book);
            appendLog("🔍 out → 客户端初始化被丢弃,拿到服务端填充的 ✅");
        } catch (RemoteException e) {
            Log.e(TAG, "testOut failed", e);
        }
    }

    private void testInout() {
        if (remote == null) { toast("请先绑定 Service"); return; }
        try {
            Book book = new Book("客户端原始书籍", 10);
            appendLog("===== testInout =====");
            appendLog("调用前: " + book);
            remote.testInout(book);
            appendLog("调用后: " + book);
            appendLog("🔍 inout → 服务端修改同步回来了 ✅");
        } catch (RemoteException e) {
            Log.e(TAG, "testInout failed", e);
        }
    }

    private void appendLog(String msg) {
        Log.d(TAG, msg);
        logBuilder.append(msg).append("\n\n");
        if (logView != null) {
            runOnUiThread(() -> logView.setText(logBuilder.toString()));
        }
    }

    private void toast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    private int dp(int px) {
        float density = getResources().getDisplayMetrics().density;
        return (int) (px * density + 0.5f);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (connection != null) {
            unbindService(connection);
        }
    }
}

AndroidManifest文件如下:

运行:

先点击绑定服务,再分别点击下面几个按钮:

ok. 可以看出in修饰的字段数据流是从客户端单向流向服务端,只读,服务端修改没用。

而out字段的数据流是从服务端流向客户端,而且服务端也获取不到客户端传的具体值,客户端传的值只是一个空架子给服务端填充的,正如日志:

而inout是双向数据流。

相关推荐
zh_xuan16 天前
aidl跨进程通信原理追踪
aidl·跨进程通信
zh_xuan17 天前
aidl 跨进程通信流程
aidl·跨进程通信
mmsx20 天前
Android 模块化三件套:轻量组件化、宿主插件双形态、AIDL 隔离
android·网络·组件化·aidl
熊猫钓鱼>_>1 个月前
Kotlin Multiplatform for OpenHarmony 实战:为 MVIKotlin 实现单向数据流适配
华为·ai编程·鸿蒙·openharmony·适配·数据流·mvkotlin
阿pin3 个月前
Android随笔-AIDL
android·开发语言·aidl
想你依然心痛4 个月前
AIoT网关架构:协议转换、边缘推理与云端协同
工业物联网·数据流·协议转换·aiot网关·边缘推理·云端协同·模型分发
想你依然心痛4 个月前
固件架构演进:从裸机到RTOS到Linux的决策矩阵——复杂度、实时性、成本三维解析
架构·嵌入式·响应式编程·数据流·流处理·资源控制·背压
想你依然心痛4 个月前
响应式编程在嵌入式中的实践——Reactive Streams与背压
嵌入式·响应式编程·数据流·流处理·资源控制·背压
与水同流5 个月前
Android13 AIDL HAL服务实现Demo
android·hal·aidl