美文网首页Android开发经验谈Binder机制
Android进程间通讯(一)Binder介绍及AIDL的具体使

Android进程间通讯(一)Binder介绍及AIDL的具体使

作者: 程序员三千_ | 来源:发表于2020-04-01 09:59 被阅读0次

    Android进程间通讯(一)Binder介绍及AIDL的具体使用
    Android进程间通讯(二)AIDL、Binder源码分析

    首先我们要明白几个概念

    image.png
    • Binder是什么?
      Binder 是IPC的通讯机制,是一种机制,实际上我们四大组件的通讯就是用的Binder

    • Binder原理
      为了保护进程空间不被别的进程破坏或者干扰,Linux的进程是相互独立的(进程隔离),而且一个进程空间还分为用户空间和内核(Kernel)空间,相当于把Kernel和上层的应用程序抽像的隔离开。这里有两个隔离,一个进程间是相互隔离的,二是进程内有用户和内核的隔离。


      Linux进程
    • Binder框架定义了四个角色:Server,Client,ServiceManager以及Binder驱动。其中Server,Client,ServiceManager运行于用户空间,驱动运行于内核空间。这四个角色的关系类似:Server是服务器,Client是客户终端,ServiceManager是服务注册中心。

    Binder是Android系统进程间通信(IPC)方式之一,但是为什么Android不使用Linux本生具有的IPC方式?

    Binder优点

    • 性能方面
      在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder相对出传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。
    • 安全方面
      首先传统IPC的接收方无法获得对方进程可靠的UID和PID(用户ID进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。其实Binder机制主要是在本机内进行跨进程通信,而socket等IPC方式主要是用于跨网络的通信手段,因而socket不能限制了入口和出口,所以从使用场景来看,socket给用户提供的使用范围更广,而binder由于为了保证它的数据安全性,必须限制于android系统的机子中,而且在内核中就已经对UID进行了验证。

    AIDL是什么?

    AIDL是Android为了方便使用Binder机制出现的接口定义语言,只是Android中众多进程间通讯方式中的一种方式。其实不用这个也能达到目的,但是用这个就简化了操作。只要按照规范写一个.aidl文件,系统会帮助自动创建一个与interface同名的.java文件,里面已经帮我们自动写好一堆标准代码,这些标准化的东西,用模板自动生成即可,让开发者尽量关注功能实现上。

    通俗的来讲,AIDL的作用就是,让你可以在自己的APP里绑定另一个APP的service,这样就实现了两个app间的交互,而这两个app的交互的规则具体表现就是通过.aidl文件里定义的接口里面的一些方法。

    IPC(Inter-Process Communication:进程间通信。
    AIDL(Android Interface definition language):Android接口定义语言。
    Binder :IPC的通讯机制。

    AIDL使用例子

    因为是两个app间的数据交互,我这里新建了两个工程,一个代表客户端一个代表服务端,客户端和服务端都存在一个ILeoAidl接口

    • 客户端和服务端公用的ILeoAidl接口和实体对象类
    package com.xx.leo_service;
    
    // Declare any non-default types here with import statements
    
    import com.xx.leo_service.Person;
    
    interface ILeoAidl {
        void addPerson(in Person person);
    
        List<Person> getPersonList();
    }
    
    // Person.aidl
    package com.xx.leo_service;
    
    // Declare any non-default types here with import statements
    
    parcelable Person;
    
    package com.xx.leo_service;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class Person implements Parcelable {
    
        private String name;
        private int grade;
    
        public Person(String name, int grade) {
            this.name = name;
            this.grade = grade;
        }
    
        protected Person(Parcel in) {
            this.name = in.readString();
            this.grade = in.readInt();
        }
    
        public static final Creator<Person> CREATOR = new Creator<Person>() {
            @Override
            public Person createFromParcel(Parcel in) {
                return new Person(in);
            }
    
            @Override
            public Person[] newArray(int size) {
                return new Person[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(grade);
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", grade=" + grade +
                    '}';
        }
    }
    
    
    
    • 客户端目录结构


      image.png
    • 主要代码
    //绑定服务
     private void bindService() {
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.xx.leo_service", "com.xx.leo_service.LeoAidlService"));
            bindService(intent, connection, Context.BIND_AUTO_CREATE);
        }
    
    
        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(TAG, "onServiceConnected: success");
                iLeoAidl = ILeoAidl.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, "onServiceDisconnected: success");
                iLeoAidl = null;
            }
        };
    
    
       private void initView() {
            btn = (Button) findViewById(R.id.but_click);
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        iLeoAidl.addPerson(new Person("小三爷", 3));
                        List<Person> persons = iLeoAidl.getPersonList();
                        Log.e(TAG, persons.toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    

    主要就是绑定aidl服务,连接服务获取aidl对象,然后通过一个按钮,调用aidl对象的addPerson方法添加一个person,然后通过iLeoAidl接口获取list数组里的值和list的长度,并且打印日志

    • 服务端目录结构


      image.png

    服务端主要是新建一个service,并且启动这个service就ok

    • 主要代码
    //清单文件里记得声明下这个服务
      <service
                android:name=".LeoAidlService"
                android:exported="true" />
    
    //1、新建服务
    public class LeoAidlService extends Service {
    
        private ArrayList<Person> persons;
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            persons = new ArrayList<>();
            Log.e("LeoAidlService", "success onBind");
            return iBinder;
        }
    
        private IBinder iBinder = new ILeoAidl.Stub() {
            @Override
            public void addPerson(Person person) throws RemoteException {
                persons.add(person);
            }
    
            @Override
            public List<Person> getPersonList() throws RemoteException {
                return persons;
            }
        };
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e("LeoAidlService", "onCreate: success");
        }
    }
    
    //2、启动服务
    startService(new Intent(this, LeoAidlService.class));
    
    
    image.png

    好,我们现在根据代码再分析下流程
    1、服务端和客户端都定义一个aidl接口,接口里有增加和显示list的方法
    2、客户端点击按钮,调用aidl的接口方法,往list里面增加数组,然后日志打印数据

    结论

    运行代码,第一次先把客户端和服务端app都运行起来,

    • 现象一:只要服务端的app没杀死(客户端app随便杀死不杀死),每次点击客户端增加按钮list数组里的person都会+1.
    • 现象二:如果把服务端端杀死了(客户端会连接会中断onServiceDisconnected),这时再运行客户端再运行起来,list数组数量就会从0开始了。
      这两种现象说明,客户端的数据确实不是存在客户端的,而是通过aidl接口调用到服务端代码里的方法,来增加和显示数据的,其实这些数据都是服务端的数据,只是客户端获得了控制服务端数据操作的“遥控器”

    那么问题来了,1、客户端是怎么获取这个遥控器的呢?2、这个遥控器又是怎么调用到服务端代码的呢?3、就算把服务端app杀死了,客户端为什么还能调用到服务端的代码?我们直接进入下一篇文章:Android进程间通讯(二)AIDL源码分析

    相关文章

      网友评论

        本文标题:Android进程间通讯(一)Binder介绍及AIDL的具体使

        本文链接:https://www.haomeiwen.com/subject/esnwuhtx.html