前言
今天要写的就是AIDL的具体使用、AIDL自动生成源码分析以及根据自动生成的源码自定义类来实现IPC。这篇博客的着重点就在于源码分析和自定义类,至于使用过程中的细节并没有介绍。开始切入正题!
服务端进程使用
在服务端进程中我们定义AIDL文件,以学生类为例,我们对外接口就是IStudentManager.aidl文件,自定义类AIDL声明文件就是Student.aidl文件,对应我们实体类就是Student,三者代码如下:
Student.java:
package com.lgy.aidl.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Student implements Parcelable {
private String mNo;
private String mName;
private String mSex;
private String mTel;
private Student(Parcel in) {
mNo = in.readString();
mName = in.readString();
mSex = in.readString();
mTel = in.readString();
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(mNo);
parcel.writeString(mName);
parcel.writeString(mSex);
parcel.writeString(mTel);
}
public String getmNo() {
return mNo;
}
public void setmNo(String mNo) {
this.mNo = mNo;
}
public String getmName() {
return mName;
}
public void setmName(String mName) {
this.mName = mName;
}
public String getmSex() {
return mSex;
}
public void setmSex(String mSex) {
this.mSex = mSex;
}
public String getmTel() {
return mTel;
}
public void setmTel(String mTel) {
this.mTel = mTel;
}
}
Student.aidl:
// Student.aidl
package com.lgy.aidl.aidl;
import com.lgy.aidl.aidl.Student;
parcelable Student;
IStudentManager.aidl:
// IStudentManager.aidl
package com.lgy.aidl.aidl;
import com.lgy.aidl.aidl.Student;
interface IStudentManager {
List<Student> getAllStudent();
void addStudent(in Student stu);
}
此时我们程序的结构图:
我们在服务端进程中开启Service,在Service中返回给客户端进程IBinder对象,此时的IBinder对象就是我们刚刚IStudentManager.aidl文件生成源码中静态内部类Stub,客户端拿到服务端IBinder对象,就可以进行进程间的通信了。
Service代码:
package com.lgy.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.lgy.aidl.aidl.IStudentManager;
import com.lgy.aidl.aidl.Student;
import java.util.ArrayList;
import java.util.List;
public class MyService extends Service {
private static final String TAG = "MyService";
private ArrayList<Student> mList = new ArrayList<>();
private IStudentManager.Stub mBinder = new IStudentManager.Stub() {
@Override
public List<Student> getAllStudent() throws RemoteException {
Log.i(TAG,"客户端调用getAllStudent方法执行,学生人数" + mList.size());
return mList;
}
@Override
public void addStudent(Student stu) throws RemoteException {
Log.i(TAG,"客户端调用addStudent方法执行之前学生人数" + mList.size());
mList.add(stu);
Log.i(TAG, "客户端调用addStudent方法执行之后学生人数" + mList.size());
}
};
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
Student stu1 = new Student("1","张三","男","13718447489");
Student stu2 = new Student("2","李四","男","13699485793");
Student stu3 = new Student("3","貂蝉","女","15896345892");
mList.add(stu1);
mList.add(stu2);
mList.add(stu3);
Log.i(TAG,"服务端Service启动!当前总共有" + mList.size() + "名学生");
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
到此服务的进程代码编写完毕!
客户端进程使用
我们将服务端进程中所需要的AIDL文件和相关自定义的实体类,原封不动的移动到客户端进程程序中。我们在客户端进程中只需要绑定Service,获取拿到服务端的IBinder对象即可。
package com.lgy.aidl;
import android.app.Activity;
import android.content.ComponentName;
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.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import com.lgy.aidl.aidl.IStudentManager;
import com.lgy.aidl.aidl.Student;
import java.util.ArrayList;
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
private IStudentManager mStudentManager;
private ArrayList<Student> mList ;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i(TAG, "客户端进程连接成功");
mStudentManager = IStudentManager.Stub.asInterface(iBinder);
try {
mList = (ArrayList<Student>) mStudentManager.getAllStudent();
Log.i(TAG,"客户端进程连接成功之后,返回的学生人数" + mList.size());
} catch (RemoteException e) {
e.printStackTrace();
}
Student stu4 = new Student("4","Tom","男","15678958962");
Student stu5 = new Student("5","lily","女","18963526354");
try{
mStudentManager.addStudent(stu4);
mStudentManager.addStudent(stu5);
}catch (RemoteException e){
e.printStackTrace();
}
try {
mList = (ArrayList<Student>) mStudentManager.getAllStudent();
Log.i(TAG, "添加了2名学生后,返回的学生人数" + mList.size());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this,MyService.class);
bindService(intent,mConnection,BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
好了,到此客户端进程程序就写好,我们运行一下程序看看我们打印的log,和我们预想的是否一致。
AIDL生成代码分析
现在我们分析一下我们写的AIDL文件生成的Java类:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: E:\\sunshine\\lgy\\aidl\\app\\src\\main\\aidl\\com\\lgy\\aidl\\aidl\\IStudentManager.aidl
*/
package com.lgy.aidl.aidl;
public interface IStudentManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.lgy.aidl.aidl.IStudentManager {
private static final java.lang.String DESCRIPTOR = "com.lgy.aidl.aidl.IStudentManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lgy.aidl.aidl.IStudentManager interface,
* generating a proxy if needed.
*/
public static com.lgy.aidl.aidl.IStudentManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lgy.aidl.aidl.IStudentManager))) {
return ((com.lgy.aidl.aidl.IStudentManager) iin);
}
return new com.lgy.aidl.aidl.IStudentManager.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_getAllStudent: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.lgy.aidl.aidl.Student> _result = this.getAllStudent();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addStudent: {
data.enforceInterface(DESCRIPTOR);
com.lgy.aidl.aidl.Student _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lgy.aidl.aidl.Student.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addStudent(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lgy.aidl.aidl.IStudentManager {
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.util.List<com.lgy.aidl.aidl.Student> getAllStudent() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lgy.aidl.aidl.Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getAllStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lgy.aidl.aidl.Student.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addStudent(com.lgy.aidl.aidl.Student stu) 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 ((stu != null)) {
_data.writeInt(1);
stu.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getAllStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
**public java.util.List<com.lgy.aidl.aidl.Student> getAllStudent() throws android.os.RemoteException;
public void addStudent(com.lgy.aidl.aidl.Student stu) throws android.os.RemoteException;**
}
从上面自动生成的代码来看,IStudentManager.java类,其实就是一个接口interface、实现了IInterface接口,在里面有2个我们定义的方法getAllStudent和addStudent(代码中粗色部分)。在该接口中有一个静态内部类Stub 继承Binder类、实现了IStudentManager接口。
现在我们从哪里讲呢?我们在客户端进程的编写过程中,在绑定Service的时候,创建连接的时候,我们有这么一句:
mStudentManager = IStudentManager.Stub.asInterface(iBinder);
好了,我们看看这个静态内部类Stub的静态内部方法asInterface是如何实现的?
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lgy.aidl.aidl.IStudentManager))) {
return ((com.lgy.aidl.aidl.IStudentManager) iin);
}
return new com.lgy.aidl.aidl.IStudentManager.Stub.Proxy(obj);
源码中首先判断参数IBinder是否为null, 接着调用Binder对象的queryLocalInterface方法,这个方法又是做什么的呢?
我们继续查看源代码:
/**
* Use information supplied to attachInterface() to return the
* associated IInterface if it matches the requested
* descriptor.
*/
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
从源代码中看很简单,判断descriptor字符串和自己内部的字符串相等还是不相等,返回的是一个mOwner对象,那么这个mOwner对象是IInterface接口,如下:
private int mObject;
private IInterface mOwner;
private String mDescriptor;
我们就要在去看源码中的构造函数Stub()如下:
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
继续看attachInterface方法如下:
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
看到这里我想我们应该知道queryLocalInterface的含义了?就是查找当前进程是否和服务端进程在同一个程序进程中,如果在同一个进程的话,那么就直接返回找到自身mOwner IInterface对象 如果不在同一个进程的话,就返回给null。那么最终会返回给我们一个Stub类中的静态代理类Proxy。 也就是说代理类Proxy才是真正的实现IPC的关键!。
好了,我知道了这个代理类Proxy,那么他又是如何实现的呢?
第一:代理类也实现了IStudentManager接口,所以他也会有2个方法去实现。
第二:代理类中的构造和私有函数传递的就是我们IStudentManager.Stub.asInterface(iBinder); IBinder对象。
我们看看两个方法的实现:
@Override
public java.util.List<com.lgy.aidl.aidl.Student> getAllStudent() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lgy.aidl.aidl.Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getAllStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lgy.aidl.aidl.Student.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addStudent(com.lgy.aidl.aidl.Student stu) 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 ((stu != null)) {
_data.writeInt(1);
stu.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
在方法的源码实现中我们可以发现就是参数 是否回应,结果的返回都是序列化的Parcel。
在这2个方法中:
mRemote.transact(Stub.TRANSACTION_getAllStudent, _data, _reply, 0);
mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
这2句才是真正的在多进程间数据通信。
在transact方法中:
/**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
*/
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
调用onTransact方法此时调用的是服务端的onTransact方法,因Binder对象是服务端的对象:
@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_getAllStudent: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.lgy.aidl.aidl.Student> _result = this.getAllStudent();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addStudent: {
data.enforceInterface(DESCRIPTOR);
com.lgy.aidl.aidl.Student _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lgy.aidl.aidl.Student.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addStudent(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
从onTransact源码中我们可以看到参数,是否回应,结果都会作为参数传递过来,然后调用相对应的方法,将结果放回对应的参数中去,客户端进程在获取拿到对应的结果返回。
_reply.readException();
_result = _reply.createTypedArrayList(com.lgy.aidl.aidl.Student.CREATOR);
从而整个过程也就完毕!(注意:客户端进程调用mRemote.transact方法和等待服务端onTransact结果 是同步的,也就是说在服务端进程不能做一些耗时的工作,否则客户端进程就会等待 从而可能会造成ANR现象)。
抛开AIDL,自定义代码实现
通过分析,我想我们应该已经有所认识了,那么我们不依靠AIDL文件生成对应的Java类的话,我们其实一样是可以实现IPC的通信。我做个实现步骤总结:
- 定义一个接口类A继承IInterface类,定义我们要实现的方法(按我们的例子就是要写2个方法)。
- 定义一个类B继承Binder类,实现刚刚定义的接口类A
- 定义我们B类的唯一标识DESCRIPTOR,构造函数实现
- 定义我们B类的asInterface静态方法
- 定义我们B类的静态内部类Proxy,实现接口类A
- 在我们定义B类中实现onTransact方法
到此就是需要这6部就完成了自定义类似AIDLJava类文件。剩下的就是我们的使用。在使用的过程中和AIDL完全是一样的,只是这个时候需要用到的IStudentManager、Stub内部类,静态内部类Proxy都是我们自己定义的即可。
看一看我按照以上6部写的类如下:
IStudentManager:
package com.lgy.aidl;
import android.os.IInterface;
import android.os.RemoteException;
import com.lgy.aidl.aidl.Student;
import java.util.List;
public interface IStudentManager extends IInterface{
public List<Student> getAllStudent() throws RemoteException;
public void addStudent(Student stu) throws RemoteException;
}
IStudentStub:
package com.lgy.aidl;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import com.lgy.aidl.aidl.*;
import java.util.List;
public class IStudentStub extends Binder implements IStudentManager{
public static final String DESCRIPTOR = "com.lgy.aidl.IStudentStub";
public static final int TRANSACTION_getAllStudent = FIRST_CALL_TRANSACTION + 0;
public static final int TRANSACTION_addStudent = FIRST_CALL_TRANSACTION + 1;
IStudentStub(){
this.attachInterface(this,DESCRIPTOR);
}
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getAllStudent: {
data.enforceInterface(DESCRIPTOR);
List<Student> _result = this.getAllStudent();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addStudent: {
data.enforceInterface(DESCRIPTOR);
Student _arg0;
if ((0 != data.readInt())) {
_arg0 = Student.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addStudent(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
public static IStudentManager asInterface(IBinder binder){
if (null == binder){
return null;
}
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof IStudentStub){
return (IStudentManager) iin;
}
return new Proxy(binder);
}
@Override
public List<Student> getAllStudent() throws RemoteException {
return null;
}
@Override
public void addStudent(Student stu) throws RemoteException {
}
@Override
public IBinder asBinder() {
return this;
}
static class Proxy implements IStudentManager{
private IBinder mRemote;
Proxy(IBinder binder){
mRemote = binder;
}
@Override
public List<Student> getAllStudent() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
List<Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getAllStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lgy.aidl.aidl.Student.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addStudent(Student stu) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((stu != null)) {
_data.writeInt(1);
stu.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(TRANSACTION_addStudent, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public IBinder asBinder() {
return mRemote;
}
}
}
我们的使用在客户端和服务器端:
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i(TAG, "客户端进程连接成功");
mStudentManager = IStudentStub.asInterface(iBinder);
try {
mList = (ArrayList<Student>) mStudentManager.getAllStudent();
Log.i(TAG,"客户端进程连接成功之后,返回的学生人数" + mList.size());
} catch (RemoteException e) {
e.printStackTrace();
}
Student stu4 = new Student("4","Tom","男","15678958962");
Student stu5 = new Student("5","lily","女","18963526354");
try{
mStudentManager.addStudent(stu4);
mStudentManager.addStudent(stu5);
}catch (RemoteException e){
e.printStackTrace();
}
try {
mList = (ArrayList<Student>) mStudentManager.getAllStudent();
Log.i(TAG, "添加了2名学生后,返回的学生人数" + mList.size());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private IStudentStub mBinder = new IStudentStub(){
@Override
public List<Student> getAllStudent() throws RemoteException {
Log.i(TAG,"客户端调用getAllStudent方法执行,学生人数" + mList.size());
return mList;
}
@Override
public void addStudent(Student stu) throws RemoteException {
Log.i(TAG,"客户端调用addStudent方法执行之前学生人数" + mList.size());
mList.add(stu);
Log.i(TAG, "客户端调用addStudent方法执行之后学生人数" + mList.size());
}
};
我们在看一看运行的log日志是否和我们预期的一致呢?!
再看一下我编写后的程序结构如下:
不知道写到这里大家是否能够理解,有问题大家一起探讨吧!