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跨进程通信就完成了。