AIDL 安卓进程间通信/跨应用通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28478281/article/details/53494703

前言

最近出去面试找工作,被人问到AIDL,我就回答这个东西我用过,也大概理解,Android的进程间通信语言嘛,人家不置可否,那我能咋着呢,毕竟没深入研究过,也没办法,咱只能回来奋发图强了

写在前面

我以前就看过的一个博客,里面原理代码什么都有,写的水平肯定比我高
Android开发者指南(6) —— AIDL

首先字面解释

A=Android
IDL=Interface definition language
意译就是android接口定义语言,马丹,完全看不明白
算了,就是Android官方给我们定义出来跨进程,甚至跨应用通信用的

开发平台

Android Studio 2.2+Android手机一部

新建工程

这个就不说了,跳过
就是新建工程后再建一个module 也是android app,功能后面再说

aidl语法

这里请去看我写在前面,里面比较详细,或者自行baidu/google,我也了解的不多

代码示例

最关键的地方到了
这里写图片描述
其实就是新建一个aidl文件

// IMyAidlInterface.aidl
package com.kikt.aidl;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

    void test(int sum,int sum2);
}

接着make project,生成下java代码
找到生成的代码看下
我靠 好复杂,还是渣格式,这里格式化一下:

不想看完全代码的可以看下后面的结构图

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: H:\\Git\\ASWorkSpace\\public\\AidlDemo\\app\\src\\main\\aidl\\com\\kikt\\aidl\\IMyAidlInterface.aidl
 */
package com.kikt.aidl;
// Declare any non-default types here with import statements

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

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

        /**
         * Cast an IBinder object into an com.kikt.aidl.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.kikt.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.kikt.aidl.IMyAidlInterface))) {
                return ((com.kikt.aidl.IMyAidlInterface) iin);
            }
            return new com.kikt.aidl.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_test: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    this.test(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.kikt.aidl.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 void test(int sum, int sum2) 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(sum);
                    _data.writeInt(sum2);
                    mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

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

    public void test(int sum, int sum2) throws android.os.RemoteException;
}

这里写图片描述

这里生成了一个静态内部类Stub
方法签名如下

public static abstract class Stub extends android.os.Binder implements com.kikt.aidl.IMyAidlInterface

这个类是抽象类 这里类本身实现了对应的接口IMyAidlInterface,但没有实现我们在aidl中定义的方法test(int,int)方法
这也代表了test方法需要我们自己来实现

另外方法继承了android.os.Binder,看到这个的时候,我们就知道,这个应该用在什么地方了
对了,和Service必须实现的那个接口onBind的返回参数一样!

这里写个类继承下

package com.kikt.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.kikt.aidl.IMyAidlInterface;

public class MyService extends Service {
    private static final String TAG = "MyService";
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
       return new AidlEntity();
    }

    public class AidlEntity extends IMyAidlInterface.Stub{

        @Override
        public void test(int sum, int sum2) throws RemoteException {
            Log.d(TAG, "test() called with: sum = [" + sum + "], sum2 = [" + sum2 + "]");
        }
    }
}

为了方便,直接写在service内部
这里只是简单的做一个日志输出,其他什么也没有干

同应用调用

这里是同应用调用

直接看核心代码

bindService(new Intent(this, MyService.class), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface test = IMyAidlInterface.Stub.asInterface(service);
                try {
                    test.test(1, 2);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

这里就是一个按钮点击后bindService,然后connection会接受到一个IBinder的实例,这个实例就是上面service的那个

当然还有一个写法,直接强转也不会报错

 bindService(new Intent(this, MyService.class), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
//                IMyAidlInterface test = IMyAidlInterface.Stub.asInterface(service);
//                try {
//                    test.test(1, 2);
//                } catch (RemoteException e) {
//                    e.printStackTrace();
//                }
                MyService.AidlEntity service1 = (MyService.AidlEntity) service;
                try {
                    service1.test(4,5);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

效果都是一样的,那么这个什么aidl还有个毛用啊,强转就好了

接着就是高潮了

跨应用调用

没错,我个人认为最重要的也就是这点了,aidl可以在不同应用中调用
比如我要做一个提供服务的应用,或者说是同公司的核心组件,而不希望别人知道我的具体实现
这时我直接丢给你我的aidl文件,告诉你包名和服务名称,你就去调就好了!

ComponentName componentName = new ComponentName(
                "com.kikt.aidldemo", "com.kikt.aidldemo.MyService");
        Intent intent = new Intent();
        intent.setComponent(componentName);

        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface iinterface = IMyAidlInterface.Stub.asInterface(service);
                try {
                    iinterface.test(10, 20);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

这里和上面的代码看上去差不多,但是我和原来的不是一个应用,而是第二个应用
这里使用test方法时,可以看到提供服务的应用可以输出日志

而引申下
如果这里有一个算法,实现了两个参数间的计算,那么我们是不是可以得到计算结果呢,比如说一个加密方法,我不需要知道具体实现,只要调用就好了

aidl修改如下:

// IMyAidlInterface.aidl
package com.kikt.aidl;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

    void test(int sum,int sum2);

    int add(int num,int num2);
}

内部类实现修改

 public class AidlEntity extends IMyAidlInterface.Stub{

        @Override
        public void test(int sum, int sum2) throws RemoteException {
            Log.d(TAG, "test() called with: sum = [" + sum + "], sum2 = [" + sum2 + "]");
        }

        @Override
        public int add(int num, int num2) throws RemoteException {
            return num + num2;
        }
    }

调用者应用的bindService中修改如下:

bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface iinterface = IMyAidlInterface.Stub.asInterface(service);
                try {
                    iinterface.test(10, 20);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

                try {
                    int add = iinterface.add(1, 2);
                    Log.d("MainActivity", "add:" + add);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

在第二个中点击可以看到日志输出
这里写图片描述

思维发散

其实aidl就像一个核心,模块化开发的时候,核心模块不用给你代码,只要你去aidl文件,和跨应用的调用方法即可,而你实现后续的业务逻辑,也算是一种解耦的方式吧

高级/原理

再回到Stub的实现中,看下onTransact方法就可以看出,其实aidl是实现自android提供的序列化
通过约定的方式,将方法名的参数和返回值序列化后再通过约定的方式取出来,这样来实现进程间通信
当然具体的内部原理因为我对于framework层没有深入研究,传输的过程我不太了解

@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_test: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    this.test(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_add: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

后记

其实aidl很方便也是一种跨应用的解决方案,非常实用,面试和工作中都应该用得到,后续如果有机会可以多使用下,就先写这么多吧,谢谢网上的android先驱者和大神们!!!
遨游在android知识的海洋中不可自拔的博主

猜你喜欢

转载自blog.csdn.net/qq_28478281/article/details/53494703