在 Android 中使用 Binder 通信,最常用和最推荐的方式是通过 AIDL (Android Interface Definition Language)。AIDL 是一种接口定义语言,它可以帮你快速生成 Binder 通信所需的 Java 代码(包括客户端的代理 Proxy 和服务端的桩 Stub),从而屏蔽掉底层复杂的 Binder 驱动交互细节。

下面我将以一个简单的 "加法服务" 为例,带你一步步实现跨进程通信。

核心步骤概览

使用 AIDL 进行 Binder 通信通常分为以下几步:

  1. 创建 .aidl 文件:定义客户端和服务端之间的通信接口。
  2. 实现服务端 (Server):创建一个 Service,在其中实现你定义的 AIDL 接口。
  3. 实现客户端 (Client):绑定服务端的 Service,并通过 AIDL 接口调用服务端的方法。

第一步:创建 AIDL 文件

AIDL 文件定义了服务端能提供哪些方法,以及这些方法的参数和返回值类型。

  1. 在 Android Studio 中,右键点击你的 app 模块 -> New -> AIDL -> AIDL File
  2. 给文件命名,例如 ICalculator.aidl
  3. 在生成的文件中,定义你的接口。我们来定义一个简单的加法方法。

ICalculator.aidl 示例代码:

java

运行

// ICalculator.aidl
package com.example.myapplication; // 替换成你的包名

// 声明一个接口
interface ICalculator {
    /**
     * 计算两个数的和
     * @param a 第一个数
     * @param b 第二个数
     * @return 两数之和
     */
    int add(int a, int b);
}

注意

  • AIDL 支持的基本数据类型:bytecharintlongfloatdoublebooleanString
  • 对于复杂类型(如自定义对象),需要额外的处理(例如 Parcelable),这里我们先从简单的开始。
  1. 同步项目:编写完 .aidl 文件后,点击 Android Studio 的 "Sync Project with Gradle Files" 按钮。Gradle 会自动根据 .aidl 文件在 build/generated/source/aidl/ 目录下生成对应的 Java 接口文件。

第二步:实现服务端 (Server)

服务端需要创建一个 Service,并在其中实现由 AIDL 生成的接口。

  1. 创建一个新的 Java/Kotlin 类,例如 CalculatorService.java,并让它继承自 Service
  2. 在 Service 中,创建一个内部类,让它继承自 ICalculator.StubStub 类是 AIDL 生成的,它继承自 Binder,并实现了你的 ICalculator 接口。你需要重写接口中定义的方法。
  3. 在 onBind() 方法中,返回这个 Stub 的实例。

CalculatorService.java 示例代码:

java

运行

package com.example.myapplication;

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

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

    // 创建 Stub 实例,实现 AIDL 接口
    private final ICalculator.Stub mBinder = new ICalculator.Stub() {
        @Override
        public int add(int a, int b) throws RemoteException {
            Log.d(TAG, "Received from client: a=" + a + ", b=" + b);
            // 在这里实现具体的业务逻辑
            return a + b;
        }
    };

    public CalculatorService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 返回 Stub 对象,客户端将通过它与服务端通信
        Log.d(TAG, "Service bound by client.");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "Service unbound by client.");
        return super.onUnbind(intent);
    }
}
  1. 在 AndroidManifest.xml 中注册 Service:为了让其他应用(或进程)能找到并绑定你的服务,必须在清单文件中声明它,并确保 exported="true"

    xml

    <manifest ...>
        <application ...>
            <!-- 其他组件 -->
    
            <service
                android:name=".CalculatorService"
                android:enabled="true"
                android:exported="true">
                <!-- 为服务添加一个 Intent Filter,方便客户端隐式启动 -->
                <intent-filter>
                    <action android:name="com.example.myapplication.CALCULATOR_SERVICE" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </service>
    
        </application>
    </manifest>
    

第三步:实现客户端 (Client)

客户端需要绑定到服务端的 Service,然后获取 IBinder 对象,并将其转换为 AIDL 接口的实例,最后调用其方法。

这个客户端可以是同一个应用中的另一个组件(如 Activity),也可以是另一个完全独立的应用。

MainActivity.java 示例代码 (作为客户端):

java

运行

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
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 android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    // AIDL 接口的实例
    private ICalculator mCalculatorService;

    // 标志位,判断服务是否已连接
    private boolean mServiceConnected = false;

    // ServiceConnection 对象,用于监听与服务的连接状态
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "Service connected.");
            // 将 IBinder 对象转换为 AIDL 接口
            mCalculatorService = ICalculator.Stub.asInterface(service);
            mServiceConnected = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "Service disconnected.");
            mCalculatorService = null;
            mServiceConnected = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button bindButton = findViewById(R.id.bind_button);
        Button unbindButton = findViewById(R.id.unbind_button);
        Button callButton = findViewById(R.id.call_button);

        // 绑定服务
        bindButton.setOnClickListener(v -> {
            Intent intent = new Intent();
            // 隐式 Intent:通过 Action 找到服务
            intent.setAction("com.example.myapplication.CALCULATOR_SERVICE");
            // 在 Android 5.0+ 中,隐式绑定服务需要指定包名,以增加安全性
            intent.setPackage("com.example.myapplication"); // 服务端应用的包名
            bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
        });

        // 解绑服务
        unbindButton.setOnClickListener(v -> {
            if (mServiceConnected) {
                unbindService(mServiceConnection);
                mServiceConnected = false;
            }
        });

        // 调用服务端方法
        callButton.setOnClickListener(v -> {
            if (!mServiceConnected || mCalculatorService == null) {
                Toast.makeText(this, "请先绑定服务!", Toast.LENGTH_SHORT).show();
                return;
            }

            try {
                // 跨进程调用服务端的 add 方法
                int result = mCalculatorService.add(10, 20);
                Toast.makeText(this, "10 + 20 = " + result, Toast.LENGTH_SHORT).show();
                Log.d(TAG, "Result from service: " + result);
            } catch (RemoteException e) {
                // 如果服务端崩溃或断开连接,会抛出 RemoteException
                e.printStackTrace();
                Toast.makeText(this, "调用服务失败!", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 当 Activity 销毁时,确保解绑服务,防止内存泄漏
        if (mServiceConnected) {
            unbindService(mServiceConnection);
        }
    }
}

布局文件 (activity_main.xml) 可以很简单,包含三个按钮即可。


总结与运行

  1. 运行服务端:将你的应用安装到手机或模拟器上。启动 MainActivity,点击 "绑定服务" 按钮。
  2. 观察 Logcat:你会看到 CalculatorService 打印出 "Service bound by client." 的日志。
  3. 调用方法:点击 "调用服务方法" 按钮。客户端会通过 Binder 调用服务端的 add 方法,并显示结果 "10 + 20 = 30"。服务端的 Logcat 也会打印出收到的参数。
  4. 解绑服务:点击 "解绑服务" 按钮,连接断开。

通过以上步骤,你就成功地实现了一次基于 Binder (AIDL) 的跨进程通信。这个模式是 Android 系统中非常核心和基础的部分,理解它对于掌握 Android 开发至关重要。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐