Service之AIDL

本文详细介绍了Android中AIDL的使用方法及Binder机制的工作原理。通过实例演示了如何利用AIDL实现进程间通讯,包括服务端与客户端的设置过程。同时深入探讨了Binder机制在Android系统中的作用及其安全性优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引子

上一篇讲到了什么Service如何开启服务、绑定本地服务,这一篇说说AIDL(android接口定义语言),实现了进程间通讯(IPC机制 inner-process communication)

所谓IPC机制,即进程间通讯(Inter-Process Communication)。我们的应用有时候出于业务需要,可能是多进程的,而由于不同进程是不共享一个内存池的,所以进程之间不能直接通讯,而要通过一些特别的机制才能通讯,所以IPC机制是解决进程间通讯的一个方案

通俗讲就是在两个不同的app应用里实现通讯,A应用(客户端)调用B应用(服务端)里的一个Service的方法

实现步骤

  1. 先写一个服务端App应用BServiceApp,在BServiceApp里AndroidStudio里直接创建一个aidl文件
  2. 在这个IMyAidlInterface.aidl文件(相当于一个接口)里定义一个方法,这个方法就是你要在AClinetApp要调用的方法,写好后build一下。

    package com.mine.bserviceapp;
    
    interface IMyAidlInterface {
        int add(int a,int b);
        void sayHello(String str);
    }
    
  3. 定义一个可远程调用的Service

    1. 将一个普通的Service转换成远程Service其实非常简单,只需要在注册Service的时候将它的android:process属性指定成:remote就可以了,
    2. 构建Intent的时候是使用MyService.class来指定要绑定哪一个Service的,但是在另一个应用程序中去绑定Service的时候并没有MyService这个类,这时就必须使用到隐式Intent了。现在修改AndroidManifest.xml中的代码,给MyService加上一个action
     <service
                android:name=".MyService"
                android:process=":remote"//远程服务
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="com.mine.bserviceapp.MyService"/>
                </intent-filter>
            </service>
    package com.mine.bserviceapp;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.util.Log;
    
    public class MyService extends Service {
        public MyService() {
    
        }
    
        @Override
        public IBinder onBind(Intent intent) {
        //sub相当于本地服务中的Binder
            return stub;
        }
    
        IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {
            @Override
            public int add(int a, int b) throws RemoteException {
                return addInService(a, b);
            }
    
            @Override
            public void sayHello(String str) throws RemoteException {
                sayHelloInService(str);
            }
        };
    
        private int addInService(int a, int b) {
            return a + b;
        }
    
        private void sayHelloInService(String str) {
            Log.e("c", str);
        }
    }
    

    至此BServiceApp里的服务写完了,开始写AClientAPP,我们要在AClientAPP里的Activity里,通过aidl来调用BServiceApp里的MyService里的两个方法。

  4. 先在AClientAPP里新建一个aidl文件夹,在建立一个和BServiceApp一模一样路径的包,并且把其aidl文件也拷贝过来
    这里写图片描述

  5. 在Activity里远程绑定服务

    package com.mine.aclientapp;
    
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.Toast;
    
    import com.mine.bserviceapp.IMyAidlInterface;
    
    public class MainActivity extends AppCompatActivity implements ServiceConnection {
        boolean isBind = false;
        private IMyAidlInterface mAidlInterface;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void bind(View v) {
            Intent intent = new Intent("com.mine.bserviceapp.MyService");
            intent.setPackage("com.mine.bserviceapp");//必须显示地指定包名,在api23后
            bindService(intent, this, BIND_AUTO_CREATE);
        }
    
        public void add(View v) {
            if (isBind){
                try {
                    int add = mAidlInterface.add(198, 230);
                    Toast.makeText(this, "add:"+add, Toast.LENGTH_SHORT).show();
    
                } catch (RemoteException e) {
    
                }
            }
    
        }
    
        public void sayHello(View v) {
            if (isBind){
                try {
                    mAidlInterface.sayHello();
                } catch (RemoteException e) {
    
                }
            }
        }
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            Toast.makeText(this, "绑定成功", Toast.LENGTH_SHORT).show();
    
            isBind=true;
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
    
        }
    }
    

    点击绑定服务,可以绑定成功过,也可以调用方法了

AIDL解析

  1. 服务端onBind()方法里返回Stub对象,这个Stub是一个Binder的子类,需要实现具体要调用的方法,
  2. 客户端在绑定服务后,通过asInterface(跨进程为代理对象)方法得到aidl接口
  3. 客户端调用服务端方法后,通过transact方法把参数序列化组包,挂起等待服务端返回数据
  4. 客户端调用transact后,服务端的onTransact()方法,会去解包,拿到参数,调用服务端的方法,然后把结果返回给客户端
  5. 到现在,完成了aidl的跨进程通讯。
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/apple/AndroidStudioProjects/GoodDemo/LuckyHttpDemo/app/src/main/aidl/com/lucky/www/luckyhttpdemo/IAidlInterface.aidl
 */
package com.lucky.www.luckyhttpdemo;
// Declare any non-default types here with import statements

public interface IAidlInterface extends android.os.IInterface {
    /**
     * Stub:服务端使用    Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.lucky.www.luckyhttpdemo.IAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.lucky.www.luckyhttpdemo.IAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         * binder.attachInterface()
         * 将一个接口和Binder联系在一起,调用后,可以通过queryLocalInterface()查询本地接口方法,返回Stub对象
         * 
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.lucky.www.luckyhttpdemo.IAidlInterface interface,
         * generating a proxy if needed.
         * 把IBinder的对象强制转换为IAidlInterface的实例,如果必要的话,生成一个代理对象
         * 客户端会调用asInterface(binder)得到IAidlInterface的实例
         * 拿到IAidlInterface的实例,就可以调用这个接口的方法了
         */
        public static com.lucky.www.luckyhttpdemo.IAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
//            因为运行了attachInterface(this, DESCRIPTOR),所以可以在queryLocalInterface里找到Stub的对象
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//            如果stub对象iin为不为null,并且iin是IAidlInterface的实例,就把iin强制转换为IAidlInterface,返回
            if (((iin != null) && (iin instanceof com.lucky.www.luckyhttpdemo.IAidlInterface))) {
                return ((com.lucky.www.luckyhttpdemo.IAidlInterface) iin);
            }
//           因为queryLocalInterface方法是查询本地接口,所以,如果为跨进程通信的话,iin为null,这时候就要返回IAidlInterface的代理对象
            return new com.lucky.www.luckyhttpdemo.IAidlInterface.Stub.Proxy(obj);
        }

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

//        服务端通过这个方法进行解包操作,
//        客户端会通过binder.transact()方法,
//        把所有参数进行组包(序列化的数据),然后客户端挂起(等待replay接收binder发来的数据),
//        这时候,调用服务端的onTransact()方法,
//        onTransact()方法执行完后,客户端的transact()方法会受到数据
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

//     Proxy 客户端使用:  stub的代理对象,跨进程通过这个代理类返回stub对象
        private static class Proxy implements com.lucky.www.luckyhttpdemo.IAidlInterface {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             * 
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
//                    客户端组包数据
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

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

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     * 这个方法要服务端去实现
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

别人的总结

● 先简单说下三者的结构,是一个interface接口,里面有一个抽象类Stub(这个Stub是交给服务端去具体实现的),然后抽象类Stub里面有一个内部类Proxy(听名字就知道是给Stub做代理的)
● 这3者对应之前我们优化的过程,对比着看,其实是一样的,只是google将他们三个放在一起了,刚看这个生产的java感觉特别乱,但是你跟之前的优化过程对比一看就会立马清晰很多了。
● 当然了,生成的这个java和我们之前优化的有点不同,就是多了android.os.IInterface这个接口,所以Stub里面的asInterface方法(也就是我之前说的那个,同一个进程Binder直接返回,不然就用代理类封装的方法)也有点不一样。
● asInterface是一个static方法,提供客户端调用,然后通过queryLocalInterface()这个方法判断是否同一个进程,因为假如是服务端所在的进程请求,获得的是Binder实现类,初始化的时候Stub()会调用attachInterface(),其实就是自己把自己存起来了,后面如果queryLocalInterface()的话就能返回到对象。但是跨进程访问,返回的是BinderProxy,这个时候queryLocalInterface()只能是null了。

https://blog.youkuaiyun.com/cjh94520/article/details/71374872 Binder机制-简单用法(一)
https://www.jianshu.com/p/bdef9e3178c9 轻松理解 Android Binder,只需要读这一篇

Binder的一些理解

进程隔离

linux 系统是进程隔离,A进程不能访问B进程,
不同进程之间的数据不共享
如果要共享数据,那么就要跨进程通信机制IPC—-binder通讯机制

系统调用

在Linux内核中有一个重要的概念:系统调用,
我们对内核有一些保护机制告诉应用程序
只能访问某些许可资源,
这样把Linux内核层和上层应用程序抽象层抽象分开,也就是内核层和用户空间
用户可以通过系统调用在应用空间访问内核的某些程序

binder驱动

android运行在Linux内核中,负责各个应用进程,通过binder通讯的内核进行交互的模块叫做binder驱动
驱动程序一般指的是,设备的驱动程序 是计算机和设备进行通讯的特殊程序,其实也是一种软件,也是硬件的接口
操作系统,通过这个接口控制硬件设备

为什么使用binder

android使用Linux内核,Linux有自己的进程通信机制,管道、socket等
1. 性能,binder比较高效
2. 安全由于传统的进程间通讯,对通讯双方,没有做严格的验证,只是在上层协议进行的架构,例如IP地址是客户端手动填写的,可以人为的伪造,而binder机制从协议本身就支持通讯双方进行身份校验,提高了android安全性
身份校验也是android权限模型的基础

Binder通讯模型

客户端进程无法访问到服务端进程,如果没有binder进程间通讯机制的话
A通过通讯录找到B
A先找到通讯录,通讯录里保存了所有人的电话号码,A在通讯录里找到B的电话号码
然后通过电话基站,传递双方的电信号

两个进程中的应用程序要想进行通讯必须借助内核的帮助,这个运行在内核中的程序就是binder驱动,他的功能类似于电话基站
通讯录—-ServiceManager
电话基站—Binder驱动
这里写图片描述

Binder通信流程

需求:client要访问Service中的add方法(跨进程通讯)
1. Service向Servicemanager中注册一张表,告诉Servicemanager,我是server端,我这里有一个object对象,可以进行add操作,于是Servicemanager就建立好了一张表
2. 这时候,client向Servicemanager查询,server段有没有一个object对象,object对象有没有一个add方法
我们知道,进程之前通讯,数据都是在内核空间里,驱动会在数据流做一些手脚,他并不会给client返回一个真正的server端的object对象,因为这是无法进行操作的,而是返回一个object代理对象,里面包含一个add方法,这个add方法是一个空方法,他没有add方法的能力,他唯一要做的就是,把参数包装好交给内核驱动去实现,这一层对clien端是透明的,他不知道驱动所作的事情,这就是分层协议的好处,他只是知道拿到了代理对象的add方法,他就去调用add方法,我们知道add方法什么也没做,调用的时候,必然会去调用binder驱动,这是驱动收到了client发给他的代理对象的add方法,驱动一看就明白了,这个add方法在我的servermanager里有一张表,object代理对象替换了object对象,我们真正访问的是Service里的objec对象t的add方法,binder驱动就会通知server端去调用真正的object的add方法,把接口返会给驱动,驱动再把结果返回给client,就这样驱作为client和server的中介,进行了进程间通讯的机制

这里写图片描述

Binder是什么:

客户端进程只不过是持有了服务端的一个代理,然后通过代理对象,协助驱动完成跨进程驱动

这里写图片描述

参考好文

https://blog.youkuaiyun.com/ls5718/article/details/51854106 Android——Binder(AIDL)机制
https://blog.youkuaiyun.com/huachao1001/article/details/51504469简单明了,彻底地理解Binder
https://blog.youkuaiyun.com/universus/article/details/6211589 Android Bander设计与实现 - 设计篇 牛!

https://blog.youkuaiyun.com/freekiteyu/article/details/70082302
一篇文章了解相见恨晚的 Android Binder 进程间通讯机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值