• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

关于java:BAT大厂技术大佬-3分钟带你看懂android的Binder机制必看

java 搞代码 3年前 (2022-01-27) 31次浏览 已收录 0个评论

一.引言
最近一段时间因为工作,接触到framework局部比拟多一点,也不免要和Binder打一些交道,也整顿了一些相干常识,但筹备写这篇文章时,还是有些慌。而且对于整个Binder机制的复杂程度不是喋喋不休能描叙分明的,也胆怯本人的了解有些偏差,误导一些敌人(ps:反正也没人看….扎心)所以也参考了很多材料。

本文次要站在Android开发的角度来大抵解析下Binder在java层的一些常识原理,给大家脑子造成一个残缺的概念,比方AIDL的实现原理,Binder是怎么通信的等等,文章文字较多,请急躁观看

相熟的敌人能够看看下篇,将介绍Activity的启动流程以及Android中的Hook技术:

震惊!Activity不必注册?手把手教你Hook

二.Binder概述
2.1 Android为什么抉择Binder
Android是基于Linux内核的,所以Android要实现过程间的通信,其实大可应用linux原有的一些伎俩,比方管道,共享内存,socket等形式,然而Android还是采纳了Binder作为次要机制,阐明Binder具备无可比拟的劣势。

其实过程通信大略就两个方面因素,一者性能方面,传输效率问题,传统的管道队列模式采纳内存缓冲区的形式,数据先从发送方缓存区拷贝到内核开拓的缓存区中,而后再从内核缓存区拷贝到接管方缓存区,至多有两次拷贝过程,而socket都晓得传输效率低,开销大,用于跨网络过程交互比拟多,共享内存尽管无需拷贝。

二者这是平安问题,Android作为一个开放式,领有泛滥开发者的的平台,应用程序的起源宽泛,确保终端平安是十分重要的,传统的IPC通信形式没有任何措施,根本依附下层协定,其一无奈确认对方牢靠的身份,Android为每个装置好的应用程序调配了本人的UID,故过程的UID是甄别过程身份的重要标记,传统的IPC要发送相似的UID也只能放在数据包里,但也容易被拦挡,歹意防御,socket则须要裸露本人的ip和端口,晓得这些恶意程序则能够进行任意接入。

综上所述,Android须要一种高效率,安全性高的过程通信形式,也就是Binder,Binder只须要一次拷贝,性能仅次于共享内存,而且采纳的传统的C/S构造,稳定性也是没得说,发送增加UID/PID,安全性高。

2.2 Binder实现机制
2.2.1过程隔离
咱们晓得过程之间是无奈间接进行交互的,每个过程独享本人的数据,而且操作系统为了保障本身的平安稳定性,将零碎内核空间和用户空间拆散开来,保障用户程序过程解体时不会影响到整个零碎,简略的说就是,内核空间(Kernel)是零碎内核运行的空间,用户空间(UserSpace)是用户程序运行的空间。为了保障安全性,它们之间是隔离的,所以用户空间的过程要进行交互须要通过内核空间来驱动整个过程。

2.2.2 C/S构造
Binder是基于C/S机制的,要实现这样的机制,server必须须要有特定的节点来承受到client的申请,也就是入口地址,像输出一个网址,通过DNS解析出对应的ip,而后进行拜访,这个就是server提供进去的节点地址,而Binder而言的话,与传统的C/S不太一样,Binder自身来作为Server中提供的节点,client拿到Binder实体对象对应的地址去拜访Server,对于client而言,怎么拿到这个地址并建设起整个通道是整个交互的关键所在,而且Binder作为一个Server中的实体,对象提供一系列的办法来实现服务端和客户端之间的申请,只有client拿到这个援用就能够或者一个有着该办法代理对象的援用,就能够进行通信了。

援用Android Bander设计与实现 – 设计篇一段话来总结:

面向对象思维的引入将过程间通信转化为通过对某个Binder对象的援用调用该对象的办法,而其独特之处在于Binder对象是一个能够跨过程援用的对象,它的实体位于一个过程中,而它的援用却遍布于零碎的各个过程之中。最迷人的是,这个援用和java里援用一样既能够是强类型,也能够是弱类型,而且能够从一个过程传给其它过程,让大家都能拜访同一Server,就象将一个对象或援用赋值给另一个援用一样。Binder含糊了过程边界,淡化了过程间通信过程,整个零碎好像运行于同一个面向对象的程序之中。不拘一格的Binder对象以及星罗棋布的援用好像粘接各个应用程序的胶水,这也是Binder在英文里的原意。

2.2.3 Binder通信模型
Binder基于C/S的构造下,定义了4个角色:Server、Client、ServerManager、Binder驱动,其中前三者是在用户空间的,也就是彼此之间无奈间接进行交互,Binder驱动是属于内核空间的,属于整个通信的外围,尽管叫驱动,然而实际上和硬件没有太大关系,只是实现的形式和驱动差不多,驱动负责过程之间Binder通信的建设,Binder在过程之间的传递,Binder援用计数治理,数据包在过程之间的传递和交互等一系列底层反对。

ServerManager的作用?

咱们晓得ServerManager也是属于用户空间的一个过程,次要作用就是作为Server和client的桥梁,client能够ServerManager拿到Server中Binder实体的援用,这么说可能有点含糊,举个简略的例子,咱们拜访,www.baidu.com,百度首页页面就显示进去了,首先咱们晓得,这个页面必定是公布在百度某个服务器上的,DNS通过你这个地址,解析出对应的ip地址,再去拜访对应的页面,而后再把数据返回给客户端,实现交互。这个和Binder的C/S十分相似,这里的DNS就是对应的ServerManager,首先,Server中的Binder实体对象,将本人的援用(也就是ip地址)注册到ServerManager,client通过特定的key(也就是百度这个网址)和这个援用进行绑定,ServerManager外部本人保护一个相似MAP的表来一一对应,通过这个key就能够向ServerManager拿到Server中Binder的援用,对应到Android开发中,咱们晓得很多零碎服务都是通过Binder去和AMS进行交互的,比方获取音量服务:

AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);

仔细的敌人应该发现ServerManager和Server也是两个不同的过程呀,Server要向ServerManager去注册不是也要波及到过程间的通信吗,以后实现过程间通信又要用到过程间的通信,你这不是扯犊子吗….莫急莫急,Binder的奇妙之处在于,当ServerManager作为Serve端的时候,它提供的Binder比拟非凡,它没有名字也不须要注册,当一个过程应用BINDER_SET_CONTEXT_MGR命令将本人注册成SMgr时Binder驱动会主动为它创立Binder实体,这个Binder的援用在所有Client中都固定为0而无须通过其它伎俩取得。也就是说,一个Server若要向ServerManager注册本人Binder就必须通过0这个援用号和ServerManager的Binder通信,有敌人又要问了,server和client属于两个不同的过程,client怎么能拿到server中对象,无妨先看看上面的交互图

从上图很清晰的能够看进去整个的交互过程,本来从SM中拿到binder的援用,通过Binder驱动层的解决之后,返回给了client一个代理对象,实际上如果client和server处于同一个过程,返回的就是以后binder对象,如果client和server不处于同一个过程,返回给client的就是一个代理对象,这一点,有趣味能够看下源码。

2.2.4 Binder角色的定位
Binder实质上只是提供了一种通信的形式,和咱们具体要实现的内容没有关系,为了实现这个服务,咱们须要定义一些接口,让client可能近程调用服务,因为是跨过程,这时候就要设计到代理模式,以接口函数位基准,client和server去实现接口函数,Server是服务真正的实现,client作为一个近程的调用。

从Server过程来看,Binder是存在的实体对象,client通过transact()函数,通过Binder驱动,最终回调到Binder实体的onTransact()函数中。
从 Client过程的角度看,Binder 指的是对 Binder 代理对象,是 Binder 实体对象的一个近程代理,通过Binder驱动进行交互
3.手写过程通信
下面说了那么多,大家伙也看累了,上面通过代码的模式让大家对Binder加深点了解,日常开发中,波及到过程间通信的话,咱们首先想到的可能就是AIDL,但不晓得有没有和我感觉一样的敌人。。第一次写AIDL是蒙蔽的,通过.aidl文件,编译器主动生成代码,生成一个java文件,外面又有Stub类,外面还有Proxy类,齐全不了解外面的机制,的确不便于咱们了解学习,为了加深了解,咱们摈弃aidl,手写一个通信代码。

首先咱们要定义一个接口服务,也就是上述的服务端要具备的能力来提供给客户端,定义一个接口继承IInterface,代表了服务端的能力

public interface PersonManger extends IInterface {

void addPerson(Person mPerson);
List<Person> getPersonList();

}
复制代码
接下来咱们就要定义一个Server中的Binder实体对象了,首先必定要继承Binder,其次须要实现下面定义好的服务接口

public abstract class BinderObj extends Binder implements PersonManger {

public static final String DESCRIPTOR = "com.example.taolin.hellobinder";
public static final int TRANSAVTION_getPerson = IBinder.FIRST_CALL_TRANSACTION;
public static final int TRANSAVTION_addPerson = IBinder.FIRST_CALL_TRANSACTION + 1;
public static PersonManger asInterface(IBinder mIBinder){
    IInterface iInterface = mIBinder.queryLocalInterface(DESCRIPTOR);
    if (null!=iInterface&&iInterface instanceof PersonManger){
        return (PersonManger)iInterface;
    }
    return new Proxy(mIBinder);
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
    switch (code){
        case INTERFACE_TRANSACTION:
            reply.writeString(DESCRIPTOR);
            return true;

        case TRANSAVTION_getPerson:
            data.enforceInterface(DESCRIPTOR);
            List<Person> result = this.getPersonList();
            reply.writeNoException();
            reply.writeTypedList(result);
            return true;

        case TRANSAVTION_addPerson:
            data.enforceInterface(DESCRIPTOR);
            Person arg0 = null;
            if (data.readInt() != 0) {
                arg0 = Person.CREATOR.createFromParcel(data);
            }
            this.addPerson(arg0);
            reply.writeNoException();
            return true;
    }
    return super.onTransact(code, data, reply, flags);

}

@Override
public IBinder asBinder() {
    return this;
}

}
复制代码
首先咱们看asInterface办法,Binder驱动传来的IBinder对象,通过queryLocalInterface办法,查找本地Binder对象,如果返回的就是PersonManger,阐明client和server处于同一个过程,间接返回,如果不是,返回给一个代理对象。

当然作为代理对象,也是须要实现服务接口

public class Proxy implements PersonManger {

private IBinder mIBinder;
public Proxy(IBinder mIBinder) {
    this.mIBinder =mIBinder;
}

@Override
public void addPerson(Person mPerson) {
    Parcel data = Parcel.obtain();
    Parcel replay = Parcel.obtain();

    try {
        data.writeInterfaceToken(DESCRIPTOR);
        if (mPerson != null) {
            data.writeInt(1);
            mPerson.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mIBinder.transact(BinderObj.TRANSAVTION_addPerson, data, replay, 0);
        replay.readException();
    } catch (RemoteException e){
        e.printStackTrace();
    } finally {
        replay.recycle();
        data.recycle();
    }
}

@Override
public List<Person> getPersonList() {
    Parcel data = Parcel.obtain();
    Parcel replay = Parcel.obtain();
    List<Person> result = null;
    try {
        data.writeInterfaceToken(DESCRIPTOR);
        mIBinder.transact(BinderObj.TRANSAVTION_getPerson, data, replay, 0);
        replay.readException();
        result = replay.createTypedArrayList(Person.CREATOR);
    }catch (RemoteException e){
        e.printStackTrace();
    } finally{
        replay.recycle();
        data.recycle();
    }
    return result;
}

@Override
public IBinder asBinder() {
    return null;
}

}
复制代码
这里的代理对象本质就是client最终拿到的代理服务,通过这个就能够和Server进行通信了,首先通过Parcel将数据序列化,而后调用 remote.transact()将办法code,和data传输过来,对应的会回调在在Server中的onTransact()中

而后是咱们的Server过程,onBind办法返回mStub对象,也就是Server中的Binder实体对象

public class ServerSevice extends Service {

private static final String TAG = "ServerSevice";
private List<Person> mPeople = new ArrayList<>();

@Override
public void onCreate() {
    mPeople.add(new Person());
    super.onCreate();
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mStub;
}
private BinderObj mStub = new BinderObj() {
    @Override
    public void addPerson(Person mPerson) {
        if (mPerson==null){
            mPerson = new Person();
            Log.e(TAG,"null obj");
        }
        mPeople.add(mPerson);
        Log.e(TAG,mPeople.size()+"");
    }

    @Override
    public List<Person> getPersonList() {
        return mPeople;
    }
};

}
复制代码
最终咱们在客户端过程,bindService传入一个ServiceConnection对象,在与服务端建设连贯时,通过咱们定义好的BinderObj的asInterface办法返回一个代理对象,再调用办法进行交互

public class MainActivity extends AppCompatActivity {

private boolean isConnect = false;
private static final String TAG = "MainActivity";
private PersonManger personManger;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    start();
    findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (personManger==null){
                Log.e(TAG,"connect error");
                return;
            }
            personManger.addPerson(new Person());
            Log.e(TAG,personManger.getPersonList().size()+"");
        }
    });
}

private void start() {
    Intent intent = new Intent(this, ServerSevice.class);
    bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.e(TAG,"connect success");
        isConnect = true;
        personManger = BinderObj.asInterface(service);
        List<Person> personList = personManger.getPersonList();
        Log.e(TAG,personList.size()+"");
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.e(TAG,"connect failed");
        isConnect = false;
    }
};

}
复制代码
这样的话,一次实现的过程间的交互就实现了~是不是感觉没有设想中那么难,最初倡议大家在不借助 AIDL 的状况下手写实现 Client 和 Server 过程的通信,加深对 Binder 通信过程的了解。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:关于java:BAT大厂技术大佬-3分钟带你看懂android的Binder机制必看

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址