理解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文件自动生成,不能编辑该文件)
package
com.magc.rpc;
public
interface
IPlayer
extends
android.os.IInterface
{
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
"
;
public
Stub()
{
this
.attachInterface(
this
, DESCRIPTOR);
}
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;
}
package
com.magc.rpc;
import
android.os.RemoteException;
import
android.util.Log;
import
com.magc.rpc.IPlayer.Stub;
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;
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;
public
class
MainActivity
extends
Activity {
private
String ACTION
=
"
com.magc.rpc.action.MYSERVICE
"
;
private
IPlayer player;
@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) {
}
@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)