`
kanwoerzi
  • 浏览: 1643557 次
文章分类
社区版块
存档分类
最新评论

AIDL和Service实现两进程通信

 
阅读更多
AIDL和Service实现两进程通信
AIDL(AndroidInterfaceDefinitionLanguage)是一种接口定义语言,用于生成代码允许Android设备上的两个进程间进程通信(IPC).
如果你需要编写一个进程(比如Activity)访问另一个进程(比如Services)的对象的方法代码,你可以使用AIDL自动生成代码而不用自己配置大量的参数.
AIDLIPC基于接口机制,类似COM,Corba,且更加轻量化.它使用一个代理来在客户和实现间传递值.

使用AIDL实现进程通信可分为五个步骤:
一.创建ISimpleRemoteService.aidl文件
packagecom.teleca.ServiceSample;
interfaceISimpleRemoteService
{
voidaddCommands(Stringcmd);
booleanisBusy();
}

利用aidl.exe生成接口文件.若你的IDE安装了ADT,将会在gen目录或src相应包中自动根据描述文件生成同名接口文件.否则请手动:
命令行:

adilpath\SomeService.adil<CR>
注意:
1.自定义类在aidl描述文件中,即便在同一个包中,也要显式import.
2.在aidl文件中所有非Java原始类型参数必须加上标记:in,out,inout.
3.Java原始类型默认为in,且不能为其它.
4.Java原始类型包括为java.lang,java,util包中包括的类.
5.接口名同aidl文件名.
6.接口前不用加访问权限修饰符public,private,protected等,也不能用final,static.

接口文件分析:
接口中生成一个Stub的抽象类,里面包括aidl定义的方法.还包括一些其它辅助方法.值得关注的是asInterface(IBinderiBinder),它返回接口的实例.
二.实现接口
接口的实现需继承接口.Stub.并实现Stub类的方法.
下面给出一个使用匿名方式实现的例子.

privatefinalISimpleRemoteService.Stubbinder=newISimpleRemoteService.Stub()
{
publicvoidaddCommands(Stringcmd)
{
SimpleRemoteService.this.addCmd(cmd);
}
publicbooleanisBusy()
{
returnSimpleRemoteService.this.isBusy();
}
};

注意:
1.没有异常会正常返回
2.RPC通常比较耗时且是异步的,因此应该在线程中调用RPC服务.
3.只支持方法,不支持静态字段.

三.暴露接口给客户
客户要服务,当然要知道在哪有服务.通常一台服务器可能提供不止一个服务.我们这里只有一个服务.
暴露服务必须继承Service.并实现onBind()方法.
@Override
publicIBinderonBind(Intentintent){
//TODOAuto-generatedmethodstub
returnbinder;
}

注意我们这里可以根据intent来返回不同的服务。
四.使用打包传送参数
如果在接口定义文件中使用了默认允许的类型(基本类型,String,CharSequence,List和Map),这些类型都将被自动处理。如果使用了其他的类型,那么该类型必须实现打包功能(Parcelable接口),而且需要创建一个aidl文件声明它是可打包类
可分为如下5个步骤:
4.1实现Parcelable接口
4.2实现publicvoidwriteToParcel(Parcelout)方法
4.3实现publicvoidreadFromParcel(Parcelin)方法
4.4添加一个静态字段CREATOR到实现Parcelable.Creator接口的类中
4.5创建一个aidl文件声明你的可打包的类

示例:
Rect.java文件
importandroid.os.Parcel;
importandroid.os.Parcelable;
publicfinalclassRectimplementsParcelable{
publicintleft;
publicinttop;
publicintright;
publicintbottom;

publicstaticfinalParcelable.Creator<Rect>CREATOR=newParcelable.Creator<Rect>(){
publicRectcreateFromParcel(Parcelin){
returnnewRect(in);
}
publicRect[]newArray(intsize){
returnnewRect[size];
}

};
publicRect(){
}
privateRect(Parcelin){
readFromParcel(in);
}
publicvoidwriteToParcel(Parcelout){
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
publicvoidreadFromParcel(Parcelin){
left=in.readInt();
top=in.readInt();
right=in.readInt();
bottom=in.readInt();
}
}
Rect.aidl文件
packageandroid.graphics;
parcelableRect
;
五.调用IPC方法
调用IPC方法还有6个步骤:
5.1声明aidl定义的接口类型引用
5.2实现ServiceConnection
5.3调用Context.bindService(),传入ServiceConnection的实现
5.4在你的ServiceConnection.onServiceConnected(),你将得到一个IBinder实例(service).
调用YourInterfaceName.Stub.asInterface((IBinder)service)强制转换YourInterface类型.
5.5调用接口定义的方法.你应该始终小心DeadObjectException异常,当连接不成功或中断它就会抛出,这也是远程对象唯一的一个异常.
5.6断开连接,调用Context.unbindService().

注意现在关于如何让AIDL定义生成的接口在客服端可见并没有发现完美的方法。
现在我的做法是把AIDL定义生成的接口,拷贝到客服端的工程中。并且让客服端打包时把AIDL定义生成的接口也打包进去。
我尝试过只用AIDL定义生成的接口来编译客户端程序,并不它打包进去。但是客服端根本无法找到服务器端AIDL文件定义生成的接口。

实例1
服务器端
文件ISimpleRemoteService.aidl
packagecom.teleca.ServiceSample;
interfaceISimpleRemoteService
{
voidaddCommands(Stringcmd);
booleanisBusy();

}
文件SimpleRemoteService.java
packagecom.teleca.ServiceSample;
importandroid.app.Service;
importandroid.content.Intent;
importandroid.os.IBinder;
importandroid.util.Log;
publicclassSimpleRemoteServiceextendsServiceimplementsRunnable{
privatefinalISimpleRemoteService.Stubbinder=newISimpleRemoteService.Stub()
{
publicvoidaddCommands(Stringcmd)
{
SimpleRemoteService.this.addCmd(cmd);
}
publicbooleanisBusy()
{
returnSimpleRemoteService.this.isBusy();
}
};
Stringtag="hubin";
@Override
publicvoidonCreate(){
Log.i(tag,"oncreate");
StartThread();
}
@Override
publicvoidonDestroy(){
blRun=false;
Log.i(tag,"OnDestory");
}
publicvoidStartThread()
{
if(blRun==false)
{
Threadt=newThread(this);
t.start();
}
}
@Override
publicIBinderonBind(Intentintent){
//TODOAuto-generatedmethodstub
returnbinder;
}
booleanblRun=false;
finalstaticintkSleepTime=5;
finalStringcmdPool[]=newString[10];
intcmdStartCursor=-1;
intcmdEndCursor=-1;
publicvoidrun()
{
blRun=true;
while(blRun)
{

if(cmdStartCursor!=cmdEndCursor)
{
cmdStartCursor=(cmdStartCursor+1)%cmdPool.length;
Log.i(tag,"run:"+cmdPool[cmdStartCursor]);
}
try{
Thread.sleep(kSleepTime);
}catch(InterruptedExceptione)
{
Log.e(tag,"InterruptedException",e);
}
}
}
voidaddCmd(Stringcmd)
{
if((cmdEndCursor+1)%cmdPool.length!=cmdStartCursor)
{
cmdEndCursor++;
cmdEndCursor=cmdEndCursor%cmdPool.length;
cmdPool[cmdEndCursor]=cmd;
}
}
booleanisBusy()
{
returncmdEndCursor!=cmdStartCursor;
}
}
文件AndroidManifest.xml
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.teleca.ServiceSample"
android:versionCode="1"
android:versionName="1.0">
<applicationandroid:icon="@drawable/icon"android:label="@string/app_name">
<serviceandroid:name="SimpleRemoteService"android:process=":remote">
<intent-filter>
<actionandroid:name="com.teleca.action.STARTSERVICE"/>
</intent-filter>
</service>
</application>
<uses-sdkandroid:minSdkVersion="7"/>
</manifest>

客服端Hello.java
packagecom.teleca;
importandroid.app.Activity;
importandroid.content.ComponentName;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.ServiceConnection;
importandroid.net.Uri;
importandroid.os.Bundle;
importandroid.os.IBinder;
importandroid.os.RemoteException;
importandroid.util.Log;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
importcom.teleca.ServiceSample.*;
publicclassHelloextendsActivity{
finalstaticStringtag="hubin";
/**Calledwhentheactivityisfirstcreated.*/
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Buttonbutton1=(Button)findViewById(R.id.Button01);
OnClickListenerlistener1=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
doBindService();
}
};
button1.setOnClickListener(listener1);
Buttonbutton2=(Button)findViewById(R.id.Button02);
OnClickListenerlistener2=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
doUnbindService();
}
};
button2.setOnClickListener(listener2);
Buttonbutton3=(Button)findViewById(R.id.Button03);
OnClickListenerlistener3=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
if(mBoundService==null)
{
Log.i(tag,"theServicehasnotbind!Pleasebindtheservicefirst");
return;
}
Stringcmd="Hello:"+System.currentTimeMillis()%100;
try{
mBoundService.addCommands(cmd);
booleanres=mBoundService.isBusy();

Log.i(tag,"isBusy:"+res);
}catch(RemoteExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
};
button3.setOnClickListener(listener3);
}
privateISimpleRemoteServicemBoundService;
privateServiceConnectionmConnection=newServiceConnection(){
publicvoidonServiceConnected(ComponentNameclassName,IBinderiservice){
mBoundService=ISimpleRemoteService.Stub.asInterface(iservice);
}
publicvoidonServiceDisconnected(ComponentNameclassName){
mBoundService=null;
}

};
booleanmIsBound=true;
voiddoBindService(){
Intentintent=newIntent("com.teleca.action.STARTSERVICE");
bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
mIsBound=true;
}
voiddoUnbindService(){
if(mIsBound){
//Detachourexistingconnection.
unbindService(mConnection);
mIsBound=false;
}
}
@Override
protectedvoidonDestroy(){
super.onDestroy();
doUnbindService();
}
}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics