理解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(); }
程序清单: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; }
程序清单: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; } }
程序清单: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(); } }
程序清单: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(); } } } }; }
程序清单: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
>
上面Android应用程序运行后结果如下所示:


小结:
1、重点理解Android中对AIDL文件的定义,以及理解ADT工具自动生成的接口类IPlayer,特别是它的静态内部类Stub以及Stub的asInterface方法,
2、Service作为一个代理角色,在其它应用程序通过Stub类的asInterface方法在绑定到一个服务时才能实现返回该接口实例,进而对该实例进行相关操作。