美文网首页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