源码分析——从aidl的使用开始理解binder进程间通信的流程

终身开发者 终身开发者     2022-08-25     711

关键词:

 

源码分析——从AIDL的使用开始理解Binder进程间通信的流程

Binder通信是Android系统架构的基础。本文尝试从AIDL的使用开始理解系统的Binder通信。

0x00 一个AIDL的例子

首先我们创建一个项目,写一个RemoteService.java,并定义个AIDL接口IRemoteService.aidl

interface IRemoteService {
    String getText();
}

这时候IDE会自动在目录build/generated/source/aidl/debug/生成IRemoteService.java文件。

本文为了方便调试和理解AIDL的过程,我们把生成的IRemoteService.java文件拷贝出来,放在app/main/java目录下,然后把aidl文件夹删除。

RemoteService为服务端,MainActivity为客户端。最后项目结构为

最终项目结构

0x01 远程服务RemoteService

public class RemoteService extends Service {
    public final static String ACTION = "net.angrycode.RemoteService";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    /**
     * 定义远程服务对外接口
     */
    IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public String getText() throws RemoteException {
            return "text from remote,pid:" + Process.myPid();
        }
    };
}

RemoteService中定义IBinder接口,并在onBind()方法中返回,供客户端使用。

最后在mainifest文件中注册远程服务,指定进程为私有进程

<service android:name=".RemoteService"
    android:process=":remote">
    <intent-filter>
        <action android:name="net.angrycode.RemoteService"/>
    </intent-filter>
</service>

0x02 本地客户端MainActivity

public class MainActivity extends AppCompatActivity {
    private TextView mTextMessage;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextMessage = (TextView) findViewById(R.id.message);
    }

    public void onClickBind(View view) {
        Intent service = new Intent(this, RemoteService.class);
        service.setAction(RemoteService.ACTION);
        bindService(service, conn, Context.BIND_AUTO_CREATE);
    }
    public void onClickUnBind(View view) {
        unbindService(conn);
    }
    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IRemoteService iRemoteService = IRemoteService.Stub.asInterface(service);
            try {//连接之后获取到远程服务text
                String text = iRemoteService.getText();
                mTextMessage.setText(text);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Toast.makeText(getApplication(), "远程服务已连接", Toast.LENGTH_LONG).show();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(getApplication(), "远程服务已断开", Toast.LENGTH_LONG).show();
        }
    };
}

本地客户端实现了ServiceConnection接口,用于监听远程服务的连接状态,并在onServiceConnected()中拿到远程服务RemoteService对外的接口IRemoteService的引用。

当客户端进行绑定远程服务时,就使用IRemoteService.Stub.asInterface(IBinder)获取到远程服务对象,客户端与服务端的通信就开始了。

运行结果

0x03 IRemoteService接口

系统自动生成的这个文件中有除了我们定义getText()方法外还生成了两个内部类StubProxy

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends Binder implements IRemoteService {
        private static final java.lang.String DESCRIPTOR = "net.angrycode.learnpro.IRemoteService";

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

        /**
         * Cast an IBinder object into an net.angrycode.learnpro.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static IRemoteService asInterface(IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof IRemoteService))) {
                return ((IRemoteService) iin);
            }
            return new IRemoteService.Stub.Proxy(obj);
        }

        @Override
        public IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getText: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getText();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements IRemoteService {
            private 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 java.lang.String getText() throws RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getText, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    public java.lang.String getText() throws android.os.RemoteException;
}

Stub类继承于Binder,但它们都实现了IRemoteService接口。

Binder是何物呢?

Base class for a remotable object, the core part of a lightweight remote procedure call mechanism defined by Binder.This class is an implementation of IBinder that provides standard local implementation of such an object.

可以看出Binder是一个远程对象,它实现了提供本地标准接口的IBinder

Stub-Proxy

Stub类代表着远程服务,而Proxy代表着远程服务在本地的代理。

0x04 获取Binder对象

在客户端MainActivity中,绑定远程服务之后,使用IRemoteService.Stub.asInterface()方法获取到远程服务的Binder对象。

/**
 * Cast an IBinder object into an net.angrycode.learnpro.IRemoteService interface,
 * generating a proxy if needed.
 */
public static net.angrycode.learnpro.IRemoteService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof net.angrycode.learnpro.IRemoteService))) {
        return ((net.angrycode.learnpro.IRemoteService) iin);
    }
    return new net.angrycode.learnpro.IRemoteService.Stub.Proxy(obj);
}

这个方法先查找本地是否存在这个对象,存在则返回;不存在则返回一个Proxy对象。

通过定点调试,可以知道当RemoteService在子进程中时,asInterface(obj)参数是一个BinderProxy对象,这个是远程服务进程的代理类。这个时候返回给客户端的是Proxy对象。

binder-asinterface

客户端与服务端不在同一进程时,通过BinderProxy进行通信

当把manifestRemoteServiceandroid:process=':remote'配置去掉时,asInterface(obj)的参数的传递就是RemoteService$1,其实就是RemoteService里面的内部类Stub

同进程中的IBinder对象就是RemoteService

然后我们再回到多进程的流程来,跳转到Proxy

0x05 Proxy.transact()

通过名字知道Proxy就是远程服务的代理,它持有Binder的引用。当客户端调用iRemoteService.getText()时其实是进入到Proxy类中getText()方法。

public java.lang.String getText() throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.lang.String _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_getText, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readString();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

首先获取到两个Parcel对象,这个是进程间通信的数据结构。_data_reply分别为getText()需要传递的参数和返回值,getText()无需参数,只有String类型返回值。

然后调用mRemotetransact()方法(其实就是调用BinderProxytransact()方法)。然后通过_reply获取到执行方法后的返回值,这里就是一个RemoteService里面实现的String

Proxy中执行transact()方法后又回调到哪里了呢?

onTransact()方法中设置一个断点,通过调试,我们发现其实是回调到了Stub类中onTransact()方法

0x06 Stub.onTransact()

public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getText: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getText();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

onTransact()方法中第一个参数code是与transact()第一个参数code是对应的,这是客户端与服务端约定好的常量。

这时候会执行到onTransact()方法中的_result = this.getText()方法。而Stub类是在RemoteService中实现的,故就访问到远程服务中资源了。

0x07 总结

通过以上流程分析可以知道,通过bindService绑定一个服务之后在onServiceConnected()中拿到了远程服务的在本地的Proxy,通过它与远程服务进行通信。

Binder在App中的流程

微信关注我们,可以获取更多

查看详情

binder机制aidl分析(分析aidl文件生成的java源文件|binder|ibinder|stub|proxy)(代码片段)

文章目录前言一、分析AIDL文件生成的Java源文件1、IMyAidlInterface.java中的类结构2、DESCRIPTOR描述符3、Stub构造方法4、Stub.asInterface方法5、Stub.onTransact方法6、Stub.Proxy代理类前言在上一篇博客【Binder机制】AIDL分析(创建AIDL文件|创建Parce... 查看详情

binder源码解析(从客户端到服务端代码流程)

Binder解析(从客户端到服务端代码流程)首先从一个例子开始服务端代码:publicclassWeatherServiceextendsServiceIWeatherInterface.Stubstub=newIWeatherInterface.Stub()@OverridepublicStringgetWeatherInfo(longtimeMilli)throwsRemoteExceptionreturn"五一劳动节&#... 查看详情

binder机制在aidl中的实现分析(代码片段)

...#xff1a;任玉刚和WeiShu一一个AIDLDemo的组成部分二通信机制的分析1bindService与onBind2代理是如何传递过去的呢3数据传递的实现三小结一、一个AIDLDemo的组成部分当我们想对一个自定义数据类型(如:Book)来实现跨进程通信... 查看详情

binder机制aidl分析(创建aidl文件|创建parcelable类|aidl中使用parcelable类|编译工程生成aidl对应的java源文件)(代(代码片段)

文章目录一、创建AIDL文件1、创建AIDL目录2、创建AIDL文件3、创建Parcelable类4、AIDL目录下声明Parcelable类5、AIDL中使用Parcelable类二、编译工程生成AIDL文件对应的Java源文件1、编译工程2、生成的AIDL对应Java源文件一、创建AIDL文件1、创... 查看详情

binder机制aidl分析(aidl通信完整流程梳理)(代码片段)

文章目录AIDL跨进程通信完整流程梳理1、AIDL文件编译2、注册服务3、IMyAidlInterface.Stub.asInterface方法获取远程服务4、IMyAidlInterface.Stub.Proxy代理类5、IMyAidlInterface.Stub.Proxy代理类方法执行6、Binder.transact方法执行7、IMyAidlInterface.Stub.onTra... 查看详情

binder详解(代码片段)

...不会深入探讨Binder驱动以及底层的实现,而是以AIDL的使用理清Java层面的Binder的实现和通信过程。      本文依然按照惯例,先总结下AIDL的使用步骤,然后根据日志、调试等信息整理其调用逻辑。如果还不知道AIDL... 查看详情

基于binder的跨进程通讯之使用aidl实现demo(代码片段)

写在前面上一篇我们介绍了binder机制的基本知识,如果还不太了解binder机制,可进行点击查看:让你一看就明白的Binder机制binder和AIDL的关系从应用层的角度来说,Binder类是android中的一个类,它实现了IBinder接... 查看详情

binder的工作机制浅析

...其中Messenger的底层实现就是AIDL,所以我们这里通过AIDL来分析一下Binder的工作机制。一、在AndroidStudio中建立AIDL首先,我们需要建立一个AIDL1.在建立了对应的实现Parcelable接口的实体类和AIDL接口后,文件结构如下:2.点击cleanProject/... 查看详情

androidframework实战开发-binder专题讲解之aidl文件的详细分析(代码片段)

大家平时做应用开发时候也经常会遇到有跨进程通信的需求,这里大部分通信可能就会回答,就是用aidl,service。确实这个aidl和service的方式是应用开发中对binder接触层面应该属于最为接近的一层。因为其他的接口方... 查看详情

ipc——浅谈aidl的架构原理

...der实现进程间的通信。然后在IPC(四)_Aidl的基本使用过程实现了如何通过Aidl实现进程间的通信。翻看代码的编写过程,其实大体上都差不多,而且也提到Aidl实质上就是对纯Binder通信进行了一次封装,毕竟IBin... 查看详情

androidframework实战开发-binder专题讲解之aidl文件的详细分析(代码片段)

csdn在线学习课程,课程咨询答疑和新课信息:QQ交流群:422901085进行课程讨论android跨进程通信实战视频课程(加群获取优惠)大家平时做应用开发时候也经常会遇到有跨进程通信的需求,这里大部分通信... 查看详情

androidbinder原理

参考技术A以前看源码经常会看到Binder的东西,比如AMS,ActivityThread等,之前虽然对Binder有所了解,但也是模模糊糊的,这次终于下定决心好好的弄一弄它的原理,揭开它头上的那块面纱。首先,Binder主要是Android跨进程通信的一种... 查看详情

从aild与bindservice谈binder进程间通信原理(上)

...章主要就是来谈谈Binder机制实现进程间通信的原理,主要分析AIDL进程间通信和bindService方法涉及的进程间数据传输相关逻辑。AIDL实现进程间通信:通过AIDL具体如何实现进程间通信,我推荐阅读以下文章:Android:学... 查看详情

不得不说的androidbinder机制与aidl(代码片段)

...统服务和上层应用。只有了解了Binder机制才能更加深入的理解Android开发和AndroidFramework。这也是为什么无论是《Android开发艺术探索》还是《深入理解Android内核设计思想》这些进阶类书籍把进程间通信和Binder机制放到靠前章节的... 查看详情

从aild与bindservice谈binder进程间通信原理(上)

...章主要就是来谈谈Binder机制实现进程间通信的原理,主要分析AIDL进程间通信和bindService方法涉及的进程间数据传输相关逻辑。AIDL实现进程间通信&# 查看详情

binder机制aidl分析(创建service服务|绑定service远程服务)(代码片段)

文章目录一、创建Service远程服务1、创建Service2、AndroidManifest.xml清单文件中配置Service二、绑定Service远程服务1、核心代码2、完整代码3、运行结果一、创建Service远程服务1、创建Servicepackagekim.hsl.aidl_demo;importandroidx.appcompat.app.AppCompa... 查看详情

toast源码分析与学习

源码查看网址,这个是当前我所用的源码地址,自备梯子知识点补充android使用注解替代枚举android进程间通信使用aidl和Messenger类源码分析涉及源码有些长,下面只截取了部分分析,githubtoast相关源码①从Toast.java中... 查看详情