戏说 “AIDL”

一、什么是AIDL

 AIDL是android接口定义语言,可以利用它定义客户端与服务端进行进程间通信 (IPC) 时都认可的编程接口。跨进程通信,不同的进程,运行在不同的VM虚拟机上,每个进程都有独立的内存空间,管理各自的数据,就像“我住长江头,君住长江尾。日日思君不见君,共饮长江水”。AIDL就是“我”与“君”沟通的桥梁。

二、AIDL支持的数据类型

“我”与“君”的联系的桥梁已经有了,但是这个桥梁上都有什么在跑呢?默认情况下,AIDL 支持下列数据类型:

  • Java 编程语言中的所有原语类型(如 intlongcharboolean 等等)
  • String
  • CharSequence
  • List

    List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。可选择将 List 用作“通用”类(例如,List<String>)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。

  • Map

    Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。不支持通用 Map(如Map<String,Integer> 形式的 Map)。另一端实际接收的具体类始终是HashMap,但生成的方法使用的是Map 接口。使用Map类型,有一点特别要注意,map中的key和value必须都是可序列化类型,否则使用会有问题。

三、通信传递的数据包

知道了数据类型,我们就可以飞鸽传书,一诉相思之苦,但是这个书不是普通的书,android定义通信的数据必须是序列化后的而数据,android默认使用的是Parcelable,实现Parcelable数据包很简单,只要去实现Parcelable的接口就可以了。我们定义个情书类实现了Parcelable接口。

/**
 * <The trouble with the world is that the stupid are sure and the intelligent are full of doubt.>
 * <p>
 * jacky.aidltest
 * <p>
 * 作者:Jacky.Ao on 2018/3/16 13:32
 * <p>
 * 邮箱: jiazhi.ao@gmail.com
 */

public class Loveletter implements Parcelable {
    //情书内容
    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }


    @Override
    public int describeContents() {
        return 0;
    }
    
    //写入数据
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.content);
    }
    
    //读取数据
    public void readToParcel(Parcel dest) {
        content = dest.readString();
    }

    public Loveletter() {
    }

    protected Loveletter(Parcel in) {
        this.content = in.readString();
    }

    public static final Creator<Loveletter> CREATOR = new Creator<Loveletter>() {
        @Override
        public Loveletter createFromParcel(Parcel source) {
            return new Loveletter(source);
        }

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

写入数据和读取数据顺序要保持一致。如果用AS编码,有生成Parcelabale的插件,默认只支持写入数据,如果需要读取数据,需要自己去写,可以参考上面读取数据的代码。

四、飞鸽传书,以解相思

情书已经准备好了,如何传递出去的呢?Loveletter是自定义数据类型,所以我们要定义2个aidl文件:

// Loveletter.aidl
package jacky.aidltest;

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

parcelable Loveletter;
// ILoveletter.aidl
package jacky.aidltest;

// Declare any non-default types here with import statements
import jacky.aidltest.Loveletter;

interface ILoveletter {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    String myConfession();
}

如果是自定义类型,需要新增一个aidl文件,声明为Parcelable类型。ILoveletter定义一个接口“我的告白”。aidl文件编译器会默认编译,如果用的as编程可以配置下编译路径。

sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
    }
}

下面就开始书写情书了,就是写服务端代码:

/**
 * <The trouble with the world is that the stupid are sure and the intelligent are full of doubt.>
 * <p>
 * jacky.aidltest
 * <p>
 * 作者:Jacky.Ao on 2018/3/16 14:03
 * <p>
 * 邮箱: jiazhi.ao@gmail.com
 */

public class LoveLetterService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private final ILoveletter.Stub mBinder = new ILoveletter.Stub() {
        @Override
        public String myConfession() throws RemoteException {
            return "I love you";
        }
    };
}

在服务端里,终于扯破嗓子喊出了这句“I love you”。但是“君”如何接收到这句滚烫的表白呢,下面看“君”即客户端如何解码数据包的。

public class KnockoutActivity extends Activity {

    private TextView textView;
    private ILoveletter mIRemoteService;
    private String content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_knockout);
        textView = (TextView) findViewById(R.id.content);
        bindService();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    private void bindService() {
        Intent intent = new Intent(this, LoveletterService.class);
        intent.setAction(LoveletterService.class.getSimpleName());
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        // Called when the connection with the service is established
        public void onServiceConnected(ComponentName className, IBinder service) {
            // Following the example above for an AIDL interface,
            // this gets an instance of the IRemoteInterface, which we can use to call on the service
            mIRemoteService = ILoveletter.Stub.asInterface(service);
            if (mIRemoteService != null) {
                try {
                    content = mIRemoteService.myConfession();
                    textView.setText(content);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        // Called when the connection with the service disconnects unexpectedly
        public void onServiceDisconnected(ComponentName className) {
            Log.e("Service has unexpectedly disconnected");
            mIRemoteService = null;
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }
}

至此终于打印出“I love you”这句告白了。

四、解密告白

服务端onbinder方法返回了ILoveletter.Stub对象类型为IBinder类型。客户端bindservice传入ServiceConnection,当与服务建立连接时会调用onServiceConnected这里也传入一个IBinder对象,这个IBinder就是LoveletterService里的mBinder对象。兴趣的可以跟踪下源码(PMS,ContextImpl.java,LoadedApk.java)。如何来获取mIRemoteService的实例呢,看下ILoveletter.Stub.asInterface(service)接口实现:

/**
 * Cast an IBinder object into an jacky.aidltest.ILoveletter interface,
 * generating a proxy if needed.
 */
public static jacky.aidltest.ILoveletter asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof jacky.aidltest.ILoveletter))) {
return ((jacky.aidltest.ILoveletter)iin);
}
Log.i("return proxy.");
return new jacky.aidltest.ILoveletter.Stub.Proxy(obj);
}

首先判断下传入的binder对象是否为空,为空直接return,然后通描述符检查是否在统一进程里,如果是同一个进程,直接返回,如果不在一个进程则返回一个Proxy对象。我们看下Proxy部分代码:

private static class Proxy implements jacky.aidltest.ILoveletter
{
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 java.lang.String myConfession() 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_myConfession, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

如果client和server都在一个进程里,就直接调用getConfession接口,返回“I love you”。如果不在同一个进程,client端拿到Proxy对象,proxy持有server端Binder对象mRemote。初始化2个数据包,_data,_reply,_data写入描述符,进行数据包签名,用于onTransact校验,然后通过mRemote对象调用onTransact,并将_data,_reply传入,看下onTransact接口实现:

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
Log.i("onTransact: " + code);
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_myConfession:
{
data.enforceInterface(DESCRIPTOR);
String _result = this.myConfession();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

,data.enforceInterface(DESCRIPTOR)校验签名,调用myConfession返回值保存在_result中,然后将result写入reply数据包中。返回true,如果返回false,则无法传回client端。proxy部分通过onTransact从回传来的reply数据包中读取数据。

这样客户端“佳人”,拆开信封mIRemoteService.myConfession()就看见“我”的表白“I love you”。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值