Android Studio中使用AIDL进行进程间通信

Posted lxn_李小牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Studio中使用AIDL进行进程间通信相关的知识,希望对你有一定的参考价值。

什么是AIDL
AIDL是 android Interface definition language的缩写,也就是安卓接口定义语言
为什么要有AIDL
AIDL允许你定义客户端与服务端达成一致的程序接口使用进程间通信相互交流。 在Android上面,一个进程不能正常的访问另一个进程的内存。 所以说,他们需要分解他们的对象为操作系统可以理解的基本单位,然后为你把这些对象按次序跨越进程边界 ,书写这些代码是单调冗长的,所以android使用AIDL为你处理这个问题。

注意:使用AIDL只有在你允许来自不同应用的客户端跨进程通信访问你的service,并且想要在你的service种处理多线程的时候才是必要的。 如果你不需要执行不同应用之间的IPC并发,你应该通过实现Binder建立你的接口,或者如果你想执行IPC,但是不需要处理多线程。那么使用Messenger实现你的接口。

如何使用aidl完成进程间通信
1.建立.aidl文件
AIDL使用一个简单的语法让你声明一个带有一个或者多个带有参数和返回值方法的接口 参数和返回值可以是任何类型,甚至是AIDL生成的接口

你必须使用java语言构建.aidl文件 每一个.aidl文件必须定义一个简单的接口并且要求只有接口声明和方法签名

默认的,AIDL支持下面数据类型

ava语言中的所有基本数据类型(比如int、long、char、boolean等等)

  • String
  • CharSequence
  • List
    List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是你定义的parcelable List可以使用范型(例如,List) 接收端的实际类经常是一个ArrayList,尽管方法是使用List接口生成的
  • Map
    Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是你定义的parcelable 范型map是不被支持的(比如这种形式Map) 接收端的实际类经常是一个HashMap,尽管方法是使用Map接口生成的

当定义你的service接口的时候,注意:

  • 方法可以接收0或多个参数,并且有返回值或者返回void
  • 所有非基本数据类型要求要求一个定向的tag来指定数据是去往哪个方向的 无论是输入、输出,还是输入输出(参加下面的例子) 基本数据类型是默认支持的,并且不能是其他的。
  • .aidl文件中的所有的代码注释都在生成的IBinder接口中(除了在import和包声明之前的注释)
  • 只支持方法,你不可以在AIDL暴露静态域

下面给出代码
以Android Stduio为例,我们看看如何使用AIDL进行进程间通信
1.创建IRemoteService .aidl文件
这里写图片描述

这里写图片描述

package com.example.aidlserver;


interface IRemoteService {
     int getPid();
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

但是此时并没有AIDL的java文件产生,其实android studio也是带有自动生成的,只不过需要确认一些信息后才能生成。此时,我们可以在目录 build–>generated–>source–>aidl–>test–>debug下面发现还没有任何文件

这里写图片描述

此时,打开AndroidManifest.xml,确认package的值,如我这个

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.aidlserver">

关键性的一步,确认aidl文件所在的包名和AndroidMainifest.xml的package名是否一致。如果一致,点击 Build–>Make Project,生成相应的java文件。如果不一致,则改aidl的包名,改成一致,再点击生成,生成效果如图。
这里写图片描述

我们看看生成的IRemoteService.java的内容

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.aidlserver.IRemoteService {
    //用来标示当前服务的名称,客户端和服务端都要用到
        private static final java.lang.String DESCRIPTOR = "com.example.aidlserver.IRemoteService";

        /**
         * 在构造方法中把Stub与接口关联
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

      //将服务端的Binder对象转化为客户端所需的AIDL接口类型的对象,要区分进程
        public static com.example.aidlserver.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.aidlserver.IRemoteService))) {
            //客户端和服务端位于统一进程,返回服务端的Stub对象
                return ((com.example.aidlserver.IRemoteService) iin);
            }
            //不同进程返回Proxy.Proxy中封装了服务端的Binder对象
            return new com.example.aidlserver.IRemoteService.Stub.Proxy(obj);
        }
    //获取Binder对象
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
        //运行在服务端的Binder线程池中
        //code标示执行那个方法,由客户端传过来
        //data,客户端传过来的数据
        //reply,服务端给客户端的响应数据
        //flag 是否有返回值 0为有,1没有
        @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_getPid: {
                    data.enforceInterface(DESCRIPTOR);
                    //执行目标方法
                    int _result = this.getPid();
                    reply.writeNoException();
                    //写入到返回值中
                    reply.writeInt(_result);
                    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);
        }

        private static class Proxy implements com.example.aidlserver.IRemoteService {
            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 int getPid() throws android.os.RemoteException {
            //创建输入对象
                android.os.Parcel _data = android.os.Parcel.obtain();
                //创建输出对象
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
//调用transact与服务器通信  ,此时onTransact方法会被调用   
               mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
                    _reply.readException();
                    //请求成功返回,从返回值中获取结果
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @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_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public int getPid() throws android.os.RemoteException;

    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

我们只看重点部分,这里有一个内部类Stub继承Binder,实现了IRemoteService接口,这个类中有一个方法asInterface,接收一个IBinder对象,并且返回一个IRemoteService类型的对象。

定义远程服务,暴漏接口给客户端

public class RemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public int getPid() throws RemoteException {
            return Process.myPid();
        }
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    };
}

同时,我们需要在清单文件中注册这个服务,并且添加一个action,因为我们是通过远程来调用服务的,所以必须指定一个action,用来隐式启动。

  <service android:name=".RemoteService">
            <intent-filter>
                <action android:name="com.lxn.remote"/>
            </intent-filter>
        </service>

现在,当一个客户端(比如一个activity)调用bindService()来连接到这个service,这个客户端的onServiceConnected()回调函数接收service中onBind()方法返回的mBinder实例

客户端必须可以访问接口类,所以如果客户端和服务端在不同的应用中,那么客户端所在的应用必须有一份.aidl文件的副本,同样,在客户端我们创建一个aidl文件夹,然后把服务器端的aidl文件拷贝到这个目录下,注意:这个时候要保证文件所在的包名和服务器端aidl的包名一样,而不是和当前应用的包名一样,否则会报错。

当客户端在onServiceConnected()回调方法中接收到IBinder时,它必须调用你的ServiceInterface.Stub.asInterface(service)来把返回参数映射到你的ServiceInterface类型上。例如:

  private IRemoteService remoteService;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            remoteService = IRemoteService.Stub.asInterface(service);
            try {
                int pid = remoteService.getPid();
                int currentPid = Process.myPid();
                System.out.println("currentPid: "+currentPid+", remotePid: "+pid);
                remoteService.basicTypes(1,12,true,3,3,"aa");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            System.out.println("=====bind Success");
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(MainActivity.this, "service disConneted unexpected", Toast.LENGTH_SHORT).show();
            remoteService = null;
        }
    };

完整代码
服务端:

public class RemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public int getPid() throws RemoteException {
            System.out.println("=====Thread getPid: "+Thread.currentThread().getName());
            return Process.myPid();
        }
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            System.out.println("=====Thread basicTypes: "+Thread.currentThread().getName());
        }
    };
}

客户端:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void bindService(View view){
        System.out.println("=====begin bindService");
        Intent service = new Intent("com.lxn.remote");
        //通过bindService绑定服务
        bindService(service,conn,BIND_AUTO_CREATE);
    }
    private IRemoteService remoteService;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            remoteService = IRemoteService.Stub.asInterface(service);
            try {
                int pid = remoteService.getPid();
                int currentPid = Process.myPid();
                System.out.println("===currentPid: "+currentPid+", remotePid: "+pid);
                remoteService.basicTypes(1,12,true,3,3,"aa");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            System.out.println("=====bind Success");
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(MainActivity.this, "service disConneted unexpected", Toast.LENGTH_SHORT).show();
            remoteService = null;
        }
    };
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

这里写图片描述

我们点击客户端的绑定服务按钮,然后在控制台进行结果的输出

这里写图片描述

那么AIDL是什么原理呢?
AIDL其实通过我们写的aidl文件,帮助我们生成了一个接口,一个Stub类用于服务端,一个Proxy类用于客户端调用。关于详细的细节,我在这里就不讨论了,可以看下面这篇文章:
http://blog.csdn.net/lmj623565791/article/details/38461079

最后给出代码下载位置
点此下载代码

以上是关于Android Studio中使用AIDL进行进程间通信的主要内容,如果未能解决你的问题,请参考以下文章

android studio中使用AIDL进行客户端与服务端互相通信

Android AIDL 使用

Android AIDL 使用教程

Android Studio中如何创建AIDL

Android进程通信之Messenger&AIDL使用详解

AIDL介绍以及简单使用