理解Android系统中的轻量级解决方案RPC的原理,需要先回顾一下JAVA中的RMI(Remote Method Invocation)这个易于使用的纯JAVA方案(用来实现分布式应用)。有关RMI的相关知识,可以通过下图来归纳:
Android中的RPC也是参考了JAVA中的RMI方案,这里我们再详细了解一下RPC的实现过程。
Android中的RPC机制是为了实现一个进程使用另一个进程中的远程对象,它使用了Android自己的AIDL(接口定义语言),使用户很方便地定义出一个接口作为规范,通过一个远程Service为代理 ,客户端在绑定该远程Service过程中获取远程对象,进而使用该对象。可参考下图所示:
补充:RPC的另一个目的是对客户端只声明接口及方法,隐藏掉具体实现类,供客户端直接获取此接口实例。
实例代码:
实例一:通过Service来远程调用一个接口子类的函数方法
功能描述:在MainActivity中通过绑定MyService服务类,来远程调用MyPlayer(实现了IPlayer接口)的方法过程。需要定义一个IPlayer.aidl文件,ADT工具会自动生成一个IPlayer接口类,然后再由MyPlayer继承IPlayer接口类中的静态内部抽象类,实现接口方法,进而供其它应用程序远程调用。(在本例中为了方便,MainActivity与MyService类同处一个应用程序中,实现运用时,可以不在同一个应用程序中,只要有权限访问MyService服务,就能得到IPlayer接口,进而执行该接口实例方法)
程序清单:IPlayer.aidl
package
com.magc.rpc;
interface IPlayer
{
void setName(String name);
void addFile(String f_name);
String ToString();
}
interface IPlayer
{
void setName(String name);
void addFile(String f_name);
String ToString();
}
程序清单:IPlayer.java (ADT根据上面IPlayer.aidl文件自动生成,不能编辑该文件)
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: F:\\work\\Android_App\\MyRPCService\\src\\com\\magc\\rpc\\IPlayer.aidl
*/
package com.magc.rpc;
public interface IPlayer extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.magc.rpc.IPlayer
{
private static final java.lang.String DESCRIPTOR = " com.magc.rpc.IPlayer " ;
/** Construct the stub at attach it to the interface. */
public Stub()
{
this .attachInterface( this , DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.magc.rpc.IPlayer interface,
* generating a proxy if needed.
*/
public static com.magc.rpc.IPlayer asInterface(android.os.IBinder obj)
{
if ((obj == null )) {
return null ;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null ) && (iin instanceof com.magc.rpc.IPlayer))) {
return ((com.magc.rpc.IPlayer)iin);
}
return new com.magc.rpc.IPlayer.Stub.Proxy(obj);
}
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_setName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this .setName(_arg0);
reply.writeNoException();
return true ;
}
case TRANSACTION_addFile:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this .addFile(_arg0);
reply.writeNoException();
return true ;
}
case TRANSACTION_ToString:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this .ToString();
reply.writeNoException();
reply.writeString(_result);
return true ;
}
}
return super .onTransact(code, data, reply, flags);
}
private static class Proxy implements com.magc.rpc.IPlayer
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
public void setName(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_setName, _data, _reply, 0 );
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public void addFile(java.lang.String f_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(f_name);
mRemote.transact(Stub.TRANSACTION_addFile, _data, _reply, 0 );
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public java.lang.String ToString() 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_ToString, _data, _reply, 0 );
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0 );
static final int TRANSACTION_addFile = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1 );
static final int TRANSACTION_ToString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2 );
}
public void setName(java.lang.String name) throws android.os.RemoteException;
public void addFile(java.lang.String f_name) throws android.os.RemoteException;
public java.lang.String ToString() throws android.os.RemoteException;
}
* This file is auto-generated. DO NOT MODIFY.
* Original file: F:\\work\\Android_App\\MyRPCService\\src\\com\\magc\\rpc\\IPlayer.aidl
*/
package com.magc.rpc;
public interface IPlayer extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.magc.rpc.IPlayer
{
private static final java.lang.String DESCRIPTOR = " com.magc.rpc.IPlayer " ;
/** Construct the stub at attach it to the interface. */
public Stub()
{
this .attachInterface( this , DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.magc.rpc.IPlayer interface,
* generating a proxy if needed.
*/
public static com.magc.rpc.IPlayer asInterface(android.os.IBinder obj)
{
if ((obj == null )) {
return null ;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null ) && (iin instanceof com.magc.rpc.IPlayer))) {
return ((com.magc.rpc.IPlayer)iin);
}
return new com.magc.rpc.IPlayer.Stub.Proxy(obj);
}
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_setName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this .setName(_arg0);
reply.writeNoException();
return true ;
}
case TRANSACTION_addFile:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this .addFile(_arg0);
reply.writeNoException();
return true ;
}
case TRANSACTION_ToString:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this .ToString();
reply.writeNoException();
reply.writeString(_result);
return true ;
}
}
return super .onTransact(code, data, reply, flags);
}
private static class Proxy implements com.magc.rpc.IPlayer
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
public void setName(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_setName, _data, _reply, 0 );
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public void addFile(java.lang.String f_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(f_name);
mRemote.transact(Stub.TRANSACTION_addFile, _data, _reply, 0 );
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public java.lang.String ToString() 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_ToString, _data, _reply, 0 );
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0 );
static final int TRANSACTION_addFile = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1 );
static final int TRANSACTION_ToString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2 );
}
public void setName(java.lang.String name) throws android.os.RemoteException;
public void addFile(java.lang.String f_name) throws android.os.RemoteException;
public java.lang.String ToString() throws android.os.RemoteException;
}
程序清单:MyPlayer.java (实现IPlayer的静态内部抽象类Stub)
package
com.magc.rpc;
import android.os.RemoteException;
import android.util.Log;
import com.magc.rpc.IPlayer.Stub;
/**
*
* @author magc
* 实现IPlayer接口类中的静态内部抽象类,即实现IPlayer接口方法
* 将来供其它应用程序远程调用执行方法
*/
public class MyPlayer extends Stub {
private String name = "" ;
@Override
public void addFile(String fName) throws RemoteException {
System.out.println( " add file ... " );
}
@Override
public void setName(String name) throws RemoteException {
this .name = name;
Log.i( " magc " , " setName-- " + name);
}
public String ToString()
{
String str = " MyPlayer-- " + name;
Log.i( " magc " , " MyPlayer-- " + name);
return str;
}
}
import android.os.RemoteException;
import android.util.Log;
import com.magc.rpc.IPlayer.Stub;
/**
*
* @author magc
* 实现IPlayer接口类中的静态内部抽象类,即实现IPlayer接口方法
* 将来供其它应用程序远程调用执行方法
*/
public class MyPlayer extends Stub {
private String name = "" ;
@Override
public void addFile(String fName) throws RemoteException {
System.out.println( " add file ... " );
}
@Override
public void setName(String name) throws RemoteException {
this .name = name;
Log.i( " magc " , " setName-- " + name);
}
public String ToString()
{
String str = " MyPlayer-- " + name;
Log.i( " magc " , " MyPlayer-- " + name);
return str;
}
}
程序清单:MyService.java (一个Service类,供其它程序来远程绑定,返回IPlayer接口)
package
com.magc.rpc;
import com.magc.rpc.IPlayer.Stub;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
/**
*
* @author magc
* 此服务类作为一个代理角色,供其它应用程序绑定,并返回接口实例
*
* 可看作是代理模式的应用
*/
public class MyService extends Service {
private Stub player = new MyPlayer();
@Override
public IBinder onBind(Intent arg0) {
return player;
}
@Override
public void onCreate() {
super .onCreate();
}
}
import com.magc.rpc.IPlayer.Stub;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
/**
*
* @author magc
* 此服务类作为一个代理角色,供其它应用程序绑定,并返回接口实例
*
* 可看作是代理模式的应用
*/
public class MyService extends Service {
private Stub player = new MyPlayer();
@Override
public IBinder onBind(Intent arg0) {
return player;
}
@Override
public void onCreate() {
super .onCreate();
}
}
程序清单:MainActivity.java (作为客户端远程调用IPlayer接口方法)
package
com.magc.rpc;
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.util.Log;
/**
*
* @author magc
* 作为一个客户端通过绑定MyService服务,实现远程调用IPlayer接口方法
*
*/
public class MainActivity extends Activity {
private String ACTION = " com.magc.rpc.action.MYSERVICE " ;
private IPlayer player;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent intent = new Intent();
intent.setAction(ACTION);
// 绑定MyService服务
bindService(intent, conn, BIND_AUTO_CREATE);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
/**
* 绑定MyService服务后,返回IPlayer接口,进而调用该接口方法
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i( " magc " , " bind service ..... " );
player = IPlayer.Stub.asInterface(service);
if (player != null )
{
try {
player.setName( " magc " );
player.ToString();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
}
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.util.Log;
/**
*
* @author magc
* 作为一个客户端通过绑定MyService服务,实现远程调用IPlayer接口方法
*
*/
public class MainActivity extends Activity {
private String ACTION = " com.magc.rpc.action.MYSERVICE " ;
private IPlayer player;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent intent = new Intent();
intent.setAction(ACTION);
// 绑定MyService服务
bindService(intent, conn, BIND_AUTO_CREATE);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
/**
* 绑定MyService服务后,返回IPlayer接口,进而调用该接口方法
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i( " magc " , " bind service ..... " );
player = IPlayer.Stub.asInterface(service);
if (player != null )
{
try {
player.setName( " magc " );
player.ToString();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
}
程序清单:AndroidManifest.xml (注册Activity和Service)
<?
xml version="1.0" encoding="utf-8"
?>
< manifest xmlns:android ="http://schemas.android.com/apk/res/android"
package ="com.magc.rpc"
android:versionCode ="1"
android:versionName ="1.0" >
< uses-sdk android:minSdkVersion ="9" />
< application android:icon ="@drawable/icon" android:label ="@string/app_name" >
< activity android:name =".MainActivity"
android:label ="@string/app_name" >
< intent-filter >
< action android:name ="android.intent.action.MAIN" />
< category android:name ="android.intent.category.LAUNCHER" />
</ intent-filter >
</ activity >
< service android:name =".MyService" >
< intent-filter >
< action android:name ="com.magc.rpc.action.MYSERVICE" />
< category android:name ="android.intent.category.DEFAULT" />
</ intent-filter >
</ service >
</ application >
</ manifest >
< manifest xmlns:android ="http://schemas.android.com/apk/res/android"
package ="com.magc.rpc"
android:versionCode ="1"
android:versionName ="1.0" >
< uses-sdk android:minSdkVersion ="9" />
< application android:icon ="@drawable/icon" android:label ="@string/app_name" >
< activity android:name =".MainActivity"
android:label ="@string/app_name" >
< intent-filter >
< action android:name ="android.intent.action.MAIN" />
< category android:name ="android.intent.category.LAUNCHER" />
</ intent-filter >
</ activity >
< service android:name =".MyService" >
< intent-filter >
< action android:name ="com.magc.rpc.action.MYSERVICE" />
< category android:name ="android.intent.category.DEFAULT" />
</ intent-filter >
</ service >
</ application >
</ manifest >
上面Android应用程序运行后结果如下所示:
![2011042000074112.png](https://i-blog.csdnimg.cn/blog_migrate/f1fd305fe084282788d4c9bc1f99069e.png)
![2011042000075710.png](https://i-blog.csdnimg.cn/blog_migrate/1a557e832602e93cf91af2af1fe8c7d3.png)
小结:
1、重点理解Android中对AIDL文件的定义,以及理解ADT工具自动生成的接口类IPlayer,特别是它的静态内部类Stub以及Stub的asInterface方法,
2、Service作为一个代理角色,在其它应用程序通过Stub类的asInterface方法在绑定到一个服务时才能实现返回该接口实例,进而对该实例进行相关操作。
待续