前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android--Binder机制与AIDL

Android--Binder机制与AIDL

作者头像
aruba
发布2021-12-06 17:40:00
7670
发布2021-12-06 17:40:00
举报
文章被收录于专栏:android技术
关于安卓中的Binder机制,该博客很值得一看:Android Binder设计与实现 - 设计篇,其中深入到了底层原理实现,篇幅很长,需要耐心看完
Binder机制是安卓自己的一套跨进程通信机制,对此需要有进程的概念,Java--线程中有提到进程,当然Linux中自带了很多跨进程通信的方式:管道、消息队列、信号灯、信号、套接字、共享内存,其中很多都因为过时而无人问津了,Binder机制底层使用的是共享内存,共享内存也分两块,一个是内存映射(mmap),一个是内存共享(shm),具体可以查看该博客:mmap和shm共享内存的区别和联系
Binder使用的是内存映射(mmap),mmap可以在磁盘上开辟一块空间,作为内存,这样写操作就不需要缓存拷贝了,只需要直接拷贝到文件,用Java来说就是不需要调用write、flush方法,读取时也不需要再通过缓存拷贝到内存中
一、Binder机制简单介绍

Binder机制核心点就是利用mmap开辟一块空间,使得多个进程可以访问,不仅接收端持有该映射,内核空间也持有,由于相当于内存,所以拷贝只需要一次:从发送端进程的工作内存(用户空间)到主内存(内核空间)的内存映射中,接收端也就相当于接收到了,想要深入理解可以查看Java--深入理解JMM模型、Java并发特性

1.ServiceManager

跨进程通信需要一个管理者,而ServiceManager就是这个管理者,它存有一份Binder的映射表,一般我们创建的Binder实体后,都会在这个表创建映射关系

ServiceManager在开机时就启动了,ServiceManager自己也会创建一份Binder实体,每个App都可以获取到ServiceManager,因为ServiceManager的Binder实体对应的引用号是固定的,通过context.getSystemService最终拿到该Binder引用,来获取映射表中的一些其他系统服务,如LOCATION_SERVICE(定位服务)、NOTIFICATION_SERVICE(通知服务)等等,代码体现在SystemServiceRegistry类中,它会预先将系统服务的Binder引用存入缓存中

IBinder:Binder实体的引用

2.Binder的创建

首先服务端想要向外部进程开放通信,那么它会创建Binder实体,并将Binder引用注册到SystemService的映射表中,利用上面的模板,Binder的创建如下:

3.Client获取Binder引用进行通信

Service端搞定后,Client进程就可以通过ServiceManager获取Binder引用进行通信了,这里不是直接使用映射表的引用,而是新建一个引用来指向Binder

二、ADIL

ADIL是一个自动生成代码的工具,它能够帮助我们快速建立Binder相关代码,实现Binder机制的跨进程通信

1.service端
1.1 首先创建AIDL文件

我这边已经创建好了

内容如下

  • RequestData.aidl:
代码语言:javascript
复制
package com.aruba.serviceapplication;

// 发送数据的自定义实体类
parcelable RequestData;
  • ResponseData.aidl:
代码语言:javascript
复制
package com.aruba.serviceapplication;

// 返回数据的自定义实体类
parcelable ResponseData;
  • IMyAidlInterface.aidl:
代码语言:javascript
复制
package com.aruba.serviceapplication;

// 注意需要手动导入自定义的类
import com.aruba.serviceapplication.RequestData;
import com.aruba.serviceapplication.ResponseData;

interface IMyAidlInterface {
    ResponseData send(in RequestData  request);
}
1.2 创建对应的实体类

需要和aidl定义的同一个包名下创建,否则会找不到

  • RequestData:
代码语言:javascript
复制
package com.aruba.serviceapplication

import android.os.Parcel
import android.os.Parcelable

/**
 * 发送数据
 * Created by aruba on 2021/11/17.
 */
class RequestData(val data: String?) : Parcelable {
    constructor(parcel: Parcel) : this(parcel.readString()) {
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(data)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<RequestData> {
        override fun createFromParcel(parcel: Parcel): RequestData {
            return RequestData(parcel)
        }

        override fun newArray(size: Int): Array<RequestData?> {
            return arrayOfNulls(size)
        }
    }
}
  • ResponseData:
代码语言:javascript
复制
package com.aruba.serviceapplication

import android.os.Parcel
import android.os.Parcelable

/**
 * 返回数据
 * Created by aruba on 2021/11/17.
 */
class ResponseData(val data: String?) : Parcelable {
    constructor(parcel: Parcel) : this(parcel.readString()) {
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(data)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<ResponseData> {
        override fun createFromParcel(parcel: Parcel): ResponseData {
            return ResponseData(parcel)
        }

        override fun newArray(size: Int): Array<ResponseData?> {
            return arrayOfNulls(size)
        }
    }
}

非常简单,都是一个字符串成员变量

1.3 创建Service

Service在onBind方法返回Binder实体,最后会注册到ServiceManager的映射表,而aidl已经帮助我们自动生成创建Binder的代码,通过IMyAidlInterface.Stub对象的asBinder方法

IMyAidlInterface就是我们定义的aidl文件中的接口,编译后会自动生成IMyAidlInterface.java类

代码语言:javascript
复制
/**
 * Created by aruba on 2021/11/17.
 */
class MyService : Service() {
    private val binder by lazy {
        object : IMyAidlInterface.Stub() {
            override fun send(request: RequestData?): ResponseData {
                Log.i("service", "receive:${request?.data}")

                return ResponseData("i'm service message")
            }
        }.asBinder()
    }

    override fun onBind(intent: Intent?): IBinder? {
        return binder
    }
}

别忘了在manifest.xml中注册,并且给其他进程提供隐式调用

代码语言:javascript
复制
        <service
            android:name=".MyService"
            android:exported="true">
            <intent-filter>
                <action android:name="com.aruba.service" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
1.4 Activity中启动服务
代码语言:javascript
复制
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //启动服务,开启通信通道
        startService(Intent(this, MyService::class.java))
    }
}
2.Client端
2.1 复制aidl相关文件

Client端需要将aidl文件复制一份到项目中,并且自定义实体类的Java文件也需要复制,包名还需要和Service端一致

2.2 bindService

Activity中通过bindService,隐式调用Service,获取Service端的Binder引用,该Binder引用就是我们在aidl中定义的IMyAidlInterface接口,通过IMyAidlInterface.Stub.asInterface方法获取

代码语言:javascript
复制
        // 隐式调用
        val intent = Intent("com.aruba.service").apply {
            `package` = "com.aruba.serviceapplication"//设置服务端的包名
        }

        bindService(intent, object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                myAidlInterface = IMyAidlInterface.Stub.asInterface(service)
            }

            override fun onServiceDisconnected(name: ComponentName?) {
                myAidlInterface = null
            }

        }, 0)

最后写一个按钮,点击调用下send方法,发送消息到Service端

Actvity完整代码:

代码语言:javascript
复制
class MainActivity : AppCompatActivity() {
    var myAidlInterface: IMyAidlInterface? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 隐式调用
        val intent = Intent("com.aruba.service").apply {
            `package` = "com.aruba.serviceapplication"//设置服务端的包名
        }

        bindService(intent, object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                myAidlInterface = IMyAidlInterface.Stub.asInterface(service)
            }

            override fun onServiceDisconnected(name: ComponentName?) {
                myAidlInterface = null
            }

        }, 0)
    }

    /**
     * 发送消息
     */
    fun send(view: android.view.View) {
        // 调用Ibinder的send方法
        myAidlInterface?.send(RequestData("hello i'm client")).let {
            (findViewById(R.id.textView) as TextView).text = it?.data
        }
    }
}

效果:

Service端也打印了日志:

三、AIDL源码分析

查看生成的IMyAidlInterface.java文件,其源码如下,过下即可:

代码语言:javascript
复制
/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.aruba.serviceapplication;

public interface IMyAidlInterface extends android.os.IInterface {
    /**
     * Default implementation for IMyAidlInterface.
     */
    public static class Default implements com.aruba.serviceapplication.IMyAidlInterface {
        @Override
        public com.aruba.serviceapplication.ResponseData send(com.aruba.serviceapplication.RequestData request) throws android.os.RemoteException {
            return null;
        }

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

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.aruba.serviceapplication.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.aruba.serviceapplication.IMyAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.aruba.serviceapplication.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.aruba.serviceapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.aruba.serviceapplication.IMyAidlInterface))) {
                return ((com.aruba.serviceapplication.IMyAidlInterface) iin);
            }
            return new com.aruba.serviceapplication.IMyAidlInterface.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_send: {
                    data.enforceInterface(descriptor);
                    com.aruba.serviceapplication.RequestData _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.aruba.serviceapplication.RequestData.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    com.aruba.serviceapplication.ResponseData _result = this.send(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.aruba.serviceapplication.IMyAidlInterface {
            private android.os.IBinder mRemote;

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

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

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public com.aruba.serviceapplication.ResponseData send(com.aruba.serviceapplication.RequestData request) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.aruba.serviceapplication.ResponseData _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((request != null)) {
                        _data.writeInt(1);
                        request.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    boolean _status = mRemote.transact(Stub.TRANSACTION_send, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().send(request);
                    }
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.aruba.serviceapplication.ResponseData.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            public static com.aruba.serviceapplication.IMyAidlInterface sDefaultImpl;
        }

        static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        public static boolean setDefaultImpl(com.aruba.serviceapplication.IMyAidlInterface impl) {
            // Only one user of this interface can use this function
            // at a time. This is a heuristic to detect if two different
            // users in the same process use this function.
            if (Stub.Proxy.sDefaultImpl != null) {
                throw new IllegalStateException("setDefaultImpl() called twice");
            }
            if (impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.aruba.serviceapplication.IMyAidlInterface getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    public com.aruba.serviceapplication.ResponseData send(com.aruba.serviceapplication.RequestData request) throws android.os.RemoteException;
}
1.Stub类

把多余的代码去掉,首先看Stub类,Stub继承至Binder ,是真正的Binder实现类,我们在Service中也是实例化了一个Stub,其中asInterface方法是给Client端调用的,asBinder方法就是返回了自己,Service端不调用也可以,Binder类的核心方法是onTransact方法,Client调用aidl接口的方法后,最终这个方法会接收到消息,并调用代理相应的方法,也就是我们在Service中实例化Stub时重写的方法,最后底层通过mmap写回数据

代码语言:javascript
复制
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.aruba.serviceapplication.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.aruba.serviceapplication.IMyAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.aruba.serviceapplication.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.aruba.serviceapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.aruba.serviceapplication.IMyAidlInterface))) {
                return ((com.aruba.serviceapplication.IMyAidlInterface) iin);
            }
            return new com.aruba.serviceapplication.IMyAidlInterface.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_send: {
                    data.enforceInterface(descriptor);
                    com.aruba.serviceapplication.RequestData _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.aruba.serviceapplication.RequestData.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    // 调用Service本地代理的send方法,返回结果
                    // 就是我们在Service中实例化Stub时写的回调函数
                    com.aruba.serviceapplication.ResponseData _result = this.send(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        // 把数据写回Binder中
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
2.Proxy类

除了Service端本地有一份默认的Proxy实例,用来调用回调方法外,其他的都是给Client生成的,其中mRemote为Client端bindService时获取到的IBinder,即Binder引用

代码语言:javascript
复制
        private static class Proxy implements com.aruba.serviceapplication.IMyAidlInterface {
            private android.os.IBinder mRemote;

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

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

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public com.aruba.serviceapplication.ResponseData send(com.aruba.serviceapplication.RequestData request) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.aruba.serviceapplication.ResponseData _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((request != null)) {
                        _data.writeInt(1);
                        request.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    // 发送消息,最终调用Binder的onTransact方法,该方法为阻塞方法
                    boolean _status = mRemote.transact(Stub.TRANSACTION_send, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {// 说明在一个进程中
                        return getDefaultImpl().send(request);
                    }
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        // client端读取数据,反序列化
                        _result = com.aruba.serviceapplication.ResponseData.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            public static com.aruba.serviceapplication.IMyAidlInterface sDefaultImpl;
        }

最后附上一张图,展示Binder机制发送消息调用流程

项目地址:https://gitee.com/aruba/aidlapplication.git
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/12/5 下,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 关于安卓中的Binder机制,该博客很值得一看:Android Binder设计与实现 - 设计篇,其中深入到了底层原理实现,篇幅很长,需要耐心看完
  • Binder机制是安卓自己的一套跨进程通信机制,对此需要有进程的概念,Java--线程中有提到进程,当然Linux中自带了很多跨进程通信的方式:管道、消息队列、信号灯、信号、套接字、共享内存,其中很多都因为过时而无人问津了,Binder机制底层使用的是共享内存,共享内存也分两块,一个是内存映射(mmap),一个是内存共享(shm),具体可以查看该博客:mmap和shm共享内存的区别和联系
  • Binder使用的是内存映射(mmap),mmap可以在磁盘上开辟一块空间,作为内存,这样写操作就不需要缓存拷贝了,只需要直接拷贝到文件,用Java来说就是不需要调用write、flush方法,读取时也不需要再通过缓存拷贝到内存中
  • 一、Binder机制简单介绍
    • 1.ServiceManager
      • 2.Binder的创建
        • 3.Client获取Binder引用进行通信
        • 二、ADIL
          • 1.service端
            • 1.1 首先创建AIDL文件
            • 1.2 创建对应的实体类
            • 1.3 创建Service
            • 1.4 Activity中启动服务
          • 2.Client端
            • 2.1 复制aidl相关文件
          • 2.2 bindService
          • 三、AIDL源码分析
            • 1.Stub类
              • 2.Proxy类
                • 项目地址:https://gitee.com/aruba/aidlapplication.git
                相关产品与服务
                消息队列 CMQ
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档