AIDL实例,双向通信

服务端实现

接下来的过程演示了服务端怎么实现Android复杂的AIDL通信实例
首先需要明白的一个事情,调用一个方法,涉及到三个重要概念:参数的个数,参数类型,和方法输出类型

AIDL支持的参数类型

1. 简单数据类型

  • Java基本类型,即int、long、char等;
  • String;
  • CharSequence;
  • List
    List中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。
  • Map
    Map中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。

2. 复杂数据类型

  • 在AIDL中传递的复杂参数类型对象必须要实现Parcelable接口,这是因为Parcelable允许Android系统将复杂对象分解成基本类型以便在进程间传输。

  • 若客户端组件和服务分开在不同APP,必须要把该Parcelable实现类.java文件拷贝到客户端所在的APP,包路径要一致。

  • 另外需要为这个Parcelable实现类定义一个相应的.aidl文件。

  • 复杂数据类型,必须要有import语句,即使它跟.aidl是同一个包下。

  • 方法可有零、一或多个参数,可有返回值或void。

  • 将复杂数据类型作为AIDL接口的形参时,记得加上in。

所有非基本类型的参数都需要标签来表明这个数据的去向:
in,表示此变量由客户端设置;
out,表示此变量由服务端设置;
inout,表示此变量可由客户端和服务端设置;
而基本类型只能是in。
只expose方法,不会expose静态变量。

3. 回调类参数类型
回调类型的参数类型和复杂数据类型用法是一样的,但是它对应的是binder类型的参数,而不是单纯的数据类型。回调类型的参数允许回调客户端代码及结果,相当于服务端调用客户端当中的方法,同样的 回调类参数类型也属于非基本类型参数,也需要用标签标明设置情况

需要定义一个AIDL服务需要以下几个步骤

  1. 创建.aidl文件
  2. SDK生成对应.java文件和Stub内部类
  3. 通过Service子类将接口暴露给外界

1. 创建.aidl文件

  • 创建对象类型的AIDL文件
    简单类型对象并不需要单独创建一个aidl文件表示,但是复杂对象需要创建一个对应的实体类型的aidl文件,如下:
//AIDL复杂对象定义

//注意点:这个包名需要和你的实体类com.visualing.application.RequestEntity包名一致
//同时也意味着,你需要顶一个实现Parcelable接口的实体类
package com.visualing.application;

//parcelable化的实体
parcelable RequestEntity;

该aidl文件对应的实体类实现了Parcelable接口,这是一个常规的实现了Parcelable接口的实例

package com.visualing.application;

import android.os.Parcel;
import android.os.Parcelable;

public class RequestEntity implements Parcelable {

    public String name;

    public long money;

    public int age;

    public byte sex;


    protected RequestEntity(Parcel in) {
        name = in.readString();
        money = in.readLong();
        age = in.readInt();
        sex = in.readByte();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeLong(money);
        dest.writeInt(age);
        dest.writeByte(sex);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public RequestEntity createFromParcel(Parcel in) {
            return new RequestEntity(in);
        }

        @Override
        public RequestEntity[] newArray(int size) {
            return new RequestEntity[size];
        }
    };

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getMoney() {
        return money;
    }

    public void setMoney(long money) {
        this.money = money;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public byte getSex() {
        return sex;
    }

    public void setSex(byte sex) {
        this.sex = sex;
    }
}

  • 创建方法类型的AIDL文件
    以下是创建一个供客户端调用的aidl文件的实例
// IMyAidlInterface.aidl
package com.visualing.application;
//导入需要别的进程访问的类对象,这些内容在客户端需要有一份拷贝,生成一模一样的内容
import com.visualing.application.RequestEntity;
import com.visualing.application.AidlCallback;
// Declare any non-default types here with import statements

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    //基本类型演示接口
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    //复杂类型演示接口
    int addValues(int anInt,int bnInt,in RequestEntity request);

    //计算回调类型演示接口
    int progressCallback(int anInt,int bnInt,in AidlCallback callback,in RequestEntity request);
}

我们可以看到上面有一个计算回调类型演示接口

 int progressCallback(int anInt,int bnInt,in AidlCallback callback,in RequestEntity request);

那其中的AidlCallback是怎么实现的呢,如下,是AidlCallback一个回调类型的方法的aidl文件的实例,和本身调用者的定义方式是一样的

  • 创建回调类型的AIDL文件
// AidlCallback.aidl
package com.visualing.application;

//此处需要主动导入你写入的复杂的实体类
import com.visualing.application.RequestEntity;
// Declare any non-default types here with import statements


//回调的AIDL写法,和普通的调用方法写法一致,只是这个表示从服务端调用客户端的方法而已
interface AidlCallback {

    /**
     * 方法可有零、一或多个参数,可有返回值或void。所有非基本类型的参数都需要标签来表明这个数据的去向:
       in,表示此变量由客户端设置;
       out,表示此变量由服务端设置;
       inout,表示此变量可由客户端和服务端设置;
       基本类型只能是in。
     */
    //注意复杂对象的参数是,前面需要带in,或者out,表明自己是输入还是输出
    void callback(in RequestEntity entity);
}

由上,已经定义了程序所需要的所有的aidl文件。

2. SDK生成对应.java文件和Stub内部类
当编译APP时,SDK工具会将项目/src//aidl目录下的.aidl文件一个个在项目/build/generated/source/aidl目录下生成IBinder接口.java文件。两个文件名一样,只是后缀不同。如AidlCallback.aidl生成AidlCallback.java。
AidlCallback.java

package com.visualing.application;
// Declare any non-default types here with import statements
//回调的AIDL写法,和普通的调用方法写法一致,只是这个表示从服务端调用客户端的方法而已

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

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

        /**
         * Cast an IBinder object into an com.visualing.application.AidlCallback interface,
         * generating a proxy if needed.
         */
        public static com.visualing.application.AidlCallback asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.visualing.application.AidlCallback))) {
                return ((com.visualing.application.AidlCallback) iin);
            }
            return new com.visualing.application.AidlCallback.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_callback: {
                    data.enforceInterface(DESCRIPTOR);
                    com.visualing.application.RequestEntity _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.visualing.application.RequestEntity.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.callback(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.visualing.application.AidlCallback {
            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;
            }

            /**
             * 方法可有零、一或多个参数,可有返回值或void。所有非基本类型的参数都需要标签来表明这个数据的去向:
             * in,表示此变量由客户端设置;
             * out,表示此变量由服务端设置;
             * inout,表示此变量可由客户端和服务端设置;
             * 基本类型只能是in。
             *///注意复杂对象的参数是,前面需要带in,或者out,表明自己是输入还是输出
            @Override
            public void callback(com.visualing.application.RequestEntity entity) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((entity != null)) {
                        _data.writeInt(1);
                        entity.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_callback, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

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

    /**
     * 方法可有零、一或多个参数,可有返回值或void。所有非基本类型的参数都需要标签来表明这个数据的去向:
     * in,表示此变量由客户端设置;
     * out,表示此变量由服务端设置;
     * inout,表示此变量可由客户端和服务端设置;
     * 基本类型只能是in。
     *///注意复杂对象的参数是,前面需要带in,或者out,表明自己是输入还是输出
    public void callback(com.visualing.application.RequestEntity entity) throws android.os.RemoteException;
}

IMyAidlInterface.java

public interface IMyAidlInterface extends android.os.IInterface {
      public static abstract class Stub extends android.os.Binder implements com.visualing.application.IMyAidlInterface {
       
        @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: {
                                  }
                case TRANSACTION_basicTypes: {
                                   }
                case TRANSACTION_addValues: {
                                  }
                case TRANSACTION_progressCallback: {
                                        return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.visualing.application.IMyAidlInterface {
         
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
              
            }

            @Override
            public int addValues(int anInt, int bnInt, com.visualing.application.RequestEntity request) throws android.os.RemoteException {
               
            }

            @Override
            public int progressCallback(int anInt, int bnInt, com.visualing.application.AidlCallback callback, com.visualing.application.RequestEntity request) throws android.os.RemoteException {
                           
        }
        //这个与服务器对应的方法标识
        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addValues = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_progressCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

   //服务接口的方法定义
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public int addValues(int anInt, int bnInt, com.visualing.application.RequestEntity request) throws android.os.RemoteException;

    public int progressCallback(int anInt, int bnInt, com.visualing.application.AidlCallback callback, com.visualing.application.RequestEntity request) throws android.os.RemoteException;
}

从上面我们看到了一个叫做Stub内部类,里面有两个关键的方法
public boolean onTransact()

public static com.visualing.application.AidlCallback asInterface(android.os.IBinder obj)
作用已经在上一篇文章中说明。他们是binder的桥梁和转换,是联系两个进程的关键点。

什么是Stub内部类,使用Stub内部类需要注意的地方
Stub内部类

  • .aidl文件编译后生成的.java文件中自动生成的内部类。
  • public static abstract声明。
  • extends android.os.Binder。
  • 实现.aidl文件中定义的接口,且声明其所有方法。

实现Stub内部类要注意

  1. 对于传过来的调用,无法保证是在主线程中执行的。
  2. Service必须要考虑多线程和线程安全。
  3. 默认情况下,RPC都是异步的。避免在主线程中调用AIDL,不然可能会导致ANR。
  4. 不能给调用方回抛异常。

3. 通过Service子类将接口暴露给外界

需要创建Stub内部类的子类并实现接口的相关方法,然后通过asBinder返回给客户端使用。

package com.visualing.application.server;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import com.visualing.application.AidlCallback;
import com.visualing.application.IMyAidlInterface;
import com.visualing.application.RequestEntity;

public class ServerApplication extends Service {

    IMyAidlInterface mAidlInterface = new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            //方法实现
        }

        @Override
        public int addValues(int anInt, int bnInt, RequestEntity request) throws RemoteException {
            //具体方法实现
            return anInt + bnInt + request.age;
        }

        @Override
        public int progressCallback(int anInt, int bnInt, AidlCallback callback, RequestEntity request) throws RemoteException {
            //具体方法实现
            request.age = addValues(anInt, bnInt, request) * 10000;
            callback.callback(request);
            return request.age;
        }

    };

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //简单的类型强转
        return mAidlInterface.asBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }
}

然后在AndroidManifest.xml进行注册


        
            
                
            
        

那么到此服务端的AIDL就已经全部实现了。
看看服务端的文件结构图是什么样的


AIDL实例,双向通信_第1张图片
server_aidl.jpg

客户端实现

如果已经写好了服务端的AIDL,那么客户端的AIDL也就写好了,因为它们是一一对应的,需要从服务端拷贝一份AIDL文件到客户端然后同步相关代码,那么就会生成相同的类对象。
同时还需要做的事情是将AIDL中涉及到的java类文件也需要拷贝一份,因为他们是相同的处理方式,都是通过Binder进行处理的。
以下是客户端的复制文件结构图


AIDL实例,双向通信_第2张图片
aidl客户端.jpg

可以看打大部分文件都是服务端文件的复制,那么客户端怎么和服务端进行沟通呢,请看关键的AidlActivity.java文件。

package com.visualing.application.client;

import android.content.ComponentName;
import android.content.Context;
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.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.visualing.application.AidlCallback;
import com.visualing.application.IMyAidlInterface;
import com.visualing.application.RequestEntity;

public class AidlActivity extends AppCompatActivity {

    IMyAidlInterface mAidlInterface;

    //关键点服务连接器
    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            //关键点,获取连接服务接口
            /**
             * 
             *     public static com.visualing.application.IMyAidlInterface asInterface(android.os.IBinder obj)
             {
             if ((obj==null)) {
             return null;
             }
             android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
             if (((iin!=null)&&(iin instanceof com.visualing.application.IMyAidlInterface))) {
             return ((com.visualing.application.IMyAidlInterface)iin);
             }
             return new com.visualing.application.IMyAidlInterface.Stub.Proxy(obj);
             }
             * 
*/ mAidlInterface = IMyAidlInterface.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mAidlInterface = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_hello_sum_aidl); //绑定远程服务,可能第一次无法绑上 bindRomoteService(); initActionView(); } protected void bindRomoteService() { final Intent intent = new Intent(); //---------关键点:确定服务的服务类型,或者加入其它权限 intent.setAction("com.visualing.application.server.MyService"); //---------关键点:确定服务所属的包名 intent.setPackage("com.visualing.application.server"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { //解绑远程服务 unbindService(mServiceConnection); super.onDestroy(); } protected void initActionView() { Button buttonCalc = (Button) findViewById(R.id.buttonCalc); buttonCalc.setOnClickListener(new View.OnClickListener() { EditText value1 = (EditText) findViewById(R.id.value1); EditText value2 = (EditText) findViewById(R.id.value2); TextView result = (TextView) findViewById(R.id.result); int v1 = 0, v2 = 0, res = -1; @Override public void onClick(View v) { //远程方法操作 if (mAidlInterface != null) { try { v1 = Integer.parseInt(value1.getText().toString()); v2 = Integer.parseInt(value2.getText().toString()); } catch (Exception e) { e.printStackTrace(); return; } //此处是则是aidl运行线程 new Thread(new Runnable() { @Override public void run() { //执行远程方法 try { //非UI运行线程 res = mAidlInterface.addValues(v1, v2, new RequestEntity(20)); try { result.setText(Integer.valueOf(res).toString()); } catch (Exception e) { //执行上述代码会报异常,因为在thread中执行 e.printStackTrace(); } mAidlInterface.progressCallback(v1, v2, new AidlCallback.Stub() { @Override public void callback(RequestEntity entity) throws RemoteException { try { result.setText(Integer.valueOf(entity.age).toString()); } catch (Exception e) { //执行上述代码会报异常,因为在thread中执行 e.printStackTrace(); } } }, new RequestEntity(30)); result.postDelayed(new Runnable() { @Override public void run() { try { //UI运行线程 mAidlInterface.progressCallback(v1, v2, new AidlCallback.Stub() { @Override public void callback(RequestEntity entity) throws RemoteException { result.setText(Integer.valueOf(entity.age).toString()); //不会有异常,会得出最后的显示结果 } }, new RequestEntity(50)); } catch (RemoteException e) { e.printStackTrace(); } } }, 1000); //如果也就说明一个问题,aidl是在那个线程中调用的,那么执行的回调也是在那个线程中 } catch (Exception e) { e.printStackTrace(); } } }).start(); } else { bindRomoteService(); } } }); } }

执行上面的代码执行过程是这样的,首先是 bindRomoteService(),然后得到接口对象mAidlInterface ,然后调用相关的方法实现,最后需要早destory中解绑服务 unbindService(mServiceConnection);从上面的测试代码也可以看出aidl是在那个线程中调用的,那么执行的回调也是在那个线程中,aidl是允许在任何线程中调用的。
由上基本上完成了复杂模式的AIDL双向通信回调。

总结

客户端使用

ServiceConnection mServiceConnection = new ServiceConnection()

链接服务端,得到服务端定义的接口

IMyAidlInterface   mAidlInterface = IMyAidlInterface.Stub.asInterface(service);

,客户端使用mAidlInterface对象进行远程调用,该对象是一个站桩对象,通过Binder的方式调用对应的接口实例

@Override 
public int progressCallback(int anInt, int bnInt, com.visualing.application.AidlCallback callback, com.visualing.application.RequestEntity request) 
{
...
//mRemote虚拟机接管的远程实例对象
mRemote.transact(Stub.TRANSACTION_progressCallback, _data, _reply, 0);
...
}

,通过transact方法将信息沟通服务端,并把相关参数信息传到客户端实现。
接下来由服务端中的onBind方法中返回的Stub类型的Binder对象接收处理

public class ServerApplication extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        //简单的类型强转
        return mAidlInterface.asBinder();
    }
}

但是并不是直接调用Stub子类中的对应方法,而是通过Binder中的一个onTransact方法

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case TRANSACTION_progressCallback:
{
...
int _result = this.progressCallback(_arg0, _arg1, _arg2, _arg3);
...
}
...
}

处理后进行调用用户实现的方法progressCallback()

 @Override
        public int progressCallback(int anInt, int bnInt, AidlCallback callback, RequestEntity request) throws RemoteException {
            //具体方法实现
            request.age = addValues(anInt, bnInt, request) * 10000;
            callback.callback(request);
            return request.age;
        }

的处理过程,可以看到该过程中有一个回调方法 callback.callback(request);该方法则又会通过上面的一个过程调用到客户端的callback方法,只不过这次客户端变成了服务端,服务端相当于客户端了。这样他们就实现了双向通信。

部分引用自

你可能感兴趣的:(AIDL实例,双向通信)