android: 使用AIDL实现进程间通信(附示例源码下载)

关于AIDL的介绍及实现步骤等请参考:

http://www.cnblogs.com/hibraincol/archive/2011/09/06/2169325.html

本篇文章只是用一个实例来分析AIDL的实现。

本示例实现的是:AIDL客户端通过AIDL接口获取AIDL服务端中提供的webPage信息,下面详述AIDL通信的实现步骤:

一、编写服务端代码

1. 首先编写AndroidManifest.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<? xml  version="1.0" encoding="utf-8"?>
< manifest  xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.braincol.aidl.service"
       android:versionCode="1"
       android:versionName="1.0">
     < uses-sdk  android:minSdkVersion="8" />
 
     < application  android:icon="@drawable/icon" android:label="@string/app_name">
 
     < service  android:name="RemoteService">
         < intent-filter >
             < action  android:name="com.braincol.aidl.remote.webpage"/>
         </ intent-filter >
     </ service >
 
     </ application >
</ manifest >

 

可以看到服务端的包名为:com.braincol.aidl.service,且该服务端只需一个service组件提供AIDL服务,service组件的名称为RemoteService,这是待会要实现的Service子类。其中<action android:name="com.braincol.aidl.remote.webpage"/> ,指定了action名称为"com.braincol.aidl.remote.webpage", 客户端会通过该action的名称来找到并连接该服务端。

2. 创建RemoteWebPage.aidl文件

在包com.braincol.aidl.service下创建RemoteWebPage.aidl文件:

1
2
3
4
package  com.braincol.aidl.service;
  interface  RemoteWebPage {
     String getCurrentPageUrl();    
}

可以看到内容很简单,该文件中包含一个RemoteWebPage 接口,并且接口中只有getCurrentPageUrl()这么一个方法,后面的客户端将通过这里提供的getCurrentPageUrl()方法获取想要的信息。

3.生成RemoteWebPage.java文件

保存并编译该工程(在eclipse中编译)会看到 gen/ 目录下的com.braincol.aidl.service包下出现了一个RemoteWebPage.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
  * This file is auto-generated.  DO NOT MODIFY.
  * Original file: F:\\workspace\\android\\AIDL-simple\\AIDLService\\src\\com\\braincol\\aidl\\service\\RemoteWebPage.aidl
  */
package  com.braincol.aidl.service;
public  interface  RemoteWebPage extends  android.os.IInterface
{
     /** Local-side IPC implementation stub class. */
     public  static  abstract  class  Stub extends  android.os.Binder implements  com.braincol.aidl.service.RemoteWebPage
     {
         private  static  final  java.lang.String DESCRIPTOR = "com.braincol.aidl.service.RemoteWebPage" ;
         /** Construct the stub at attach it to the interface. */
         public  Stub()
         {
             this .attachInterface( this , DESCRIPTOR);
         }
         /**
          * Cast an IBinder object into an com.braincol.aidl.service.RemoteWebPage interface,
          * generating a proxy if needed.
          */
         public  static  com.braincol.aidl.service.RemoteWebPage 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.braincol.aidl.service.RemoteWebPage))) {
                 return  ((com.braincol.aidl.service.RemoteWebPage)iin);
             }
             return  new  com.braincol.aidl.service.RemoteWebPage.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_getCurrentPageUrl:
             {
                 data.enforceInterface(DESCRIPTOR);
                 java.lang.String _result = this .getCurrentPageUrl();
                 reply.writeNoException();
                 reply.writeString(_result);
                 return  true ;
             }
             }
             return  super .onTransact(code, data, reply, flags);
         }
         private  static  class  Proxy implements  com.braincol.aidl.service.RemoteWebPage
         {
             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  java.lang.String getCurrentPageUrl() 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_getCurrentPageUrl, _data, _reply, 0 );
                     _reply.readException();
                     _result = _reply.readString();
                 }
                 finally  {
                     _reply.recycle();
                     _data.recycle();
                 }
                 return  _result;
             }
         }
         static  final  int  TRANSACTION_getCurrentPageUrl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0 );
     }
     public  java.lang.String getCurrentPageUrl() throws  android.os.RemoteException;
}

这个文件是Android SDK工具根据RemoteWebPage.aidl自动生成的,不要尝试着去修改该文件(改了也白改)。可以看到RemoteWebPage接口内包含了一个名为Stub的抽象的内部类,该类声明了RemoteWebPage.aidl中描述的方法getCurrentPageUrl(),并且还定义了少量的辅助方法Stub还定义了少量的辅助方法,尤其是asInterface(),通过它或以获得IBinder(当applicationContext.bindService()成功调用时传递到客户端的onServiceConnected())并且返回用于调用IPC方法的接口实例,更多细节参见Calling an IPC Method

4. 编写RemoteService.java

为了实现AIDL通信,必须在RemoteService类中实现RemoteWebPage.Stub接口,然后RemoteWebPage.Stbu内的相关方法,下面是RemoteService.java的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package  com.braincol.aidl.service;
 
import  android.app.Service;
import  android.content.Intent;
import  android.os.IBinder;
import  android.os.RemoteException;
import  android.util.Log;
/**
  *
  * @author briancol
  * @description 提供service
  *
  */
public  class  RemoteService extends  Service {
     private  final  static  String TAG = "RemoteService" ;
     @Override
     public  IBinder onBind(Intent intent) {
         Log.i(TAG, "OnBind" );
         return  new  MyBinder();
     }
 
     private  class  MyBinder extends  RemoteWebPage.Stub{
         @Override
         public  String getCurrentPageUrl() throws  RemoteException{
             return  "http://www.cnblogs.com/hibraincol/" ;
         }
     }
}

这样MyBinder就是一个RemoteWebPage.Stub类得子类,这样就可以通过RemoteService向客户端暴露AIDL接口了(MyBinder )。现在,如果客户端(比如一个Activity)调用bindService()来连接该服务端(RemoteService) ,客户端的onServiceConnected()回调函数将会获得从服务端(RemoteService )的onBind()返回的MyBinder对象。

在这里总结下服务端的编写流程:

    1. 创建.aidl文件:

        该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口

    2. 实现这个接口:

        Android SDK将会根据你的.aidl文件产生AIDL接口。生成的接口包含一个名为Stub的抽象内部类,该类声明了所有.aidl中描述的方法,你必须在代码里继承该Stub类并且实现.aidl中定义的方法。

    3.向客户端公开服务端的接口:

        实现一个Service,并且在onBinder方法中返回第2步中实现的那个Stub类的子类(实现类)。

 

至此,服务端的代码就编写完成了。 下面开始编写客户端。

二、编写客户端代码

因为客户端和服务端不在同一个进程(应用程序)中,那么客户端也必须在src/目录下拥有和服务端同样的一份.aidl文件的拷贝(这样的同样是指:包名、类名、内容完全一样,可以把客户端的.aidl文件理解为代理),这样客户端将会通过这个RemoteWebPage.aidl文件在gen/目下生成和服务端一样的RemoteWebPage.java文件,然后客户端就可以通过该接口来访问服务端提供的方法了。下面是客户端的只要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package  com.braincol.aidl.client;
 
import  android.app.Activity;
import  android.content.ComponentName;
import  android.content.Context;
import  android.content.Intent;
import  android.content.ServiceConnection;
import  android.os.Bundle;
import  android.os.IBinder;
import  android.os.RemoteException;
import  android.util.Log;
import  android.view.View;
import  android.view.View.OnClickListener;
import  android.widget.Button;
import  android.widget.TextView;
import  com.braincol.aidl.service.RemoteWebPage;
 
public  class  ClientActivity extends  Activity implements  OnClickListener {
     private  final  static  String TAG= "ClientActivity" ;
     TextView textView ;
     Button btn_bind ;
     Button btn_getAllInfo;
     String actionName = "com.braincol.aidl.remote.webpage" ;
     RemoteWebPage remoteWebPage= null ;
     String allInfo = null ;
     boolean  isBinded= false ;
 
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.main);
         textView = (TextView) findViewById(R.id.textView);
         btn_bind = (Button) findViewById(R.id.btn_bind);
         btn_getAllInfo = (Button)findViewById(R.id.btn_allinfo);
 
         btn_getAllInfo.setEnabled( false );
 
         btn_bind.setOnClickListener( this );
         btn_getAllInfo.setOnClickListener( this );
     }
     @Override
     protected  void  onPause(){
         super .onPause();
         Log.d(TAG, "onPause" );
         if (isBinded){
             Log.d(TAG, "unbind" );
             unbindService(connection);   
         }
     }
     private  class  MyServiceConnection implements  ServiceConnection{
 
         @Override
         public  void  onServiceConnected(ComponentName name, IBinder service) {
             Log.i(TAG, "建立连接..." );
             remoteWebPage = RemoteWebPage.Stub.asInterface(service);
             if (remoteWebPage== null ){
                 textView.setText( "bind service failed!" );   
                 return ;
             }
             try  {
                 isBinded= true ;
                 btn_bind.setText( "断开" );
                 textView.setText( "已连接!" );
                 allInfo = remoteWebPage.getCurrentPageUrl();
                 btn_getAllInfo.setEnabled( true );   
             } catch  (RemoteException e) {
                 e.printStackTrace();
             }
         }
 
         @Override
         public  void  onServiceDisconnected(ComponentName name) {
             Log.i(TAG, "onServiceDisconnected..." );
         }
 
     }
     MyServiceConnection connection = new  MyServiceConnection();
 
     @Override
     public  void  onClick(View v) {
         if (v== this .btn_bind){
             if (!isBinded){
                 Intent intent  = new  Intent(actionName);
                 bindService(intent, connection, Context.BIND_AUTO_CREATE);               
             } else {
                 Log.i(TAG, "断开连接..." );
                 unbindService(connection);
                 btn_getAllInfo.setEnabled( false );   
                 btn_bind.setText( "连接" );
                 isBinded = false ;
                 textView.setText( "已断开连接!" );
             }
         } else  if (v== this .btn_getAllInfo){
             textView.setText(allInfo);
         }
 
     }
}

 

上面的代码中类MyServiceConnection实现了ServiceConnection类,在MyServiceConnection类的onServiceConnected方法中通过RemoteWebPage.Stub.asInterface(service)获取到远端服务端的RemoteWebPage接口对象remoteWebPage,这样就可以通过remoteWebPage.getCurrentPageUrl()获取到服务端提供的相关的信息。客户端通过bindService()方法绑定远程服务端,通过unbindService()断开连接。连接客户端的相关的代码为:

1
2
Intent intent  = new  Intent(actionName);
bindService(intent, connection, Context.BIND_AUTO_CREATE);

客户端就是通过actionName(com.braincol.aidl.remote.webpage)来找到服务端。

下面总结下客户端的编写流程:

    1. 在 src/ 目录下包含.adil文件。

    2. 声明一个IBinder接口(通过.aidl文件生成的)的实例。

    3. 实现ServiceConnection.

    4. 调用Context.bindService()绑定你的ServiceConnection实现类的对象(也就是远程服务端)。

    5. 在onServiceConnected()方法中会接收到IBinder对象(也就是服务端),调用YourInterfaceName.Stub.asInterface((IBinder)service)将返回值转换为YourInterface类型。

    6. 调用接口中定义的方法,并且应该总是捕获连接被打断时抛出的DeadObjectException异常,这是远端方法可能会抛出唯一异常。

    7. 调用Context.unbindService()方法断开连接。

 

下面给出本示例源码的下载地址:

http://download.csdn.net/detail/Niosm/3593187

在附一个稍微复杂点的例子(通过IPC传递Parcelable对象):

http://download.csdn.net/detail/Niosm/3593376

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值