android进程间通信AIDL详解
AIDL简介
AIDL全称是Android Interface Definition Language,字面意思就是android接口定义语言,写这个语言的目的就是定义一个标准的android进程间通信接口。我们开发者就可以通过这个标准统一的接口方便地去进行进程间通信。
Parcelable接口
我们知道不同进程间传递数据前需要先将数据序列化封装,在java中是通过Serializable来实现的,而在android中通过Parcelable接口来更高效地实现序列化封装。所以在这里先创建继承Parcelable接口的类Student.java来保存学生数据。
Student.java
package com.android.wwd.aidldemo;
import android.os.Parcel;
import android.os.Parcelable;
public class Student implements Parcelable {
private String name;
private int number;
public Student(){}
protected Student(Parcel in) {
name = in.readString();
number = in.readInt();
}
public void setName(String newName){
name = newName;
}
public String getName(){
return name;
}
public void setNumber(int newNumber){
number = newNumber;
}
public int getNumber(){
return number;
}
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 dest, int flags) {
dest.writeString(name);
dest.writeInt(number);
}
public void readFromParcel(Parcel dest) {
name = dest.readString();
number = dest.readInt();
}
}
这个类基本功能就是保存姓名和学号,这里需要注意的是继承了Parcelable之后
protected Student(Parcel in)
public void writeToParcel(Parcel dest, int flags)
里面的相应name和number一定要写上,否则传递数据时会丢失name和number数据。
AIDL文件创建
先看下服务端目录,我们把aidl相关文件放在src/main/aidl目录里面
在android studio内创建Student.aidl
// Student.aidl
package com.android.wwd.aidldemo;
// Declare any non-default types here with import statements
parcelable Student;
IStudentsManager.aidl
// IStudentsManager.aidl
package com.android.wwd.aidldemo;
import com.android.wwd.aidldemo.Student;
// Declare any non-default types here with import statements
interface IStudentsManager {
void addStudent(in Student student);
List<Student> getAllStudents();
}
加完后android studio可能会找不到文件,需要在build.gradle内添加
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
}
}
这样基本aidl文件就写完了,我们重新rebuild project之后就会在app\build\generated\source\aidl\debug\com\android\wwd\aidldemo里面生成IStudentsManager.java,而这个java文件才是程序间通信需要用的代码。生成代码如下
IStudentsManager.java
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\Android\\AndroidStudioProjects\\AIDLdemo\\app\\src\\main\\aidl\\com\\android\\wwd\\aidldemo\\IStudentsManager.aidl
*/
package com.android.wwd.aidldemo;
// Declare any non-default types here with import statements
public interface IStudentsManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.wwd.aidldemo.IStudentsManager
{
private static final java.lang.String DESCRIPTOR = "com.android.wwd.aidldemo.IStudentsManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.android.wwd.aidldemo.IStudentsManager interface,
* generating a proxy if needed.
*/
public static com.android.wwd.aidldemo.IStudentsManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.wwd.aidldemo.IStudentsManager))) {
return ((com.android.wwd.aidldemo.IStudentsManager)iin);
}
return new com.android.wwd.aidldemo.IStudentsManager.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_addStudent:
{
data.enforceInterface(DESCRIPTOR);
com.android.wwd.aidldemo.Student _arg0;
if ((0!=data.readInt())) {
_arg0 = com.android.wwd.aidldemo.Student.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addStudent(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getAllStudents:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.android.wwd.aidldemo.Student> _result = this.getAllStudents();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.android.wwd.aidldemo.IStudentsManager
{
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 void addStudent(com.android.wwd.aidldemo.Student student) 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 ((student!=null)) {
_data.writeInt(1);
student.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.util.List<com.android.wwd.aidldemo.Student> getAllStudents() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.android.wwd.aidldemo.Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getAllStudents, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.android.wwd.aidldemo.Student.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getAllStudents = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void addStudent(com.android.wwd.aidldemo.Student student) throws android.os.RemoteException;
public java.util.List<com.android.wwd.aidldemo.Student> getAllStudents() throws android.os.RemoteException;
}
服务端代码
上面AIDL接口已经写完了,现在我们需要写一个服务端去实现这些AIDL接口的具体逻辑,这样客户端才能通过AIDL接口调用服务端的具体逻辑来最终实现进程间通信。直接上service代码。
StudentService.java
package com.android.wwd.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import com.android.wwd.aidldemo.IStudentsManager;
import java.util.ArrayList;
import java.util.List;
public class StudentService extends Service{
//保存学生列表
private List<Student> mStudents = new ArrayList<>();
private final String TAG = "AIDL service";
//这里实现了AIDL接口的具体逻辑
private final IStudentsManager.Stub mBinder = new IStudentsManager.Stub() {
//添加学生信息,用来测试客户端的数据能否传递到服务端
@Override
public void addStudent(Student student) throws RemoteException {
Log.w(TAG,"addStudent");
if (student != null){
Log.w(TAG,"addStudent student.name = "+student.getName());
mStudents.add(student);
}
}
//传递学生列表
@Override
public List<Student> getAllStudents() throws RemoteException {
synchronized (this) {
Log.w(TAG,"getAllStudents mStudents = "+mStudents);
if (mStudents != null) {
return mStudents;
}
return new ArrayList<>();
}
}
};
//启动service时写入一个学生信息,用来测试客户端是否读取得到这个信息
@Override
public void onCreate() {
super.onCreate();
Student student = new Student();
student.setName("Jone");
student.setNumber(1);
mStudents.add(student);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
最后AndroidManifest注册service
<service
android:name=".StudentService"
android:exported="true">
<intent-filter>
<action android:name="com.android.wwd.AIDL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
这样服务端代码就写完,接下来新建一个项目作为客户端。
客户端代码
先上目录,这里aidl相关目录要和服务端的包名类名保持一致,所以这里直接把文件夹整个复制过来了。
完整的客户端代码如下
package com.android.wwd.aidlclient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.android.wwd.aidldemo.IStudentsManager;
import com.android.wwd.aidldemo.Student;
import java.util.ArrayList;
import java.util.List;
public class ClientActivity extends AppCompatActivity {
private IStudentsManager mStudentsManager = null;
private List<Student> mStudents = new ArrayList<>();
private final String TAG = "AIDL client";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
//获取服务器学生列表
Button mButton = (Button)findViewById(R.id.show_list);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mStudentsManager != null){
try {
mStudents = mStudentsManager.getAllStudents();
if(mStudents != null){
for(int i = 0; i < mStudents.size(); i++){
Log.w(TAG,"name = "+mStudents.get(i).getName()+",
number = "+mStudents.get(i).getNumber());
}
}
}catch (RemoteException e){
Log.w(TAG,"getStudents error!");
}
}
}
});
//向服务器添加学生
Button addButton = (Button)findViewById(R.id.add_student);
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Student student = new Student();
student.setName("mike");
student.setNumber(16);
if(mStudentsManager != null){
try {
mStudentsManager.addStudent(student);
}catch (RemoteException e){
Log.w(TAG,"addStudent error!");
}
}
}
});
//启动service
Intent intent = new Intent();
intent.setAction("com.android.wwd.AIDL");
intent.setPackage("com.android.wwd.aidldemo");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(mServiceConnection);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"onServiceConnected");
mStudentsManager = IStudentsManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG,"onServiceDisconnected");
}
};
}
这样所有的代码都写完了,直接安装这两个apk之后运行下看下LOG。
服务端LOG
18:43:58.411 12731-12744/com.android.wwd.aidldemo W/AIDL service: addStudent
18:43:58.411 12731-12744/com.android.wwd.aidldemo W/AIDL service: addStudent student.name = mike
18:44:17.850 12731-12744/com.android.wwd.aidldemo W/AIDL service:
getAllStudents mStudents = [name : Jone , number : 1, name : mike , number : 16]
客户端LOG
18:44:17.853 13016-13016/com.android.wwd.aidlclient W/AIDL client: name = Jone, number = 1
18:44:17.854 13016-13016/com.android.wwd.aidlclient W/AIDL client: name = mike, number = 16
43:58点击添加学生按钮,44:17点击查看学生列表按钮,可以看到整个进程间通信成功。
AIDL流程分析
我们先从服务端来看,服务端如果需要一个支持绑定的service,需要重写onBind()方法,返回IBinder对象给客户端。这样客户端和服务端就能通过这个接口来通信了。在本文中我们IBinder对象通过AIDL接口创建。
IStudentsManager.Stub mBinder = new IStudentsManager.Stub(){
@Override
public void addStudent(Student student) throws RemoteException {
...
}
@Override
public List<Student> getAllStudents() throws RemoteException {
...
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
这里可以看到mBinder是通过new IStudentsManager.Stub()生成,回头看生成的IStudentsManager.java文件
public static abstract class Stub extends android.os.Binder implements com.android.wwd.aidldemo.IStudentsManager
可以看到这是一个抽象类继承Binder,所以我们这里重写addStudent和getAllStudents方法,创建Binder类mBinder,传回客户端。
接着看客户端,通过bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)绑定service,
private IStudentsManager mStudentsManager = null;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"onServiceConnected");
mStudentsManager = IStudentsManager.Stub.asInterface(service);
}
};
这里onServiceConnected(ComponentName name, IBinder service)的IBinder service正是服务端中onBind返回的mBinder对象。而mBinder无法直接调用接口,我们这里需要获取IStudentsManager对象才能调用相应接口实现通信,所以mStudentsManager = IStudentsManager.Stub.asInterface(service);成为了最后的关键,这里继续查看生成文件IStudentsManager.java来查看他是如何实现的。
之前分析服务端的时候我们知道这里onServiceConnected返回的IBinder对象是new IStudentsManager.Stub()生成的,所以new时会走IStudentsManager.java的
IStudentsManager.java
public interface IStudentsManager extends android.os.IInterface
{
private static final java.lang.String DESCRIPTOR = "com.android.wwd.aidldemo.IStudentsManager";
...
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
}
这里要注意第一个this参数传的是IInterface对象,所以这里传递的正是我们的IStudentsManager。
Stub继承Binder.java查看源代码如下
public class Binder implements IBinder {
private IInterface mOwner;
private String mDescriptor;
//这里的owner正是上面的IStudentsManager对象
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
}
前面铺垫说完了继续回来看asInterface方法
IStudentsManager.java
public static com.android.wwd.aidldemo.IStudentsManager asInterface(android.os.IBinder obj)
{
//这里obj是我们service返回的mBinder所以不为空
if ((obj==null)) {
return null;
}
//这里是关键看到IInterface我们第一个想到的是上面说的IStudentsManager
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.wwd.aidldemo.IStudentsManager))) {
return ((com.android.wwd.aidldemo.IStudentsManager)iin);
}
//如果上面iin获取失败这里创建一个IStudentsManager对象返回
return new com.android.wwd.aidldemo.IStudentsManager.Stub.Proxy(obj);
}
我们知道obj为Binder对象,继续看obj.queryLocalInterface(DESCRIPTOR)代码
Binder.java
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
在上面分析的attachInterface方法中传入了2个参数mOwner和mDescriptor正是这里的值,所以return mOwner。
总结一下就是在服务端创建mBinder初始化时,将IStudentsManager赋值给mOwner,然后在客户端读取了mOwner对象,至此整个AIDL流程分析完毕。