【Android】Binder(一)Binder的介绍和AIDL使用Binder的实例

Binder介绍

Android 中的 Binder 是一个进程间通信机制,它允许不同进程之间相互调用方法和传递数据。Binder 主要用于实现系统服务和应用程序之间的通信,以及实现 IPC(Inter-Process Communication,进程间通信)。

Binder 的核心是 Binder 驱动程序,它负责管理不同进程之间的通信。每个进程都可以创建自己的 Binder 对象作为服务提供者,也可以获取其他进程提供的 Binder 对象作为客户端使用。这些 Binder 对象都必须通过 Binder 驱动来进行跨进程通信。

机制来说: Binder是一种进程间通信机制
驱动来说: Binder是一个虚拟物理设备驱动
应用层来说:Binder是一个能发起通信的Java类

在 Android 开发中,我们可以通过 AIDL(Android Interface Definition Language)来定义自己的 Binder 接口,并实现相应的服务提供者和客户端。AIDL 生成的代码可用于在不同进程之间进行 IPC 通信。


Binder和其他进程通信方案的比较

Binder与传统IPC对比
Binder 共享内存 Socket
性能 需要拷贝一次 无需拷贝 需要拷贝两次
特点 基于C/S架构,易用性高 控制复杂,易用性差 基于C/S 架构,作为一款通用接口,其传输效率低,开销大
安全性 为每个APP分配UID,同时支持实名和匿名 依赖上层协议访问,接入点是开放的,不安全

Binder 机制组成

Binder(IInterface 接口) 该类定义了远程服务的接口,用于在客户端和服务端之间通信
IBinder 接口 该类是 Binder 的实现类,提供了轻量级的进程间通信能力
ServiceManager 该类提供了一种注册和查找 Binder 服务的机制
Binder驱动程序 该驱动程序是 Binder 进程间通信的核心,用于管理 Binder 连接、数据传输、线程等操作

Binder 机制为 Android 开发提供了一种高效、灵活的进程间通信方式,使得应用程序能够更加方便地进行跨进程数据共享和调用远程服务。


AIDL与 Binder 的关系

AIDL(Android Interface Definition Language,Android 接口定义语言)是 Android 系统中用于实现进程间通信(IPC)的一种机制。AIDL 可以帮助开发者在不同的应用程序之间或不同进程之间实现远程方法调用,从而实现跨进程的数据共享和交互。

Binder 是 Android 系统中用于实现 IPC 的核心驱动程序,它可以为 AIDL 定义的接口提供底层支持。Binder 驱动程序通过管理进程间的连接、数据传输等操作,完成了基于 AIDL 的进程间通信功能。

AIDL 与 Binder 的关系可以理解为:AIDL 提供了描述跨进程间通信接口的语言,而 Binder 则是实现 AIDL 接口调用的内部框架。在 Android 应用程序中,一般都是通过在 AIDL 文件中定义跨进程访问的接口,并通过 Binder 实现这些接口,从而实现应用程序之间的通信。


使用Binder进行进程间通信例子

首先,我们需要创建两个端,分别是服务端客户端

服务端

创建一个项目,名字叫MyBinderService
在这里插入图片描述
然后创建AIDL文件

注意:aidl文件夹是需要自己创建的,且文件夹在main文件夹下面,和 java文件夹平级

在这里插入图片描述

当我们在aidl文件夹创建aidl文件时,遇到这个错误:
在这里插入图片描述

这个提示意味着需要在 build.gradle 文件中设置 buildFeatures.aidl 为 true 才能使用 AIDL 技术
在这里插入图片描述

 buildFeatures {
    
    
    aidl = true
 }

此时我们看到上面的aidl文件夹也变颜色了。

服务端应用程序中,我们需要先定义一个 AIDL 接口,为客户端提供数据获取的方法。
在这里插入图片描述
然后我们需要实现接口方法并注册到系统中,这里使用一个 Service 来实现:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完整代码

public class MyService extends Service {
    
    

    private static final int NOTIFICATION_ID = 1001; // 前台通知 ID

    private final IBinder mBinder = new MyBinder();

    @Override
    public IBinder onBind(Intent intent) {
    
    
        Log.e("Binder响应:", "已经绑定到Binder上面了");
        return mBinder;
    }

    static class MyBinder extends IMyService.Stub {
    
    
        @Override
        public long getCurrentTimestamp() throws RemoteException {
    
    
            long time = System.currentTimeMillis();
            Log.e("Binder数据:", "服务端的时间戳:" + time);
            return time;
        }
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
            NotificationManager manager = getSystemService(NotificationManager.class);
            NotificationChannel channel = new NotificationChannel(
                    "channel_id",
                    "前台通知渠道名称",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            manager.createNotificationChannel(channel);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel_id")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentTitle("Binder服务端")
                .setContentText("测试Binder的前台Service");

        startForeground(NOTIFICATION_ID, builder.build());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
    
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                Log.e("Binder的服务", "在Service做耗时任务的话,请使用子线程,因为Service是在主线程运行的");
                Log.e("Binder的服务", "此时的线程:" + Thread.currentThread().getName());
            }
        }).start();
        return START_STICKY;
    }
}

主页面:
在这里插入图片描述
完整代码:

public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tvStartService = findViewById(R.id.tv_startService);
        TextView tvStopService = findViewById(R.id.tv_stopService);
        tvStartService.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                Intent intent = new Intent(MainActivity.this, MyService.class);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
                    startForegroundService(intent);
                } else {
    
    
                    startService(intent);
                }
            }
        });
        tvStopService.setOnClickListener(new View.OnClickListener() {
    
    

            @Override
            public void onClick(View view) {
    
    
                Intent intent = new Intent(MainActivity.this, MyService.class);
                stopService(intent);
            }
        });
    }
}

注意:

需要在AndroidManifest.xml文件中添加这个权限:

  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

不然会报错:

Permission Denial: startForeground from pid=16363, uid=10134 requires android.permission.FOREGROUND_SERVICE

同时在此文件里面注册完善MyService文件

<service
     android:name=".MyService"
     android:enabled="true"
     android:exported="true">
     <intent-filter>
           <action android:name="com.shsany.mybinderservice"/>
     </intent-filter>
</service>

此时配置好之后,要记得Build一下项目,这样Android Studio会自动生成AIDL的代码
在这里插入图片描述
生成的代码:
在这里插入图片描述

客户端

在客户端需要配置的和服务端的步骤方法一样,不再叙述。

注意:

在客户端的AIDL文件不要自己重新写,要从服务端复制过来,保证两边是一样的。

服务端

在这里插入图片描述

客户端

在这里插入图片描述

这两个端的这个文件无论是包名还是里面的代码都要一样的,所以直接复制最好。

客户端命名为MyBinder,只有一个文件

在这里插入图片描述

MainActivity代码

在这里插入图片描述
在这里插入图片描述
完整代码:

public class MainActivity extends AppCompatActivity {
    
    
    private IMyService mMyService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tvGetData = findViewById(R.id.tv_getBinderData);

        Intent intent = new Intent();
        intent.setAction("com.shsany.mybinderservice");
        intent.setPackage("com.shsany.mybinderservice");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

        tvGetData.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                try {
    
    
                    // 在使用服务端方法时,先判断服务是否可用
                    if (mMyService != null) {
    
    
                        long currentTime = mMyService.getCurrentTimestamp();
                        Log.e("Binder数据:", "从服务端接收到的时间戳:" + currentTime);
                    } else {
    
    
                        Log.e("Binder数据:", "无法连接到服务端");
                    }
                } catch (Exception e) {
    
    
                    Log.e("Binder错误:", e.getMessage());
                }
            }
        });
    }

    private final ServiceConnection mConnection = new ServiceConnection() {
    
    
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    
    
            //远程Service绑定
            Log.e("Binder数据:", "已经连接到服务端");
            mMyService = IMyService.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
    
    
            //Service被意外销毁
            Log.e("Binder数据:", "服务端被意外销毁");
            mMyService = null;
        }
    };
}
测试

首先需要先启动服务端
在这里插入图片描述
然后再启动客户端
在这里插入图片描述
此时连接到服务端之后,服务端的显示
在这里插入图片描述
然后再客户端上面调用getCurrentTimestamp方法获取数据:
在这里插入图片描述
此时再看服务端
在这里插入图片描述
至此,一个简单的AIDL使用Binder跨进程通信就完成了。

猜你喜欢

转载自blog.csdn.net/qq_43358469/article/details/130981992