Android IPC 之 AIDL 原理

简介: Android IPC 之 AIDL 原理

在上篇文章中我们使用 AIDL 完成了进程之间的通信,下面我们分析一下 AIDL 文件的实现类


aidl 文件如下:


/**
 * 连接服务
 */
interface IConnectionService {
   oneway void connect();
    void disconnect();
    boolean isConnected();
}


实现类在 gen/aidl… 目录下面,如下:


/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.www.mk_ipc_demo;
/**
 * 杩炴帴鏈嶅姟
 */
public interface IConnectionService extends android.os.IInterface {
    /**
     * Default implementation for IConnectionService.
     */
    public static class Default implements com.www.mk_ipc_demo.IConnectionService {
        @Override
        public void connect() throws android.os.RemoteException {
        }
        @Override
        public void disconnect() throws android.os.RemoteException {
        }
        @Override
        public boolean isConnected() throws android.os.RemoteException {
            return false;
        }
        @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.www.mk_ipc_demo.IConnectionService {
        private static final java.lang.String DESCRIPTOR = "com.www.mk_ipc_demo.IConnectionService";
        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an com.www.mk_ipc_demo.IConnectionService interface,
         * generating a proxy if needed.
         */
        public static com.www.mk_ipc_demo.IConnectionService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //判断当前进程和服务进程是否为一个,如果是一个,则不需要进行 IPC 通信
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.www.mk_ipc_demo.IConnectionService))) {
                return ((com.www.mk_ipc_demo.IConnectionService) iin);
            }
            //生成 IConnectionService 服务的代理
            return new com.www.mk_ipc_demo.IConnectionService.Stub.Proxy(obj);
        }
        //返回 IBinder 对象
        @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;
             //根据传递过来的 code 来判断要触发那个方法
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_connect: {
                    //校验是不是为同一个 DESCRIPTOR
                    data.enforceInterface(descriptor);
                     //触发具体实现
                    this.connect();
                    return true;
                }
                case TRANSACTION_disconnect: {
                    data.enforceInterface(descriptor);
                    this.disconnect();
                     //disconnect 方法没有被 oneway 标记,所以需要一个返回值
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_isConnected: {
                    data.enforceInterface(descriptor);
                    boolean _result = this.isConnected();
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
        private static class Proxy implements com.www.mk_ipc_demo.IConnectionService {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            //返回 IBinder 对象
            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }
    //返回标记
            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }
            //代理实现的 connect ,主进程调用时会触发的方法
            @Override
            public void connect() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    //写入标记,用于在子进程接收到这个 binder 之后能知道这个 Parcel 是来自哪个接口
                    _data.writeInterfaceToken(DESCRIPTOR);
                      //1,标识请求的是哪个方法,2,发送的数据,3,不需要返回数据,不需要等待子进程的回复
            //4,是一个标志位 FLAG_ONEWAY,因为我们定义的时候使用了 oneway 关键字
                    boolean _status = mRemote.transact(Stub.TRANSACTION_connect, _data, null, android.os.IBinder.FLAG_ONEWAY);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().connect();
                        return;
                    }
                } finally {
                    _data.recycle();
                }
            }
            @Override
            public void disconnect() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //3,因为没有标记 oneway,所以需要返回数据,所以传入 reply
                    boolean _status = mRemote.transact(Stub.TRANSACTION_disconnect, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().disconnect();
                        return;
                    }
                     // 因为没有标记 oneway,所以需要去读取 IPC 调用的结果,读取过程中可能会出现异常
                    _reply.readException();
                } finally {
                    //回收
                    _reply.recycle();
                    _data.recycle();
                }
            }
            @Override
            public boolean isConnected() throws android.os.RemoteException {
                //发送过去的数据是通过 _data
                android.os.Parcel _data = android.os.Parcel.obtain();
                 //返回回来的数据是通过 _reply
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    //写入标记
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_isConnected, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().isConnected();
                    }
                    _reply.readException();
                    //获取返回的数据
                    _result = (0 != _reply.readInt());
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
            public static com.www.mk_ipc_demo.IConnectionService sDefaultImpl;
        }
        static final int TRANSACTION_connect = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_disconnect = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_isConnected = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        public static boolean setDefaultImpl(com.www.mk_ipc_demo.IConnectionService impl) {
            if (Stub.Proxy.sDefaultImpl == null && impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }
        public static com.www.mk_ipc_demo.IConnectionService getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }
    public void connect() throws android.os.RemoteException;
    public void disconnect() throws android.os.RemoteException;
    public boolean isConnected() throws android.os.RemoteException;
}


可以看到他内部有 三个方法,分别对应着 AIDL 中定义的三个方法。还有一个默认的实现类,实现了这三个方法。这个不是重点,重点是 抽象类Stub


Stub 就是一个 Binder 类,实现了IConnectionService,当客户端和服务端位于同一个进程时,方法调用不会走跨进程的 transact 方法,当两者唯一不同进程时,方法调用需要走 transact 过程,这个逻辑有 Proxy来完成,在 asInterface 中就会判断是否为同一个进程,如果不是则就会返回 内部的代理类 Proxy


下面介绍一下这两个类的方法和含义


DESCRIPTOR


Binder 的唯一标识,一般用当前类名表示


asInterface(android.os.IBinder obj)


由客户端调用,用户将服务端的 Binder 对象转为客户端所需要的 AIDL 接口类型的对象。在内部判断了是否Wie同一个进程,如果是同一个进程,返回的就是服务端 Stub 对象本身,否则返回的是系统封装后的 Stub.proxy 对象,这个对象中实现了跨进程的调用


asBinder


返回当前 Binder 对象


onTransact


这个方法运行在服务端的 Binder 线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交给此方法处理。该方法的原型为: public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服务端通过 code 确定客户端请求的方法是啥,从 data 中取出目标方法所需要的参数(如果目标方法有参数),然后执行目标方法。目标方法执行完后就向 reply 中写入返回值(如果有返回值)。


调用目标方法,这个目标方法其实就是我们在服务端实现的 Stub 的方法


onTransact 的执行过程就是这个样子。要注意的是如果此方法返回 false,那么客户端的请求会失败。


Proxy#isConnected


这个方法执行在客户端,当客户端调用此方法时,内部实现是这样的:首先创建输入型 Parcel 对象_data,输出型 Parcel 对象_ _replay 和 返回值 boolean _result。然后把该方法的参数写入_data 中(如果有参数的话),接着调用 transact 方法来发起RPC(远程过程调用)请求,同时将当前线程挂起,然后服务端的 onTransact(就是上面的方法) 方法被调用,直到 RPC 过程结束返回后当前线程继续执行,并从 _replay 中取出 RPC 过程的返回结果。最后返回 ——reply 中的数据


Proxy#connect


这个方法执行在客户端,执行过程和 isConnected 是一样的。只不过 connect 没有返回值,所以不用从_reply 中取出返回值。


需要注意的地方:


发起远程请求时,当前线程会被挂起,直至服务端进程返回数据,所以这个方法时很耗时的,尽量不要在 UI 线程中发起远程请求。当然可以使用 oneway 关键字 ,使用后当前线程不会进行挂起,而是继续往下执行,但是使用 这个关键字的方法不能有返回值

由于服务的的 Binder 方法运行在 Binder 的线程池中,所以 Binder 方法不管是否耗时都应该采用同步的方式去实现。


执行流程


客户端 通过 asInterface 拿到 AIDL 的接口类型的对象,并且内部判断是否为同一个进程。接着使用 接口类型的对象调用 AIDL的方法,如果是不同的进程就会调用的代理 Proxy ,然后创建 发送数据和接收数据(根据方法的需求),然后调用 transact 方法发起远程调用请求,当前线程挂起,等待远程带调用结果。拿到结果后最终返回给客户端

0a2653c851af460fa595bd959398a8f1.png

相关文章
|
安全 Android开发 Kotlin
Android经典实战之SurfaceView原理和实践
本文介绍了 `SurfaceView` 这一强大的 UI 组件,尤其适合高性能绘制任务,如视频播放和游戏。文章详细讲解了 `SurfaceView` 的原理、与 `Surface` 类的关系及其实现示例,并强调了使用时需注意的线程安全、生命周期管理和性能优化等问题。
491 8
|
10月前
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
465 56
|
11月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
267 3
|
编解码 前端开发 Android开发
Android经典实战之TextureView原理和高级用法
本文介绍了 `TextureView` 的原理和特点,包括其硬件加速渲染的优势及与其他视图叠加使用的灵活性,并提供了视频播放和自定义绘制的示例代码。通过合理管理生命周期和资源,`TextureView` 可实现高效流畅的图形和视频渲染。
778 12
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
234 2
|
11月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
260 0
|
SQL 安全 Java
Android经典面试题之Kotlin中object关键字实现的是什么类型的单例模式?原理是什么?怎么实现双重检验锁单例模式?
Kotlin 单例模式概览 在 Kotlin 中,`object` 关键字轻松实现单例,提供线程安全的“饿汉式”单例。例如: 要延迟初始化,可使用 `companion object` 和 `lazy` 委托: 对于参数化的线程安全单例,结合 `@Volatile` 和 `synchronized`
254 6
|
存储 监控 数据库
Android经典实战之OkDownload的文件分段下载及合成原理
本文介绍了 OkDownload,一个高效的 Android 下载引擎,支持多线程下载、断点续传等功能。文章详细描述了文件分段下载及合成原理,包括任务创建、断点续传、并行下载等步骤,并展示了如何通过多种机制保证下载的稳定性和完整性。
469 0
|
安全 网络协议 算法
Android网络基础面试题之HTTPS的工作流程和原理
HTTPS简述 HTTPS基于TCP 443端口,通过CA证书确保服务器身份,使用DH算法协商对称密钥进行加密通信。流程包括TCP握手、证书验证(公钥解密,哈希对比)和数据加密传输(随机数加密,预主密钥,对称加密)。特点是安全但慢,易受特定攻击,且依赖可信的CA。每次请求可能复用Session ID以减少握手。
205 2
|
大数据 Android开发
Android使用AIDL+MemoryFile传递大数据
Android使用AIDL+MemoryFile传递大数据
201 0

热门文章

最新文章