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

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

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

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

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

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

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

正文

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;

}

@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_basicTypes: {

data.enforceInterface(descriptor);

int _arg0;

_arg0 = data.readInt();

long _arg1;

_arg1 = data.readLong();

boolean _arg2;

_arg2 = (0 != data.readInt());

float _arg3;

_arg3 = data.readFloat();

double _arg4;

_arg4 = data.readDouble();

java.lang.String _arg5;

_arg5 = data.readString();

最后

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

img

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!

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

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

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

最后

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-SxBIjyNV-1713331408191)]

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值