征服面试官系列: Binder 核心机制和进程间通信,你都理解了吗?(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文


使用Binder完成进程间通信其实非常简单。我们举一个查询成绩的例子,服务端提供根据学生姓名查询学生成绩的接口,客户端连接服务端通过学生姓名来查询成绩,而客户端与服务端的媒介就是Binder。

1.服务端的实现

服务端自然是要提供服务的,因此就需要我们开启一个Service等待客户端的连接。关于Android的Service这里就不用多说了吧,我们实现一个GradeService并继承Service,来提供成绩查询接口。代码如下:

public class GradeService extends Service {

public static final int REQUEST_CODE=1000;

private final Binder mBinder = new Binder() {

@Override

protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {

if (code == REQUEST_CODE) {

String name = data.readString();

// 根据姓名查询学生成绩并将成绩写入到返回数据

int studentGrade = getStudentGrade(name);

if (reply != null)

reply.writeInt(studentGrade);

return true;

}

return super.onTransact(code, data, reply, flags);

}

// 根据姓名查询学生成绩

public int getStudentGrade(String name) {

return StudentMap.getStudentGrade(name);

}

};

@Nullable

@Override

public IBinder onBind(Intent intent) {

return mBinder;

}

}

因为我们要实现的是跨进程通信,因此,我们将这个服务端的Service设置到远程进程中,在AndroidManifest文件中如下:

<service

android:name=“com.zhpan.sample.binder.server.GradeService”

android:process=“:server”>

就这样,一个远程的,提供成绩查询的服务就完成了。

2.客户端的实现

客户端自然而然的是要连接服务端进程成绩查询。因此,我们在客户端的Activity中取绑定GradeService进行成绩查询。代码如下:

public class BinderActivity extends AppCompatActivity {

// 远程服务的Binder代理

private IBinder mRemoteBinder;

private final ServiceConnection mServiceConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

// 获取远程服务的Binder代理

mRemoteBinder = iBinder;

}

@Override

public void onServiceDisconnected(ComponentName componentName) {

mRemoteBinder = null;

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_binder);

// 绑定服务

findViewById(R.id.btn_bind_service).setOnClickListener(view -> bindGradeService());

// 查询学生成绩

findViewById(R.id.btn_find_grade).setOnClickListener(view -> getStudentGrade(“Anna”));

}

// 绑定远程服务

private void bindGradeService() {

String action = “android.intent.action.server.gradeservice”;

Intent intent = new Intent(action);

intent.setPackage(getPackageName());

bindService(intent, mServiceConnection, BIND_AUTO_CREATE);

}

// 从远程服务查询学生成绩

private int getStudentGrade(String name) {

Parcel data = Parcel.obtain();

Parcel reply = Parcel.obtain();

int grade = 0;

data.writeString(name);

try {

if (mRemoteBinder == null) {

throw new IllegalStateException(“Need Bind Remote Server…”);

}

mRemoteBinder.transact(REQUEST_CODE, data, reply, 0);

grade = reply.readInt();

} catch (RemoteException e) {

e.printStackTrace();

}

return grade;

}

客户端的代码就是通过绑定远程服务,然后获取到服务的Binder代理,来查询学生成绩。可见,使用Binder实现进程间通信是非常简单的,可以说简单的有点出乎所料。那我们之前写的AIDL是什么呢?AIDL生成的那一堆是什么玩意儿?我们且往下看。

三、代理模式优化Binder使用


虽然上一章中的代码已经非常简单了,但是还是有可以优化的空间。我们可以通过设计模式来进行优化,让代码更加简洁。

首先需要定义一个接口查询成绩的接口IGradeInterface,代码如下:

public interface IGradeInterface {

// 查询成绩接口

int getStudentGrade(String name);

}

1.服务端代码优化

接着,对服务端的代码进行优化。我们实现一个自定义的GradeBinder,并实现上述接口,代码如下:

public class GradeBinder extends Binder implements IGradeInterface {

@Override

public int getStudentGrade(String name) {

return StudentMap.getStudentGrade(name);

}

@Override

protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {

if (code == REQUEST_CODE) {

String name = data.readString();

int studentGrade = getStudentGrade(name);

if (reply != null)

reply.writeInt(studentGrade);

return true;

}

return super.onTransact(code, data, reply, flags);

}

}

上述代码将查询成绩的相关逻辑从Service搬到了GradeBinder中。因此,此时Service中只需要在onBind的时候返回GradeBinder的实例即可。代码如下:

public class GradeService extends Service {

public static final int REQUEST_CODE=1000;

@Nullable

@Override

public IBinder onBind(Intent intent) {

return new GradeBinder();

}

}

2.客户端代码优化

客户端优化的思路是在连接到远程服务时候实例化一个代理类,代理类持有Binder,让代理类行使Binder的权利。首先来看代理类的代码实现:

public class BinderProxy implements IGradeInterface {

// 被代理的Binder

private final IBinder mBinder;

// 私有化构造方法

private BinderProxy(IBinder binder) {

mBinder = binder;

}

// 通过Binde读取成绩

@Override

public int getStudentGrade(String name) {

Parcel data = Parcel.obtain();

Parcel reply = Parcel.obtain();

int grade = 0;

data.writeString(name);

try {

if (mBinder == null) {

throw new IllegalStateException(“Need Bind Remote Server…”);

}

mBinder.transact(REQUEST_CODE, data, reply, 0);

grade = reply.readInt();

} catch (RemoteException e) {

e.printStackTrace();

}

return grade;

}

// 实例化Binder代理类的对象

public static IGradeInterface asInterface(IBinder iBinder) {

if (iBinder == null) {

return null;

}

if (iBinder instanceof IGradeInterface) {

LogUtils.e(“当前进程”);

// 如果是同一个进程的请求,则直接返回Binder

return (IGradeInterface) iBinder;

} else {

LogUtils.e(“远程进程”);

// 如果是跨进程查询则返回Binder的代理对象

return new BinderProxy(iBinder);

}

}

}

BinderProxy类的构造方法被设置成了private。同时提供了一个asInterface方法中,这个方法通过判断Binder是不是IGradeInterface类型从而确定是不是跨进程的通信。如果不是跨进程通信,则返回当前这个Binder,否则就返回Binder的这个代理类。

接下来客户端连接上远程服务的时候使用BinderProxy获取Binder或者BinderProxy实例。代码如下:

public class BinderProxyActivity extends BaseViewBindingActivity {

// 此处可能是BinderProxy也可能是GradeBinder

private IGradeInterface mBinderProxy;

private final ServiceConnection mServiceConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

// 连接服务成功,根据是否跨进程获取BinderProxy或者GradeBinder实例

mBinderProxy = BinderProxy.asInterface(iBinder);

}

@Override

public void onServiceDisconnected(ComponentName componentName) {

mBinderProxy = null;

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

binding.btnBindService.setOnClickListener(view -> bindGradeService());

// 查询学生成绩点击事件,通过mBinderProxy查询成绩

binding.btnFindGrade.setOnClickListener(view -> ToastUtils.showShort("Anna grade is " + mBinderProxy.getStudentGrade(“Anna”)));

}

// 绑定服务

private void bindGradeService() {

String action = “android.intent.action.server.gradeservice”;

Intent intent = new Intent(action);

intent.setPackage(getPackageName());

bindService(intent, mServiceConnection, BIND_AUTO_CREATE);

}

}

可以看到,此时的代码相比第一章的代码整洁了不少。但是,代码写起来似乎还没有第一章中的方便。主要是因为要我们增加一个IGradeInterface接口,还要自定义一个GradeBinder,同时,还需要写代理类的相关代码,感觉非常繁琐。那么有没有办法让代码简洁,写起来还不繁琐呢?答案是肯定的,使用AIDL就可以实现。

四、AIDL


AIDL是Android Interface Description Languaged 简写。用于描写客户端/服务端通信接口的一种描述语言。提起AIDL相信很多人都会觉得头大,定义一个AIDL接口,生成了那么多不知所云的代码,看起来简直就是灾难。先别担心,如果你看懂了第三章的内容,那么其实你已经完全掌握了AIDL。没错,说白了AIDL生成的那一坨代码其实就是我们第三章中写的代码。即AIDL的原理其实就是使用了代理模式对Binder的使用进行了优化,使用AIDL保证了代码的整洁,同时也省去了自己编写繁琐的代理类相关代码。

关于AIDL的使用就非常简单了。

1.创建AIDL接口

首先,在要创建AIDL的目录上右键->New->AIDL->AIDl File 来创建一个AIDL文件,如下图所示:

创建一个名为IGradeService的AIDL文件,并添加一个getStudentGrade的方法。代码如下:

// IGradeService.aidl

package com.zhpan.sample.binder.aidl;

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

interface IGradeService {

/**

  • Demonstrates some basic types that you can use as parameters

  • and return values in AIDL.

*/

void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,

double aDouble, String aString);

int getStudentGrade(String name);

}

接着Rebuild一下项目后IDE就会自动生成AIDL的代码了。

2.AIDL生成的代码

在项目的build目录下com.zhpan.sample.binder.aidl包中会看到自动生成的一个名为IGradeService的接口,代码如下:

// 这个接口相当于上一章中的IGradeInterface接口

public interface IGradeService extends android.os.IInterface {

// Stub是一个Binder,相当于上一章中的GradeBinder

public static abstract class Stub extends android.os.Binder

implements com.zhpan.sample.binder.aidl.IGradeService {

private static final java.lang.String DESCRIPTOR = “com.zhpan.sample.binder.aidl.IGradeService”;

public Stub() {

this.attachInterface(this, DESCRIPTOR);

}

public static IGradeService asInterface(android.os.IBinder obj) {

if ((obj == null)) {

return null;

}

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

if (((iin != null) && (iin instanceof com.zhpan.sample.binder.aidl.IGradeService))) {

// 如果是当前进程则直接返回当前Binder对象

return ((com.zhpan.sample.binder.aidl.IGradeService) iin);

}

// 跨进程则返回Binder的代理对象

return new com.zhpan.sample.binder.aidl.IGradeService.Stub.Proxy(obj);

}

@Override public android.os.IBinder asBinder() {

return this;

最后

一线互联网Android面试题含详解(初级到高级专题)

这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率

如果设置门槛,很多开发者朋友会因此错过这套高级架构资料,错过提升成为架构师的可能。这就失去了我们的初衷;让更多人都能通过高效高质量的学习,提升自己的技术和格局,升职加薪。

最后送给大家一句话,望共勉,永远不要放弃自己的梦想和追求;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

new com.zhpan.sample.binder.aidl.IGradeService.Stub.Proxy(obj);

}

@Override public android.os.IBinder asBinder() {

return this;

最后

一线互联网Android面试题含详解(初级到高级专题)

这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率

[外链图片转存中…(img-pdRdYFnc-1713331371332)]

如果设置门槛,很多开发者朋友会因此错过这套高级架构资料,错过提升成为架构师的可能。这就失去了我们的初衷;让更多人都能通过高效高质量的学习,提升自己的技术和格局,升职加薪。

最后送给大家一句话,望共勉,永远不要放弃自己的梦想和追求;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-76N3Apm5-1713331371332)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值