这一篇我将给大家讲解AIDL是如何使得服务端和客户端能够进行通信的。我上传了一份简单的源码(前往下载),实现了不同进程服务端和客户端进行通信。大家在看这篇博客的时候务必要将这份源码导入工程跑起来。跟着讲解一步步走。当然,在阅读这篇文章的时候,最好将博客一 二 三看一遍。
源码执行过程分析:
源码有上传也在下面贴出来了。
1:首先把服务端app跑起来,服务端app界面里有两个按钮,一个是绑定服务,一个是解绑服务。
2:点击绑定服务按钮,执行bindService(intent, conn, Service.BIND_AUTO_CREATE);执行该代码后,会相继执行MyService里面的onCreate->onBind。
(在MyService.java 里面先会创建一个Binder类型的对象iPerson(private IPerson.Stub iPerson = new Person()),这个对象就是前面一直提到的服务端的那个Binder对象。然后服务执行onCreate函数启动服务,然后再执行public IBinder onBind(Intent intent)函数,该函数必须返回iPerson这个Binder类型的对象。我感觉只有这里返回之后才会开始执行上一篇博客贴的图5-3的流程,也就是AmS调用 scheduleBindService带的参数就包含这个服务端的Binder对象,继而会回调服务端MainActivity里面的conn接口,这也是为什么要conn接口会被触发,必须服务端要重载onBind这个函数的原因。)
3:第二步中说到执行到了服务端MainActivity里面的conn接口,public void onServiceConnected(ComponentName name, IBinder service)函数会被执行,通过该参数调用IPerson iPerson = IPerson.Stub.asInterface(service);客户端就获取到了之前一直提到的客户端的Binder对象。(服务端onBind返回的对象为Person类型,Person类继承IPerson.Stub(为什么要添加这个Person类?因为IPerson.Stub是个抽象类,我们并不能直接通过它得到Binder的实例 ),IPerson.Stub继承Binder又implements了IPerson,所以此处可以认为IPerson 是Person的父类,把子类对象赋值给父类,再通过父类对象iPerson ,就可以调用到Person里面定义的setValue和getValue这两个函数,这也是常常说到的覆盖)。因为目前这个客户端和服务端处于同一个app当中,所以通过IPerson.Stub.asInterface(service);获取到的Binder类型的对象就是MyService.java里面onBinder返回的那个对象,也就是获取到的是服务端的那个Binder对象。若是客户端与服务端在不同的app当中,那么通过IPerson iPerson = IPerson.Stub.asInterface(service);获取到的Binder对象是Binder驱动中的那个mRemote对象。大家可以断点跟进asInterface中去看看。asInterface这个函数在AIDL生成的对应的java文件中也就是在IPerson.java这个文件中。如果上面的不是很能理解,可以先跳到下面我对AIDL生成的IPerson.java这个文件解释去先看。
4:到这里服务端就执行完毕。那么我们接着把客户端app执行起来。
5:客户端app启动,点击界面里的远程调用service按钮。那么会执行bindService(intent, conn, Service.BIND_AUTO_CREATE);代码。调用该代码并不会又去启动服务,因为服务端已经跑起来了,可以认为是为了客户端和服务端建立连接。断点跟踪服务端的MyService.java 里面的代码,发现不会执行onCreate,只会执行onBind,为什么会执行onBind,原因在步骤2中有解释。继而就会回调到客户端里面定义的conn接口。同样,客户端里面的conn接口里面的public void onServiceConnected(ComponentName name, IBinder service)函数会被执行。在该函数里面通过调用IPerson person= IPerson.Stub.asInterface(service);就可以获取到客户端所需要的这个Binder对象,不过此时该Binder对象其实是Binder驱动里面的那个mRemote对象。
6:有了person这个对象,就可以调用服务端在Person里面定义的函数了(因为该对象其实就是来自服务端,所以自然可以调用服务端定义的函数)。接着代码往下走调用String name= person.getValue();。此时会调用到在客户端AIDL文件生成对应的IPerson.java这个文件里面的class Proxy里面的getValue函数,在该函数里面会调用mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0)(mRemote就是之前博客里一直提到的Binder驱动里面的那个Binder对象),此时客户端进入等待状态,等待服务端数据的返回。该函数被调用后,将代码切至服务端的AIDL文件生成对应的IPerson.java文件里面,此时会执行该文件里的onTransact函数,这样服务端就收到了客户端发送过来的数据,同时调用reply对象回复客户端,客户端收到回复后,从调用mRemote.transact之后的地方继续执行,然后就可以读取到服务端发送回来的消息。这也就是在博客二中,介绍Binder驱动时提到的那三点。
7:好了,到了这里服务端和客户端就借助Binder对象真正的完成了跨进程间的一次通信,大家可以根据上述描述把代码跑起来,利用断点进行跟踪。接下来还一项任务就是分析AIDL文件生成的对应的Java 文件。
分析AIDL文件生成的对应的Java文件的组成
源码为下面贴出的IPerson.java。该代码在gen目录下。
首先看代码由三部分组成:
1:interface IPerson extends android.os.IInterface
2:static abstract classStubextends android.os.Binder implements com.example.server.IPerson
3:static class Proxy implements com.example.server.IPerson
1: 在IPerson里面里面有类Stub,Proxy。同时还有在IPeson.java类里面声明的两个函数setValue和getValue。目的在于在conn接口,public void onServiceConnected(ComponentName name, IBinder service)里面通过IPerson iPerson = IPerson.Stub.asInterface(service);这种方式取得Binder对象时,可以直接通过iPerson调用到Peson.java里面的函数setValue和getValue。上面说过(Person类继承IPerson.Stub,IPerson.Stub继承Binder又implements了IPerson,所以此处可以认为IPerson 是Person的父类,把子类对象赋值给父类,再通过父类对象iPerson ,就可以调用到Person里面定义的setValue和getValue这两个函数,这也是常常说到的覆盖)。
2:Stub 类继承至Binder并实现了IPerson接口。
a:Stub里面有个asInterface函数,用于返回这个接口对象类型。
参数是IBinder类型,通过该IBinder判断,如果服务端和客户端再同一个进程,
那么得到的是该接口类型的对象。
如果服务端和客户端不在同一个进程,那么得到的是Proxy接口类型的对象。
b:Stub里面还有个asBinder函数,用于返回Stub类对象本身。
c:Stub里面还有个onTransact函数,用于接收客户端发送回来的数据信息。
3:Proxy 类实现了IPerson接口。
a:该类的主要作用是提供给客户端使用,作为与服务端沟通的一个代理
b:Proxy里面有一个IBinder类型的mRemote成员变量,该变量其实是上面提到的Binder驱动里面的那个变量。
c:Proxy里面有个构造函数,用于给mRemote成员变量赋值,值的来源于Stub类里面asInterface函数的参数。
e:Proxy里面有步骤1中提到的getValue/setValue函数,并且提供了具体的实现,这些函数实际是给客户端调用的, 客户端获取到这个Proxy对象,然后调用Proxy里面getValue函数,getValue函数里面会调用mRemote.transact这个函数,目的在于给服务端发送消息,此时getValue函数会在mRemote.transact调用处进行等待服务端的消息返回。服务端接收到消息后就会触发步骤3中C里面提到的onTransact函数,然后服务端可以做出相应的回应。然后 getValue继续往下走,获取服务端返回的值,并返回给客户端。
4:在客户端conn接口,可以通过 IPerson.Stub.asInterface(service);获取到IPerson类型的对象。然后可以调用IPerson接口里面声明的函数。
好了,讲到这里也差不多讲完了,简单的说,这几篇博客讲的其实就是IPC进程间通信AIDL方式的实现原理及过程。不知道大家看到这里是否明白了。
服务端客户端源码:
首先我们看下服务端和客户端的源码结构:
服务端:
MainActivity.java
package com.example.server;
import com.example.server.IPerson;
import com.example.server.R;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener{
private Button bindButton;
private Button unbindButton;
private IPerson iPerson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindButton = (Button)findViewById(R.id.button1);
unbindButton = (Button)findViewById(R.id.button2);
bindButton.setOnClickListener(this);
unbindButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button1:
Intent intent = new Intent(MainActivity.this,MyService.class);
bindService(intent, conn, Service.BIND_AUTO_CREATE);
break;
case R.id.button2:
unbindService(conn);
break;
default:
break;
}
}
private ServiceConnection conn = new ServiceConnection() {
//连接对象
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
iPerson = IPerson.Stub.asInterface(service);
if(iPerson!=null){
try {
iPerson.setValue("AIDL TEST");
Toast.makeText(MainActivity.this, "赋值成功", Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(MainActivity.this, "赋值失败", Toast.LENGTH_LONG).show();
}
}
}
};
}
MyService.java
package com.example.server;
import com.example.server.IPerson;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service{
private IPerson.Stub iPerson = new Person();
@Override
public void onCreate()
{
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return iPerson;
}
}
Person.java
package com.example.server;
import com.example.server.IPerson;
import android.os.RemoteException;
public class Person extends IPerson.Stub{
private String name;
@Override
public void setValue(String name) throws RemoteException {
// TODO Auto-generated method stub
this.name = name;
}
@Override
public String getValue() throws RemoteException {
// TODO Auto-generated method stub
return this.name;
}
}
IPerson.aidl
package com.example.server;
interface IPerson{
void setValue(String name);
String getValue();
}
MainActivity.java
package com.example.client;
import com.example.server.IPerson;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
private Button btn;
private IPerson person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent();
intent.setAction("com.example.server.MyService");
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public synchronized void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
person = IPerson.Stub.asInterface(service);
if(person != null){
try {
String name1 = person.getValue();
Toast.makeText(MainActivity.this, "远程调用成功,值为:"+name1, Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(MainActivity.this, "远程调用失败", Toast.LENGTH_LONG).show();
}
}
}
};
}
IPerson.aidl 与服务端的IPerson.aidl一样
IPerson.java
在Eclipse里面发现有aidl文件,会根据该aidl文件,自动在服务端和客户端的gen目录下生成对应的IPerson.java,服务端和客户端该文件的内容一样。该文件内容比较重要,也是实现两者通信的关键,同时也是将前几篇博客将的内容对应到该代码中。,其内容如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Users\\Administrator\\Desktop\\AIDLServiceDemo\\AIDLServiceServer\\src\\com\\example\\server\\IPerson.aidl
*/
package com.example.server;
public interface IPerson extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.example.server.IPerson {
private static final java.lang.String DESCRIPTOR = "com.example.server.IPerson";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.server.IPerson interface,
* generating a proxy if needed.
*/
public static com.example.server.IPerson asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.server.IPerson))) {
return ((com.example.server.IPerson) iin);
}
return new com.example.server.IPerson.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_setValue: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setValue(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getValue: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getValue();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.server.IPerson {
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 setValue(java.lang.String name)
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.writeString(name);
mRemote.transact(Stub.TRANSACTION_setValue, _data, _reply,
0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.lang.String getValue()
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_getValue, _data, _reply,
0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void setValue(java.lang.String name)
throws android.os.RemoteException;
public java.lang.String getValue() throws android.os.RemoteException;
}
Binder与Service 通信机制详解二 (Binder与Service理解)
Binder与Service 通信机制详解三 (服务端和客户端设计)
Binder与Service 通信机制详解四 (源码分析AIDL工作机制)