当前位置:网站首页>binder通信实现
binder通信实现
2022-08-04 08:03:00 【目标是技术宅】
本文为Java实现,基本流程与参考链接一致,代码和可运行的APK文件在github文件夹中
service端
目录结构
service端实际架构如下(与android studio中的目录显示不太一样)
aidl是Android 接口定义语言,定义数据接口
通过android studio的build功能,可以自动生成IMyAidlInterface.aidl的java接口实现,位置在:
service端需要实现aidl中定义的RequestData类和ResponseData类,以及MyService类调用IMyAidlInterface.Stub对象的asBinder方法
aidl文件
RequestData.aidl
parcelable定义了binder在用户空间使用的数据模型
package com.example.appbinder;
parcelable RequestData;
ResponseData.aidl
package com.example.appbinder;
parcelable ResponseData;
IMyAidlInterface.aidl
interface定义了一个binder service,在编译中aidl会自动生成两类binder对象:
- Stub类继承了Binder,应该在服务端实现
- Proxy类应该在客户端实现
interface中函数的参数
- 可以是原始类型、parcelable、Binder对象
- 可以通过in、out、inout来修饰
- in表示参数中的数据只从客户端移动到服务端,服务端只接收其中的数据不做更改
- out则是相反的情况,在客户端给服务端发消息时,该参数只是一个占位符,不会被序列化;在服务端返回消息时,该占位符才会被初始化
- inout是两者的结合
package com.example.appbinder;
import com.example.appbinder.RequestData;
import com.example.appbinder.ResponseData;
interface IMyAidlInterface {
ResponseData send(in RequestData request);
}
aidl文件编译
配置app的build.gradle
直接编译,在刚才的位置可以找到生成的IMyAidlInterface.java文件
java文件
RequestData.java
实现Parcelable接口,有一个字符串作为私有变量,一个toString函数来打印RequestData类数据
package com.example.appbinder;
import android.os.Parcel;
import android.os.Parcelable;
import static android.os.UserHandle.readFromParcel;
public class RequestData implements Parcelable {
private String s;
protected RequestData(Parcel in) {
readFromParcel(in);
}
public RequestData(String s) {
this.s = s;
}
/** 将数据写入到Parcel **/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(s);
}
/** 从Parcel中读取数据 **/
public void readFromParcel(Parcel in){
s = in.readString();
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<RequestData> CREATOR = new Creator<RequestData>() {
@Override
public RequestData createFromParcel(Parcel in) {
return new RequestData(in);
}
@Override
public RequestData[] newArray(int size) {
return new RequestData[size];
}
};
@Override
public String toString() {
return s;
}
}
ResponseData.java
package com.example.appbinder;
import android.os.Parcel;
import android.os.Parcelable;
public class ResponseData implements Parcelable {
private String s;
protected ResponseData(Parcel in) {
readFromParcel(in);
}
public ResponseData(String s) {
this.s = s;
}
/** 将数据写入到Parcel **/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(s);
}
/** 从Parcel中读取数据 **/
public void readFromParcel(Parcel in){
s = in.readString();
}
@Override
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<ResponseData> CREATOR = new Parcelable.Creator<ResponseData>() {
@Override
public ResponseData createFromParcel(Parcel in) {
return new ResponseData(in);
}
@Override
public ResponseData[] newArray(int size) {
return new ResponseData[size];
}
};
@Override
public String toString() {
return s;
}
}
MyService.java
Service在onBind方法返回Binder实体,最后会注册到ServiceManager的映射表,供客户端调用;binder实体在IMyAidlInterface中已经被定义,只需要实现自定义的send方法
package com.example.appbinder;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public ResponseData send(RequestData data) throws RemoteException {
Log.i("service","[RemoteService] receive "+ data.toString());
return new ResponseData("i'm service message");
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
MainActivity.java
启动service
package com.example.appbinder;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent in = new Intent();
in.setComponent(new ComponentName("com.example.appbinder", "com.example.appbinder.MyService"));
//启动service
startService(in);
}
}
清单文件
注册service
<service android:name=".MyService" android:exported="true">
<intent-filter>
<action android:name="com.example.appbinder.service" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
client端
目录结构
aidl文件夹与server端一致,里面包含RequestData.aidl、ResponseData.aidl、IMyAidlInterface.aidl三个文件
java文件夹下com.example.appbinder文件夹中,包含RequestData.java、ResponseData.java两个文件
注意上面的文件与server端的完全相同,且包名也一致
aidl文件编译
同样需要配置app的build.gradle
同样对IMyAidlInterface.aidl进行编译得到IMyAidlInterface.java文件
MainActivity中bindservice
实现bindservice,同时定义一个按钮,实现send发送消息和回显消息
package com.example.appbinderclient;
import androidx.appcompat.app.AppCompatActivity;
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.widget.TextView;
import com.example.appbinder.IMyAidlInterface;
import com.example.appbinder.RequestData;
import com.example.appbinder.ResponseData;
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface mAidlInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// bind上服务端的service
Intent in = new Intent();
in.setComponent(new ComponentName("com.example.appbinder", "com.example.appbinder.MyService"));
bindService(in, connD, Context.BIND_AUTO_CREATE);
}
private ServiceConnection connD = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mAidlInterface = null;
}
};
// 发送消息和回显消息
public void send(android.view.View v) {
try {
ResponseData data = mAidlInterface.send(new RequestData("hello i'm client"));
TextView textView = findViewById(R.id.textview);
textView.setText(data.toString());
} catch (Exception e) {
}
}
}
实现效果
先打开server端:
然后打开客户端:
点击客户端的send按钮,客户端收到信息:
同时server端后台日志也说明收到了client端信息:
AIDL源码分析
查看生成的IMyAidlInterface.java文件
IMyAidlInterface实现了android的IInterface接口:
Stub类继承了Binder类,为服务端
构造器Stub(),创建stub并附加到接口上
asInterface(Ibinder obj),提供给客户端调用
输入obj可以是Binder也可以是BinderProxy,客户端可以通过调用bindService bind to RemoteService,服务端自己也可以bind to RemoteService,两种情况下都会在onServiceConnected得到一个IBinder,但是如果是客户端IBinder类型为BinderProxy,如果是服务端类型为Binder。
public static com.example.appbinder.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } // DESCRIPTOR就是com.example.appbinder.IMyAidlInterface android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.example.appbinder.IMyAidlInterface))) { return ((com.example.appbinder.IMyAidlInterface)iin); } return new com.example.appbinder.IMyAidlInterface.Stub.Proxy(obj); }
onTransact是核心,Client调用aidl接口后,最终onTransact会接收到消息,并调用我们自己定义的方法
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_send: { data.enforceInterface(descriptor); com.example.appbinder.RequestData _arg0; if ((0!=data.readInt())) { _arg0 = com.example.appbinder.RequestData.CREATOR.createFromParcel(data); } else { _arg0 = null; } // 调用我们定义的方法 com.example.appbinder.ResponseData _result = this.send(_arg0); reply.writeNoException(); if ((_result!=null)) { reply.writeInt(1); _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } default: { return super.onTransact(code, data, reply, flags); } } }
Proxy类为客户端
private static class Proxy implements com.example.appbinder.IMyAidlInterface { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public com.example.appbinder.ResponseData send(com.example.appbinder.RequestData request) throws android.os.RemoteException { // 序列化参数 android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); com.example.appbinder.ResponseData _result; try { _data.writeInterfaceToken(DESCRIPTOR); if ((request!=null)) { _data.writeInt(1); request.writeToParcel(_data, 0); } else { _data.writeInt(0); } // 发送消息,调用Binder的onTransact方法 boolean _status = mRemote.transact(Stub.TRANSACTION_send, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { return getDefaultImpl().send(request); } _reply.readException(); if ((0!=_reply.readInt())) { //读取返回结果 _result = com.example.appbinder.ResponseData.CREATOR.createFromParcel(_reply); } else { _result = null; } } finally { _reply.recycle(); _data.recycle(); } return _result; } public static com.example.appbinder.IMyAidlInterface sDefaultImpl; }
send方法是IMyAidlInterface中自己定义的函数
public com.example.appbinder.ResponseData send(com.example.appbinder.RequestData request) throws android.os.RemoteException;
边栏推荐
- 使用单调栈解决接雨水问题——LeetCode 42 接雨水+单调栈说明
- 将回调函数转为Flow
- 无人驾驶运用了什么技术,无人驾驶技术是
- Secondary network security competition C module MS17-010 batch scanning
- 解决报错: YarnScheduler: Initial job has not accepted any resources
- How many assertion methods are commonly used in JMeter?
- 图的基本概念
- Lightweight Backbone VGNetG Achieves "No Choice, All" Lightweight Backbone Network
- 从零开始C语言精讲篇6:结构体
- data:image/jpg;base64格式数据转化为图片
猜你喜欢
随机推荐
玩转TypeScript对象、对象作为参数进行函数传递、接口和内置对象[无敌态]
从底层看 Redis 的五种数据类型
函数柯里化详解
js-第一个出现两次的字母
IDEA引入类报错:“The file size (2.59 MB) exceeds the configured limit (2.56MB)
推荐几种可以直接翻译PDF英文文献的方法
inject() can only be used inside setup() or functional components.
图的基本概念
经典二分法查找的进阶题目——LeetCode33 搜索旋转排序数组
1161. Maximum Level Sum of a Binary Tree
JNI学习1.环境配置与简单函数实现
线程安全问题
分布式计算实验3 基于PRC的书籍信息管理系统
RHCSA第五天
MySQL group_concat()详解
电脑系统数据丢失了是什么原因?找回方法有哪些?
经典递归回溯问题之——解数独(LeetCode 37)
金仓数据库的单节点如何转集群?
力扣 剑指 Offer 04. 二维数组中的查找
一天学会JDBC06:PrepaerdStatemtnt