今天学习了Android Binder相关的内容,发现大部分博客聚焦于讲原理,少有文章详细讲解怎么写代码。本文主要提供一个简单的Binder的例程,手把手展示整个项目的创建流程,所有完整代码均提供复制粘贴,尽可能帮助Android新手复现成功。
在进行项目创建之前,建议最好简单了解一下Binder的流程,不需要太深入原理,因为学知识要由浅到深。在学习完Binder流程后,通过这个简单项目你可以加深理解,然后再去深入原理会更轻松。

废话有点多了,下面开始吧。

1、新建一个空项目,然后在main文件夹下new一个aidl,发现new不了。这个提示说明你需要在项目的构建配置中启用 AIDL 支持。在现代 Android 项目中,AIDL 默认是不启用的,需要手动配置。
图片: https://odocs.myoas.com/uploader/f/yio9op5WPPgd0wju.png?source=all&accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3NTgxMDQ3OTcsImZpbGVHVUlEIjoid1YzVlZtRFlFTnV3TzEzeSIsImlhdCI6MTc1ODEwNDE5NywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwicGFhIjoiYWxsOmFsbDo4ZmUzNjY3OC04NWE4LTQ0YWItYWQ4MC1lYWYyOTEwMjA3MDAiLCJ1c2VySWQiOjMzOTYzMH0.SwETiNJoBZRG2OBexKLj1grsdwaIbCzJo_tOH2XFjEE

2、在app/build.gradle中添加如下代码,表示支持aidl配置
图片: https://odocs.myoas.com/uploader/f/mEyCmtvuWgz7oXX1.png?source=all&accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3NTgxMDQ3OTcsImZpbGVHVUlEIjoid1YzVlZtRFlFTnV3TzEzeSIsImlhdCI6MTc1ODEwNDE5NywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwicGFhIjoiYWxsOmFsbDo4ZmUzNjY3OC04NWE4LTQ0YWItYWQ4MC1lYWYyOTEwMjA3MDAiLCJ1c2VySWQiOjMzOTYzMH0.SwETiNJoBZRG2OBexKLj1grsdwaIbCzJo_tOH2XFjEE

3、现在重新在main下面新建一个aidl,我这边命名为ICalculator,自动生成文件如下
图片: https://odocs.myoas.com/uploader/f/BjqMWqE3lcSBqLew.png?source=all&accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3NTgxMDQ3OTcsImZpbGVHVUlEIjoid1YzVlZtRFlFTnV3TzEzeSIsImlhdCI6MTc1ODEwNDE5NywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwicGFhIjoiYWxsOmFsbDo4ZmUzNjY3OC04NWE4LTQ0YWItYWQ4MC1lYWYyOTEwMjA3MDAiLCJ1c2VySWQiOjMzOTYzMH0.SwETiNJoBZRG2OBexKLj1grsdwaIbCzJo_tOH2XFjEE

4、系统会自动生成一个basicType方法(用于测试基本数据类型传递),我把这个方法删除掉,不然后面Stub类中这个方法也需要被实现。在上面的aidl中定义Service中的调用接口,我这边以加法、减法函数为例

// ICalculator.aidl
package com.example.binderdemo;

// Declare any non-default types here with import statements

interface ICalculator {
    // 定义加法方法
    int add(int a, int b);
    // 定义减法方法
    int subtract(int a, int b);
}

5、点击菜单栏的Build->Make Project,让系统生成AIDL对应的Java文件。自动生成的Java文件在下图的路径中,发现真实的ICalculator非常复杂,而AIDL就是帮我们简化这个步骤的。
图片: https://odocs.myoas.com/uploader/f/kzcDtz8duYcI6Vjr.png?source=all&accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3NTgxMDQ3OTcsImZpbGVHVUlEIjoid1YzVlZtRFlFTnV3TzEzeSIsImlhdCI6MTc1ODEwNDE5NywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwicGFhIjoiYWxsOmFsbDo4ZmUzNjY3OC04NWE4LTQ0YWItYWQ4MC1lYWYyOTEwMjA3MDAiLCJ1c2VySWQiOjMzOTYzMH0.SwETiNJoBZRG2OBexKLj1grsdwaIbCzJo_tOH2XFjEE

6、然后创建一个CalculatorService.java。在这里面创建Binder对象以及实现我们定义的AIDL接口中加法和减法的方法

//CalculatorService.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;

// 服务端Service,实现我们定义的AIDL接口
public class CalculatorService extends Service {
    private static final String TAG = "BinderDemo";

    // 创建Binder对象,实现AIDL接口定义的方法
    private final ICalculator.Stub mBinder = new ICalculator.Stub() {
        @Override
        public int add(int a, int b) throws RemoteException {
            // 打印当前处理请求的线程信息,验证Binder线程池
            Log.d(TAG, "add() called in thread: " + Thread.currentThread().getName());
            return a + b;
        }

        @Override
        public int subtract(int a, int b) throws RemoteException {
            // 打印当前处理请求的线程信息,验证Binder线程池
            Log.d(TAG, "subtract() called in thread: " + Thread.currentThread().getName());
            return a - b;
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        // 打印服务所在进程ID
        Log.d(TAG, "CalculatorService onCreate, PID: " + android.os.Process.myPid());
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 返回Binder对象,供客户端绑定
        return mBinder;
    }
}

7、我这边把Service和Client放在同一个应用中,它们是在同一个进程中的,为了测试Binder的跨进程通信功能,在manifest文件中将CalculatorService配置为运行在独立线程

android:process="com.example.binderdemo.remote"

图片: https://odocs.myoas.com/uploader/f/GgDsxpnrjP2exazW.png?source=all&accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3NTgxMDQ3OTcsImZpbGVHVUlEIjoid1YzVlZtRFlFTnV3TzEzeSIsImlhdCI6MTc1ODEwNDE5NywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwicGFhIjoiYWxsOmFsbDo4ZmUzNjY3OC04NWE4LTQ0YWItYWQ4MC1lYWYyOTEwMjA3MDAiLCJ1c2VySWQiOjMzOTYzMH0.SwETiNJoBZRG2OBexKLj1grsdwaIbCzJo_tOH2XFjEE

8、然后修改客户端的界面布局,activity_main.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"
    android:padding="16dp"
    android:gravity="center">

    <EditText
        android:id="@+id/et_num1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入第一个数字"
        android:inputType="number" />

    <EditText
        android:id="@+id/et_num2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:hint="输入第二个数字"
        android:inputType="number" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_add"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="" />

        <Button
            android:id="@+id/btn_subtract"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="" />
    </LinearLayout>

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:textSize="18sp"
        android:textAlignment="center"
        android:text="结果将显示在这里" />

</LinearLayout>

9、最后,编写客户端MainActivity的代码如下,具体分析在代码注释中

package com.example.binderdemo;

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.EditText;
import android.widget.TextView;

// 客户端Activity,负责绑定服务并调用远程方法
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "BinderDemo";
    
    // AIDL接口实例,用于调用远程方法
    private ICalculator mCalculator;
    
    // 界面元素
    private EditText mNum1EditText;
    private EditText mNum2EditText;
    private TextView mResultTextView;
    
    // 服务连接对象,用于绑定服务
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 绑定成功,获取AIDL接口实例
            mCalculator = ICalculator.Stub.asInterface(service);
            Log.d(TAG, "服务绑定成功");
        }
        
        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 服务断开连接
            mCalculator = null;
            Log.d(TAG, "服务断开连接");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 打印客户端进程ID
        Log.d(TAG, "MainActivity onCreate, PID: " + android.os.Process.myPid());
        
        // 初始化界面元素
        mNum1EditText = findViewById(R.id.et_num1);
        mNum2EditText = findViewById(R.id.et_num2);
        mResultTextView = findViewById(R.id.tv_result);
        Button addButton = findViewById(R.id.btn_add);
        Button subtractButton = findViewById(R.id.btn_subtract);
        
        // 绑定服务
        Intent intent = new Intent(this, CalculatorService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
        
        // 加法按钮点击事件
        addButton.setOnClickListener(v -> calculate('+'));
        
        // 减法按钮点击事件
        subtractButton.setOnClickListener(v -> calculate('-'));
    }
    
    // 执行计算操作
    private void calculate(char operation) {
        // 检查是否已绑定服务
        if (mCalculator == null) {
            mResultTextView.setText("服务未连接");
            return;
        }
        
        try {
            // 获取输入的数字
            int num1 = Integer.parseInt(mNum1EditText.getText().toString());
            int num2 = Integer.parseInt(mNum2EditText.getText().toString());
            int result = 0;
            
            // 根据操作类型调用不同的远程方法
            switch (operation) {
                case '+':
                    result = mCalculator.add(num1, num2);
                    break;
                case '-':
                    result = mCalculator.subtract(num1, num2);
                    break;
            }
            
            // 显示结果
            mResultTextView.setText(String.valueOf(result));
            
        } catch (NumberFormatException e) {
            mResultTextView.setText("请输入有效的数字");
        } catch (RemoteException e) {
            mResultTextView.setText("远程调用失败");
            e.printStackTrace();
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 解绑服务
        unbindService(mConnection);
    }
}

然后就可以运行了!!!如果我的文章帮助到你,就点点赞和收藏吧

Logo

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

更多推荐