谈谈Android-Binder机制及AIDL使用(2)

顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

为什么要使用Binder?
性能方面 在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信 机制的性能有严格的要求,Binder相对于传统的Socket方式,更加高效。Binder数 据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内 存拷贝都不需要,但实现方式又比较复杂。

安全方面
传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信 的IP地址是客户端手动填入,很容易进行伪造。然而,Binder机制从协议本身就支 持对通信双方做身份校检,从而大大提升了安全性。

2、 Binder

IPC原理
从进程角度来看IPC(Interprocess Communication)机制

每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。例如,对应一 个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间。当然内核空间的 大小是可以通过参数配置调整的。对于用户空间,不同进程之间是不能共享的,而 内核空间却是可共享的。Client进程向Server进程通信,恰恰是利用进程间可共享 的内核内存空间来完成底层通信工作的。Client端与Server端进程往往采用ioctl等方 法与内核空间的驱动进行交互。

Binder原理
Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager 以及Binder驱动,其中ServiceManager用于管理系统中的各种服务。架构图如下所 示:

Binder通信的四个角色

Client进程: 使用服务的进程。
Server进程: 提供服务的进程。
ServiceManager进程: ServiceManager的作用是将字符形式的Binder名字转化成 Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder 实体的引用。
Binder驱动: 驱动负责进程之间Binder通信的建立,Binder在进程之间的传递, Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

Binder运行机制
图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于 Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与 Server端。

注册服务(addService): Server进程要先注册Service到ServiceManager。该过 程:Server是客户端,ServiceManager是服务端。
获取服务(getService): Client进程使用某个Service前,须先向ServiceManager中 获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。
使用服务: Client根据得到的Service信息建立与Service所在的Server进程通信的通 路,然后就可以直接与Service交互。该过程:Client是客户端,Server是服务端。

图中的ClientServerService Manager之间交互都是虚线表示,是由于它们彼此 之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信 (Interprocess Communication)方式。其中Binder驱动位于内核空间,ClientServerService Manager位于用户空间。Binder驱动和Service Manager可以看做 是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自 定义实现Client、Server端,借助Android的基本平台架构便可以直接进行IPC通 信。

Binder运行的实例解释
首先我们看看我们的程序跨进程调用系统服务的简单示例,实现浮动窗口部分代 码:

//获取WindowManager服务引用
WindowManager wm = (WindowManager) getSystemService(getApplicati on().WINDOW_SERVICE);
//布局参数layoutParams相关设置略…
View view = LayoutInflater.from(getApplication()).inflate(R.layo ut.float_layout, null);
//添加view
wm.addView(view, layoutParams);

注册服务(addService): 在Android开机启动过程中,Android会初始化系统的各种 Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。 这一步是系统自动完成的。
获取服务(getService): 客户端想要得到具体的Service直接向ServiceManager要 即可。客户端首先向ServiceManager查询得到具体的Service引用,通常是Service 引用的代理对象,对数据进行一些处理操作。即第2行代码中,得到的wm是 WindowManager对象的引用。
使用服务: 通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。即 第6行调用WindowManageraddView函数,将触发远程调用,调用的是运行在 systemServer进程中的WindowManageraddView函数。
使用服务的具体执行过程

  1. Client通过获得一个Server的代理接口,对Server进行调用。
  2. 代理接口中定义的方法与Server中定义的方法是一一对应的。
  3. Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打 包成Parcel对象。
  4. 代理接口将Parcel发送给内核中的Binder Driver。
  5. Server会读取Binder Driver中的请求数据,如果是发送给自己的,解包Parcel 对象,处理并将结果返回。
  6. 整个的调用过程是一个同步过程,在Server处理的时候,Client会Block住。因 此Client调用过程不应在主线程。
AIDL的使用
1.AIDL的简介

AIDL (Android Interface Definition Language) 是一种接口定义语言,用于生成可以 在Android设备上两个进程之间进行进程间通信(Interprocess Communication, IPC) 的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service) 对象的操作,就可以使用AIDL生成可序列化的参数,来完成进程间通信。

简言之,AIDL能够实现进程间通信,其内部是通过Binder机制来实现的,后面会 具体介绍,现在先介绍AIDL的使用。

2.AIDL的具体使用

AIDL的实现一共分为三部分,一部分是客户端,调用远程服务。一部分是服务端, 提供服务。最后一部分,也是最关键的是AIDL接口,用来传递的参数,提供进程间 通信。
先在服务端创建AIDL部分代码。
AIDL文件 通过如下方式新建一个AIDL文件

默认生成格式

interface IBookManager {
/**

  • Demonstrates some basic types that you can use as paramet ers
  • and return values in AIDL.
    */
    void basicTypes(int anInt, long aLong, boolean aBoolean, flo at aFloat, double aDouble, String aString);
    }

默认如下格式,由于本例要操作Book类,实现两个方法,添加书本和返回书本列 表。
定义一个Book类,实现Parcelable接口。

public class Book implements Parcelable {
public int bookId;
public String bookName;

public Book() {
}

public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}

public int getBookId() {
return bookId;
}

public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.bookId);
dest.writeString(this.bookName);
}

protected Book(Parcel in) {
this.bookId = in.readInt();
this.bookName = in.readString();
}
public static final Parcelable.Creator CREATOR = new P arcelable.Creator() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}

@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}

由于AIDL只支持数据类型:基本类型(int,long,char,boolean等),String, CharSequence,List,Map,其他类型必须使用import导入,即使它们可能在同一 个包里,比如上面的Book。 最终IBookManager.aidl 的实现

// Declare any non-default types here with import statements import com.lvr.aidldemo.Book;
interface IBookManager {
/**

  • Demonstrates some basic types that you can use as paramet ers
  • and return values in AIDL.
    */
    void basicTypes(int anInt, long aLong, boolean aBoolean, flo at aFloat, double aDouble, String aString);
    void addBook(in Book book); List getBookList();
    }

注意: 如果自定义的Parcelable对象,必须创建一个和它同名的AIDL文件,并在其 中声明它为parcelable类型。

Book.aidl

// Book.aidl
package com.lvr.aidldemo;

parcelable Book;

以上就是AIDL部分的实现,一共三个文件。 然后Make Project ,SDK为自动为我们生成对应的Binder类。 在如下路径下:

其中该接口中有个重要的内部类Stub ,继承了Binder 类,同时实现了 IBookManager接口。 这个内部类是接下来的关键内容。

public static abstract class Stub extends android.os.Binder impl ements com.lvr.aidldemo.IBookManager{}

服务端 服务端首先要创建一个Service用来监听客户端的连接请求。然后在Service 中实现Stub 类,并定义接口中方法的具体实现。

//实现了AIDL的抽象函数
private IBookManager.Stub mbinder = new IBookManager.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoole an, float aFloat, double aDouble, String aString) throws RemoteE xception {
//什么也不做
}

@Override
public void addBook(Book book) throws RemoteException {
//添加书本
if (!mBookList.contains(book)) {
mBookList.add(book); }
}

@Override
public List getBookList() throws RemoteException {
return mBookList;
}
};

当客户端连接服务端,服务端就会调用如下方法:

public IBinder onBind(Intent intent) {
return mbinder;
}

就会把Stub实现对象返回给客户端,该对象是个Binder对象,可以实现进程间通 信。 本例就不真实模拟两个应用之间的通信,而是让Service另外开启一个进程来 模拟进程间通信。






android:process=":remote" 设置为另一个进程。 <action android:name="com.lvr.aidldemo.MyService"/> 是为了能让其他apk隐式 bindService。通过隐式调用的方式来连接service,需要把category设为default, 这是因为,隐式调用的时候,intent中的category默认会被设置为default。

客户端

尾声

以薪资待遇为基础,以发展为最终目标,要在高薪资的地方,谋求最好的发展!

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。

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

需要这份系统化学习资料的朋友,可以戳这里获取

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

知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化学习资料的朋友,可以戳这里获取

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值