Binder核心机制分析揭秘跨进程得实现原理

前言

想写篇关于Binder的文章,可对其一无所知,无从下手。在阅读了大量的优秀文章后,心惊胆战的提笔,不怕文章被贻笑大方,怕的是误人子弟!望各位大佬抽空阅读本文的同时,能够对文章的知识点持怀疑态度,共同探讨,共同进步!

一、序列化

日常开发中,通过Intent携带数据跳转Activity时,数据通常要通过实现Serializable或Parcelable接口,才能在被Intent所携带,而Serializable接口和Parcelabel接口主要是完成对象的序列化过程。将对象持久化到设备上或者网络传输同样也需要序列化。

1.Serializable 接口

Serializable接口是Java所提供的,为对象提供标准的序列化和反序列化操作。通常一个对象实现Serializable接口,该对象就具有被序列化和反序列化的能力,而且几乎所有工作有系统自动完成。Serializable接口内serialVersionID可指定也可以不指定,其作用是用来判断序列化前和反序列化的类版本是否发生变化。该变量如果值不一致,表示类中某些属性或者方法发生了更改,反序列化则出问题。(静态成员变量和transient关键字标记的成员不参与序列化过程)

2.Parcelable 接口

Parcelable 接口是Android所提供的,其实现相对来说比价复杂。实现该接口的类的对象就可以在Intent和Binder进行传递。

3.两者的区别

Serializable是Java提供的接口,使用简单,但序列化与反序列化需要大量的IO操作,所以开销比较大。Parcelable是Android提供的序列化方法,使用麻烦当效率高。在Android开发中,将对象序列化到设备或者序列化后通过网络传输建议使用Serializable接口,其他情况建议是用Parcelable接口,尤其在内存的序列化上。例如Intent和Binder传输数据。

二、AIDL

在Java层,想利用Binder进行夸进程的通信,那就得通过AIDL(Android 接口定义语言)了,AIDL是客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口,只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL,如果是在单应用(单进程),建议使用Messager。

1、AIDL支持的数据类型

  • Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等)
  • String 和 CharSequence
  • 所有实现了Parcelable接口的对象
  • AIDL接口
  • List,目前List只支持ArrayList类型,持有元素必须是以上讲到类型。
  • Map,目前只支持HashMap类型,持有元素必须是以上讲到类型

自定义的Parcelable对象和AIDL接口必须显示导入到AIDL文件中。

数据的走向

Parcelable对象和AIDL接口在使用前必须标明数据的走向:

  • in 客户端流向服务端
  • out 服务端流向客户端
  • inout 服务端与客户端可进行交互

示例:

void addUser(inout User user);

2、服务端的实现

2.1、定义数据对象
定义一个实现了Parcelable 接口,作为客户端和服务端传输的数据对象。

public class User implements Parcelable {

    private String username;

    private String address;

    public User() {
    }

    public User(String username, String address) {
        this.username = username;
        this.address = address;
    }

    User(Parcel in) {
       readFromParcel(in);
    }
    //系统默认生成,反序列化过程,我们只需要要构造方法读取相关值就可以
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
     //系统默认生成,内容描述功能,几乎所有情况下都返回0,
     //仅仅当前存在文件描述符,才返回1
    @Override
    public int describeContents() {
        return 0;
    }
    //序列化过程,通过一系列的write将值写到Parcel 对象
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(username);
        dest.writeString(address);
    }

    @Override
    public String toString() {
        return username+":"+address;
    }

    public void readFromParcel(Parcel in){
        username=in.readString();
        address=in.readString();
    }
}

2.2、抽象服务端服务
通过下面方法,建立一个UserManger.aidl文件,表示服务端能为客户端提供什么样的服务。

下面代码通过建立UserManager.aidl文件,为客户端提供addUsergetUser的能力。UserManager可以理解为,服务端和客户端的共同约定,两者能进行怎么样的交互

package com.gitcode.server;

// 在这里要导入传递对象的类型,例如User
import com.gitcode.server.User;

interface UserManager {

    void addUser(inout User user);

    User getUser(int index);
}

定义UserManager.aidl文件后,系统默认会生成UserManager.java文件。

UserManager.java的代码如下,为了减少篇幅,去掉了一些实现。

public interface UserManager extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.gitcode.server.UserManager {
        private static final String DESCRIPTOR = "com.gitcode.server.UserManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.gitcode.server.UserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.gitcode.server.UserManager))) {
                return ((com.gitcode.server.UserManager) iin);
            }
            return new 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 {
           ......
        }

        private static class Proxy implements com.gitcode.server.UserManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void addUser(com.gitcode.server.User user) throws android.os.RemoteException {
                 ......
            }

            @Override
            public com.gitcode.server.User getUser(int index) throws android.os.RemoteException {
                .....
            }
        }

        static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void addUser(com.gitcode.server.User user) throws android.os.RemoteException;

    public com.gitcode.server.User getUser(int index) throws android.os.RemoteException;
}

从上文可知,UserManager本身是一个接口,并继承IInterface接口。UserManager.java声明了addUsergetUser,和在UserManager.aidl的声明是一致的。同时声明两个整型TRANSACTION_addUserTRANSACTION_getUser,用于在transact()方法中标识调用服务端哪个方法。如果服务端和客户端在不同进程,方法调用会走transact()方法,逻辑由Stub 和Proxy 内部类完成。
内部类Stub的一些概念和方法含义:
DESCRIPTOR
Binder的唯一标识,一般用当前的类名全名标识。
asInterface(IBinder obj)
将服务端的Binder对象转换成客户端的AIDL接口类型的对象,如果客户端和服务端同一进程,直接返回Stub对象本身,不在同一进程,则返回由系统封装的Stub.proxy对象
asBinder
返回当前Binder对象
onTransact(int code, Parcel data, Parcel reply, int flags)
运行在服务端Binder线程池,当客户端跨进程发起请求后,系统封装后交由此方法来处理。code表示调用服务端什么方法,上文声明的整型。data表示客户端传递过来的数据,reply为服务端对客户端的回复。
内部代理类 Poxy,表示客户端远程能对服务端进行的操作。
addUser运行在客户端,当客户端远程调用时,
在相同目录下创建User.aidl,可以直接复制UserManager.aidl,内容修改如下。

package com.gitcode.server;

parcelable User;

在服务端中,服务一般以Service体现,定义UserServcie,继承Service。

public class UserService extends Service {
    private static final String TAG = "Server";
    private List<User> list = new ArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        list.add(new User("GitCode", "深圳"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG,"on Bind");
        return stub;
    }

    private UserManager.Stub stub = new UserManager.Stub() {
        @Override
        public void addUser(User user) throws RemoteException {
            list.add(user);
            Log.i(TAG,"add user:"+user);
        }

        @Override
        public User getUser(int index) throws RemoteException {
            Log.i(TAG,"get user,index:"+index);
            return list.size() > index && index >= 0 ? list.get(index) : null;
        }
    };
}

在AndroidManifest.xml文件声明Service,以两个组件形成单独的app来体现两个进程,通过AIDL进行数据交互。在客户端通过bindService()来启动该服务。

<service android:name="com.gitcode.server.UserService"
    android:enabled="true"
    android:exported="true">
        <intent-filter>
            <action android:name="com.gitcode.server.userservice"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
</service>

3、客户端的实现

客户端主要是通过共同的约定(UserManger.aidl)向服务端进行请求,服务端响应客户端的请求。为了提高效率和减少出错,通过拷贝来实现客户端的AIDL文件。将服务端的aidl整个文件拷贝到客户端的main目录下,不做任何修改。

在客户端建立与服务端User类同包的目录,并将User类拷贝过来,不做任何修改。

在Activity中绑定服务端的Service,绑定成功后进行数据交互。

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Client";
    private UserManager mUserManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        toBindService();
    }

    private void toBindService() {
        Intent intent = new Intent("com.gitcode.server.userservice");
        intent.setPackage("com.gitcode.server");
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mUserManager = UserManager.Stub.asInterface(service);

            try {
                User user = mUserManager.getUser(0);
                Log.e(TAG, user.toString());

                mUserManager.addUser(new User("张三","北京"));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
}

运行效果:

客户端:

服务端:

4、小结

客户端调用服务的方法,被调用的方法运行在服务端的的Binder线程池,同时客户端会被挂起,如果服务端方法执行耗时操作,就会导致客户端ANR,所以不要在客户端主线程访问远程服务方法。同时服务端不应该自己新建新建线程运行服务方法,因为方法会交由线程池处理,同时对数据也要做好并发访问处理。
AIDL可以说为应用层开发提供了封装,不用过多的了解Binder的机制,通过生成的UserManager.java,初步可以了解Binder的IPC机制。使用AIDL在进程之间进行数据通信,更注重的是细节和业务的实现。

三、Binder

Binder是Android系统提供的一种IPC机制。由于Android是基于Linux内核,因此,除了Binder以外,还有其他的IPC机制,例如Socket,共享内存,管道和消息队列等。之所有不使用原有的 IPC机制,是因为使用Binder机制,能从性能、稳定性、安全性带来更好的效果。例如,Socket是一套通用的接口,传输速率低下,适合网络传输这种情况,而管道和消息队列需要数据的两次拷贝,共享内容难以管控等。而Binder对数据只需要一次拷贝,使用C/S架构,职责明确,容易维护和使用。
通过下图可以了解到,Binder机制通过内存映射实现跨进程通信,Binder在IPC机制只是作为一个数据的载体,当进程A向虚拟内存空间中写入数据,数据会被实时反馈到进程B的虚拟内存空间。整个发送数据的过程,只从用户空间拷贝一次到虚拟内存空间。

在Binder机制中,主要涉及到Client、Server、ServiceManger三个端,三者通过Binder进行跨进程通信,支持着Android这个大网络。它们的关系如下图。

Server
Server进程需要注册一些Service到ServiceManger中,以对外告知其可提供的服务。例如上文AIDL中,会注册UserService,并为Client提供添加User和获取User的操作。注册的过程,Server进程就是客户端,而ServiceManger就是服务端。
Client
对Sever进程进行业务逻辑操作。通过Service的名称在ServiceManger查找对应的Service。
ServiceManager
ServiceManger集中管理系统内的所有Service,服务通过注册Service到ServiceManger的查找表中,当Client根据Service名称请求ServiceManger在查找表中查询对应的Service。
图表示三者的C/S架构,例如Client查询向ServiceManger查询Service时,Client就是客户端,而ServiceManger就是服务端。而虚线则表示两者之间通过Binder进行进程间的通信,因此通过了解一条虚线的流程,就可以知道Binder的机制。

1、Service的注册过程

通过Server进程的注册Service过程,可以了解到Binder机制的工作原理。

BpServcieManger和BnServcieManger是客户端与服务端进程业务层辑实现的封装,而BpBinder和BBinder是IPC机制的方式。此时Server进程是客户端,ServiceManger是服务端。
1.1 ProcessState
每个进程通过单例模式创建了唯一的ProcessState对象,在其构造器中,通过open_driver()方法打开了/dev/binder设备,相当于Server进程打开了与内核的Binder驱动交互的通道,并设置最大支持线程数为15。binder设备是Android在内核中为完成进程间通信而专门设置的一个虚拟设备。
1.2 BpBinder和BBinder
BpBinder与BBinder都是Android与Binder通信相关的代表,两者一一对应,都从IBinder派生而来。如果说BpBinder代表客户端,那么BBinder就代表服务端,一个BpBinder通过handler标识与对应的BBinder进行交互。在Binder系统,handler标识为0代表着ServiceManger所对应的BBinder。BpBinder与BBinder并没有直接操作ProcessState打开的binder设备。
1.3 BpServiceManger和BnserviceManger
两者继承至IServiceManger,与业务逻辑相关,可以说将业务层的逻辑架构到Binder机制上。BnserviceManger从IServiceManger BBinder派生而来,可直接参与Binder的通信,而BpServiceManger通过mRemote指向BpBinder。
1.4 注册相关Service
通过上文三小节,BpServiceManger对象实现对IServiceManger的业务函数,又有BpBinder作为通信代表,下面分析一下注册的过程。
将字符串名字和Service对象作为参数传到BpServiceManger对象的addService()函数,该方法将参数数据打包后传递给BpBidner的transact()函数。业务层的逻辑到此就结束,主要作用是将请求信息打包交给通信层去处理。
在BpBinder的transact()函数调用了IPCThreadState对象的 transact() 函数,所以说BpBinder本身没有参与Binder设备的交互。每个线程都有一个IPCThreadState对象,其拥有一个mOut、mIn的缓冲区,mOut用来存储转发Binder设备的数据,而mIn用来接收Binder设备的数据。通过ioctl方式与Binder设备进行交互。

1.5 小结

通过上文Service的注册过程,分析了Binder的机制。Binder只是通信机制,业务可以基于Binder机制,也可以基于其他IPC方式的机制,也就是上文为啥有BpServiceManger和BpBinder。Binder之所以复杂,是Android通过层层的封装,巧妙的将业务与通信融合在一起。主要还是设计理想很牛逼。

2、ServiceManger

通过1小节的分析,是否应该也有一个类继承自BnServiceManger来处理远方请求呢?
很可惜的是在服务端并没有BnServiceManger子类来响应远程客户端的请求,而是交给了ServiceManger来处理。
2.1 成为Service管理中心
ServiceManger通过binder_open函数打开binder设备,并映射内存。通过handler等于0标识自己,让自己成为管理中心,所有service向ServiceManger注册时,都是通过handle标识为的0的BpBinder找到ServiceManger对应的BBinder,ServiceManager会保存要注册的Service的相关信息,方便Client查找。并不是所有的Service都可以在ServiceManger注册,如果Server进程的权限不够root或system,那么需要在allowed添加相应的项。
2.2 ServiceManger集中管理带来的好处

  • 统一管理,施加管控权
  • 通知字符串名称查找Service
  • Server进程生命无常,通过ServiceManger,Client可以实时知道Server进程的最行动态。

3、Client

Client想要使用Server进程提供的Service,又该进行哪些步骤呢?
3.1 查询ServiceManger
Client想要得到某个Service的信息,就得与ServiceManager打交道,通过调用getService()方法来获取对应Service信息。Client通过服务名称向ServiceManger查询对应的Service。如果Service未注册,则循环等待直到该Service注册;如果已注册,则会对应封装了一个能与远程Service通信的BpBinder的BpXXXService,通过该Service,Client客户调用相关业务逻辑函数。
3.2 请求信息的处理
Client调用的业务函数,莫非就是将请求参数打包发送给Binder驱动,BpBinder通过handler的值找到对应端的Service来处理。
在1.4小节中,说到IPCThreadState对象,在其executeCommand函数中,通过调用实现了BnServiceXXX的对象onTransact函数,直接定位到业务层。这就是在AIDL中,为什么在onTransact()函数中处理响应数据。

四、总结

通过对Binder机制的学习,了解Android是如何通过层层封装将Binder机制集成要应用程序,对Binder机制有一个较深入的理解。可以通过第Java层AIDL的使用,加深对Binder机制的理解。

原文地址:https://blog.51cto.com/14295695/2417572

时间: 2024-10-10 00:00:37

Binder核心机制分析揭秘跨进程得实现原理的相关文章

Android系统篇之----Binder机制和远程服务调用机制分析

一.前景概要 最近要实现Android中免注册Activity就可以运行的问题,那么结果是搞定了,就是可以不用在AndroidManifest.xml中声明这个Activity即可运行,主要是通过骗取系统,偷龙转凤技术的,这个知识点后面会详细讲解的,因为在研究了这个问题过程中遇到了很多知识点,当然最重要也是最根本的就是Android中的Binder机制和远程服务调用机制,而关于Binder机制的话,在Android中算是一个非常大的系统架构模块了,光这篇文章是肯定不能讲解到全部的,而且本人也不是

Wayland中的跨进程过程调用浅析

原文地址:http://blog.csdn.net/jinzhuojun/article/details/40264449 Wayland协议主要提供了Client端应用与Server端Compositor的通信机制,Weston是Server端Compositor的一个参考实现.Wayland协议中最基础的是提供了一种面向对象的跨进程过程调用的功能,在作用上类似于Android中的Binder.与Binder不同的是,在Wayland中Client和Server底层通过domain socke

Wayland (三) Wayland中的跨进程过程调用浅析 [FW]

原文地址:http://blog.csdn.net/jinzhuojun/article/details/40264449 Wayland协议主要提供了Client端应用与Server端Compositor的通信机制,Weston是Server端Compositor的一个參考实现.Wayland协议中最基础的是提供了一种面向对象的跨进程过程调用的功能,在作用上类似于Android中的Binder.与Binder不同的是,在Wayland中Client和Server底层通过domain socke

Android Binder跨进程通信原理分析

出发前预备子弹 我们知道进程之间,虚拟地址不同,是不能直接通信的,这是一种保护机制.打开任务管理器,查看一下N多的进程,试想一下如果这些进程直接通信会带来什么后果? 而用户空间可以通过System calls(系统回调)与内核空间通信的,如果在内核空间中有一个模块,能够完成数据的转发,那么是不是两个进程就可以通信了呢?如下图: 上面提到一些用户空间.内核空间的概念,用户空间也能大概猜到是什么东西,而内核空间,就知道它是很底层的东西好了.而模块呢,可以简单的理解为实现一个功能的程序或一个硬件电路等

从AIDL开始谈Android进程间Binder通信机制

本文首先概述了Android的进程间通信的Binder机制,然后结合一个AIDL的例子,对Binder机制进行了解析. 概述 我们知道,在Android app中的众多activity,service等组件可以运行在同一进程中,也可以运行在不同进程中.当组件运行在同一进程中进行通信就显得比较简单,在之前的Android线程间通信机制中已经讲过了:而当它们运行在不同的进程中时,就需要使用我们本文中所要介绍的Binder机制了. Binder作为一种进程间通信机制,负责提供远程调用的功能(RPC),

Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的.另外,Messenger用来进程间进行数据传递但是却不能满足跨进程的方法调用,接下来我们来使用AIDL来实现跨进程方法调用,此前我们都是用Eclipse来实现的,这次我们看看在Android Studio中使用AI

Android IPC机制(五)用Socket实现跨进程聊天程序

相关文章: Android IPC机制(一)开启多进程 Android IPC机制(二)用Messenger进行进程间通信 Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用 Android IPC机制(四)用ContentProvider进行进程间通信 1.Socket简介 Socket也称作"套接字",是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信.它分为流式套接字和

Android中的跨进程通信方法实例及特点分析(二):ContentProvider

1.ContentProvider简单介绍 在Android中有些数据(如通讯录.音频.视频文件等)是要供非常多应用程序使用的.为了更好地对外提供数据,Android系统给我们提供了Content Provider使用,通过它能够訪问上面所说的数据.比如非常多音乐播放器中的扫描功能事实上就用到了Content Provider功能(当然,也有的播放器是自己去实现更底层的功能). 这种优点是统一管理,比方添加了某个音频文件,底层就会将这种变化通知Content Provider.从而当应用程序訪问

Binder的使用(跨进程——AIDL,非跨进程)

一.Binder类 1.作用:Binder是客户端与服务器端的通信的媒介(连接各种Manager的桥梁),客户端通过Binder对象获取服务器端提供的数据 (为什么要用Binder来提供数据呢,服务器不能自己传给客户端数据么?) 因为服务器端通过Binder创建接口暴露自身数据,能够防止其他黑客入侵,导致数据被偷走或者篡改,只能让其他用户获取我们想要给他们的数据,不让他们从服务器自己拿数据. 二.跨进程使用(实现客户端添加Book对象,服务器端接收对象并打印) 步骤一:创建Book.java类.