前言:IPC机制必然会碰到AIDL,网上相关的博客很多,但总不如自己走一边来的印象深刻。
预备的所有文件
关于如何使用AIDL,网上很多也比较简单,不赘述了。
Book.aidl
// Book.aidl
package com.lct.zyw.serviceaidl;
parcelable Book;
Book.java
package com.lct.zyw.serviceaidl;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by zhangyawen on 2016/9/14.
*/
public class Book implements Parcelable{
private int id;
private String name;
public Book() {
}
public Book(int id, String name) {
this.id = id;
this.name = name;
}
protected Book(Parcel in) {
id = in.readInt();
name = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* Describe the kinds of special objects contained in this Parcelable's
* marshalled representation.
*
* @return a bitmask indicating the set of special object types marshalled
* by the Parcelable.
*/
@Override
public int describeContents() {
return 0;
}
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
/**
* 用于AIDL接口方法中Book类型的参数的tag为out,inout。
* 如果该方法改成空实现,则服务端对参数的任何修改不会改变客户端传入的参数,其实最关键的是搞懂IBookManagerInterface.java的流程,就能知其所以然了。
* @param source
*/
public void readFromParcel(Parcel source) {
id = source.readInt();
name = source.readString();
}
@Override
public String toString() {
return "Book { id: "+this.id+", name: "+this.name+"}";
}
}
IBookManagerinterface.aidl
// IBookManagerInterface.aidl
// android studio 不会自动生成对应aidl文件的java文件,具体做法是在aidl文件下Ctrl+ F9(Make Project)
// build成功之后在对应项目文件路径下的\build\generated\source\aidl\debug中就会生成aidl文件的对应的java文件
package com.lct.zyw.serviceaidl;
//需要手动导入(即使在同一个包下)
import com.lct.zyw.serviceaidl.Book;
interface IBookManagerInterface {
void doSomething(int x,int y);
List<Book> getBooks();
//关于定向Tag,在对应的java文件中需要深入分析
Book addBookIn(in Book book);
Book addBookOut(out Book book);
Book addBookInout(inout Book book);
}
分析IBookManagerinteraface.java
网上很多时从服务端代码往客户端代码分析的,我是从客户端代码往服务端代码分析的,其实都差不多,个人习惯而已
IBookManagerInterface.java
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\ServiceAIDL\\serviceprot\\src\\main\\aidl\\com\\lct\\zyw\\serviceaidl\\IBookManagerInterface.aidl
*/
/* *
* Binder的框架:
* 1.Android Binder框架分为服务器接口、Binder驱动、以及客户端接口;简单想一下,需要提供一个全局服务,
* 那么全局服务那端即是服务器接口,任何程序即客户端接口,它们之间通过一个Binder驱动访问。
* 2.服务器端接口:实际上是Binder类的对象,该对象一旦创建,内部则会启动一个隐藏线程,会接收Binder驱动发送的消息,
* 收到消息后,会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码。
* 3.Binder驱动:该对象也为Binder类的实例,客户端通过该对象访问远程服务。
* 4.客户端接口:获得Binder驱动,调用其transact()发送消息至服务器
* */
package com.lct.zyw.serviceaidl;
/**
* 1.接口的继承仍然用[extends]关键字
* 2.所有可以在Binder中传输的接口都需要继承IInterface接口
* 3.虽然该Java文件时根据对应的aidl文件由android studio自动生成的,在理解细节实现之后,就可以不依赖aidl文件,
* 直接手写IBookManagerInterface.java,甚至将该java文件中的代码拆分到服务端和客户端中去。
*
*/
public interface IBookManagerInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
* 声明了一个Stub的静态抽象内部类,继承了Binder,即使一个Binder的子类,实现IBookManagerInterface接口
* 说明:1.抽象类实现接口,则接口中的抽象方法可以不在抽象方法中实现,交由继承该抽象类的实现类(非抽象类)来实现,此时就必须实现。
* 2.若抽象类中实现了接口中的抽象方法,那么实现类(继承自抽象类)就可以不实现,也可以实现(方法的重写)。
*/
public static abstract class Stub extends android.os.Binder implements com.lct.zyw.serviceaidl.IBookManagerInterface {
//Binder类(Stub)的唯一标识,一般以类名表示
private static final java.lang.String DESCRIPTOR = "com.lct.zyw.serviceaidl.IBookManagerInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lct.zyw.serviceaidl.IBookManagerInterface interface,
* generating a proxy if needed.
* 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象
* obj:客户端绑定服务端成功后,ServiceConnection中的onServiceConnected方法被调用,返回的service(Ibinder接口)
*/
public static com.lct.zyw.serviceaidl.IBookManagerInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//通过标识,检验obj是否属于本进程(客户端)中的Ibinder
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lct.zyw.serviceaidl.IBookManagerInterface))) {
return ((com.lct.zyw.serviceaidl.IBookManagerInterface) iin);
}
//进程间通信最终返回客户端的是Stub类中的内部代理类Proxy对象,那么在客户端的任何服务端方法调用都是Proxy类中对应方法的调用。
return new com.lct.zyw.serviceaidl.IBookManagerInterface.Stub.Proxy(obj);
}
/**
* IInterface接口中的方法 一个Binder类返回一个Ibinder接口(Binder实现了Ibinder接口),
* 这其实是一种向上转型。
* @return
*/
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 该方法时通过Proxy类中的transact方法发起的
* @param code 方法ID:和Proxy类中的transact方法中的一一对应
* @param data 客户端传递过来的参数
* @param reply 服务器返回回去的值
* @param flags 标明是否有返回值,0为有(双向),1为没有(单向)
* @return
* @throws android.os.RemoteException
*/
@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_doSomething: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
this.doSomething(_arg0, _arg1);
reply.writeNoException();
return true;
}
case TRANSACTION_getBooks: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.lct.zyw.serviceaidl.Book> _result = this.getBooks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBookIn: {
//与客户端的writeInterfaceToken对用,标识远程服务的名称
data.enforceInterface(DESCRIPTOR);
//将data转换成Book类
com.lct.zyw.serviceaidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//调用服务端 new IBookManagerInterface.Stub()实现类的具体方法
com.lct.zyw.serviceaidl.Book _result = this.addBookIn(_arg0);
//将返回值_result转换成 reply,返回给客户端
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addBookOut: {
data.enforceInterface(DESCRIPTOR);
com.lct.zyw.serviceaidl.Book _arg0;
/**
* 相比tag为in,服务端仅仅自己new一个Book类的对象,参数_data并没有对book对象进行赋值操作
* 而tag为in的方法是有赋值操作的
*/
_arg0 = new com.lct.zyw.serviceaidl.Book();
com.lct.zyw.serviceaidl.Book _result = this.addBookOut(_arg0);
reply.writeNoException();
/**
* 此处将服务端返回的_result(Book)赋值给reply(Parcel)(不管定向Tag是什么,操作都是一样的,不解释)
*/
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
/**
* Note:
* 相比tag为in,此处代码是多出来的,这里是将服务端的入参_arg0赋值给reply,那就说服务端方法的入参就是返回值,
* 如果服务端方法中不对入参_arg0做任何操作,那么返回的将是一个新new出来的Book对象,需注意。
*/
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addBookInout: {
data.enforceInterface(DESCRIPTOR);
com.lct.zyw.serviceaidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
com.lct.zyw.serviceaidl.Book _result = this.addBookInout(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/**
* 客户端绑定服务端成功后,执行的直接方法都是内部代理Proxy类中对应的方法
*/
private static class Proxy implements com.lct.zyw.serviceaidl.IBookManagerInterface {
private android.os.IBinder mRemote;
//mRemote被赋值:客户端的传进来的service(Ibinder接口类型)
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 doSomething(int x, int y) 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(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_doSomething, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<com.lct.zyw.serviceaidl.Book> getBooks() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lct.zyw.serviceaidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lct.zyw.serviceaidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.lct.zyw.serviceaidl.Book addBookIn(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
/**
* _data: 输入型Parcel对象
* _reply: 输出型Parcel对象
* _result: 返回值对象(类型根据返回值类型而定)
*/
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lct.zyw.serviceaidl.Book _result;
try {
//服务器端的enforceInterfac对应
_data.writeInterfaceToken(DESCRIPTOR);
//将写入参数到_data中去(此处也证明了关于进程间通信的自定义类型的参数对象(Book),必须是实现Parcelable接口的)
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
/**
* transact(int code, Parcel data, Parcel reply, int flags)方法对应的参数及自身说明
*
* code: 方法ID,唯一性,一一对应,服务端onTransact方法会根据客户端传过来的方法ID来确定调用具体的方法
* data: 用来存储客户端传递给服务端的Parcel类型的数据
* reply: 用来存储客户端传回给客户端的Parcel类型的数据
* flags: 是否有返回值:为 0 表示数据可以双向流通,即 _reply 流可以正常的携带数据回来,
* 为 1 的话那么数据将只能单向流通,从服务端回来的 _reply 流将不携带任何数据
* Note:默认为0,不解释。
*
* 调用该方法来发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务端的onTransact方法会被调用,
* 直到RPC过程返回后,当前线程继续执行
* Note:此处提到了线程挂起,说明RPC请求很可能是耗时操作,那么在客户端调用服务端的方法时,最好避免直接在UI
* 线程中直接调用,以免引起ANR,另起线程更安全
*/
mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0);
//_reply接受到服务端的数据,转换成返回值的数据类型(Book)返回给客户端
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
/**
* Recycle 机制(Note: 并不是java虚拟机的垃圾回收机制)
* 简释:
* 当一个对象不再使用时把它储藏起来,不让虚拟机回收,需要的时候再从仓库里拿出来重新使用,
* 这就避免了对象被回收后再重分配的过程。对于在应用的生命周期内(或者在循环中)需要频繁
* 创建的对象来说这个机制特别实用,可以显著减少对象创建的次数,从而减少 GC 的运行时间。
* 运用得当便可改善应用的性能。唯一的不足只是需要手动为废弃对象调用 recycle 方法。
* 文/TiouLims(简书作者)
* 原文链接:http://www.jianshu.com/p/5cba251c7fd9
*/
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* 关于定向Tag(in out inout)的分析(Note:在aidl文件中方法的传入参数设置定向Tag,最根本的还是android studio会根据Tag
* 在对应java代码中方法的实现上有区别)
* 该方法的传入参数的定向tag在aidl文件中定义为out。
* @param book
* @return
* @throws android.os.RemoteException
*/
@Override
public com.lct.zyw.serviceaidl.Book addBookOut(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
/**
* _data: 输入型Parcel对象
* _reply: 输出型Parcel对象
* _result: 返回值对象(类型根据返回值类型而定)
*/
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lct.zyw.serviceaidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
/**
* 相比tag为in,该方法的入参book并没有转化成_data,通过transcat方法传递给服务端,那就是说,参数定向tag为out的方法
* 服务端将不接受的_data参数(Ontransact方法中说明)
*/
mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
_reply.readException();
/**
* 此处将服务端返回的_reply转化成Book对象,并返回给客户端(不管定向Tag是什么,操作都是一样的,不解释)
*/
if ((0 != _reply.readInt())) {
_result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
/**
* 相比tag为in,客户端的入参book被重新赋值,关键在Book.java中需要额外的实现readFromParcel方法,
* 那么客户端的入参book到底会有什么变化,主要看readFromParcel方法的具体实现:
* 1.参照Book.java中的构造函数Book(Parcel in)实现,那么入参将和返回值相同
* 2.readFromParcel是空实现,那么入参没有任何变化
*/
if ((0 != _reply.readInt())) {
book.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* 定向Tag为inout,则方法实现是同时拥有in和out的并集效果,相对的底层的开销也会比较大,没有必要就不要这么做了。
* @param book
* @return
* @throws android.os.RemoteException
*/
@Override
public com.lct.zyw.serviceaidl.Book addBookInout(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lct.zyw.serviceaidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
if ((0 != _reply.readInt())) {
book.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
//在aidl中定义的方法都会在此得到唯一的ID
static final int TRANSACTION_doSomething = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBookIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_addBookOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_addBookInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
}
public void doSomething(int x, int y) throws android.os.RemoteException;
public java.util.List<com.lct.zyw.serviceaidl.Book> getBooks() throws android.os.RemoteException;
public com.lct.zyw.serviceaidl.Book addBookIn(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;
public com.lct.zyw.serviceaidl.Book addBookOut(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;
public com.lct.zyw.serviceaidl.Book addBookInout(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;
}
扩展链接:对Binder的浅显分析及AIDL的使用
结语:好记性不如烂笔头,写下来,方便以后查找。