AIDL --- Android中的远程接口

http://janla.javaeye.com/blog/408616

在Android中, 每个应用程序都可以有自己的进程. 在写UI应用的时候, 经常要用到Service. 在不同的进程中, 怎样传递对象呢?  显然, Java中不允许跨进程内存共享. 因此传递对象, 只能把对象拆分成操作系统能理解的简单形式, 以达到跨界对象访问的目的. 在J2EE中,采用RMI的方式, 可以通过序列化传递对象. 在Android中, 则采用AIDL的方式. 理论上AIDL可以传递Bundle,实际上做起来却比较麻烦.

AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数, 在消息的接收端, 使用这些参数组装成自己需要的对象.

AIDL的IPC的机制和COM或CORBA类似, 是基于接口的,但它是轻量级的。它使用代理类在客户端和实现层间传递值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相关类.; 2. 调用aidl产生的class.

具体实现步骤如下:

1、创建AIDL文件, 在这个文件里面定义接口, 该接口定义了可供客户端访问的方法和属性。 如: ITaskBinder.adil

Java代码 复制代码
  1. package com.cmcc.demo;   
  2.   
  3.     
  4.   
  5. import com.cmcc.demo.ITaskCallback;   
  6.   
  7.     
  8.   
  9. interface ITaskBinder {   
  10.   
  11.        
  12.   
  13.     boolean isTaskRunning();   
  14.   
  15.            
  16.   
  17.     void stopRunningTask();       
  18.   
  19.        
  20.   
  21.     void registerCallback(ITaskCallback cb);       
  22.   
  23.       
  24.   
  25.     void unregisterCallback(ITaskCallback cb);   
  26.   
  27. }  
package com.cmcc.demo; import com.cmcc.demo.ITaskCallback; interface ITaskBinder {        boolean isTaskRunning();            void stopRunningTask();            void registerCallback(ITaskCallback cb);           void unregisterCallback(ITaskCallback cb);}



其中: ITaskCallback在文件ITaskCallback.aidl中定义:

Java代码 复制代码
  1. package com.cmcc.demo;   
  2.   
  3.     
  4.   
  5. interface ITaskCallback {   
  6.   
  7.     void actionPerformed(int actionId);   
  8.   
  9. }  
package com.cmcc.demo; interface ITaskCallback {    void actionPerformed(int actionId);}



注意: 理论上, 参数可以传递基本数据类型和String, 还有就是Bundle的派生类, 不过在Eclipse中,目前的ADT不支持Bundle做为参数, 据说用Ant编译可以, 我没做尝试.

2、编译AIDL文件, 用Ant的话, 可能需要手动, 使用Eclipse plugin的话,可以根据adil文件自动生产java文件并编译, 不需要人为介入.

3、在Java文件中, 实现AIDL中定义的接口. 编译器会根据AIDL接口, 产生一个JAVA接口。这个接口有一个名为Stub的内部抽象类,它继承扩展了接口并实现了远程调用需要的几个方法。接下来就需要自己去实现自定义的几个接口了.

ITaskBinder.aidl中接口的实现, 在MyService.java中接口以内嵌类的方式实现:

Java代码 复制代码
  1. private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {   
  2.   
  3.         public void stopRunningTask() {   
  4.   
  5.             //@TODO   
  6.   
  7.         }   
  8.   
  9.            
  10.   
  11.         public boolean isTaskRunning() {   
  12.   
  13.             //@TODO   
  14.   
  15.             return false;   
  16.   
  17.         }    
  18.   
  19.            
  20.   
  21.         public void registerCallback(ITaskCallback cb) {   
  22.   
  23.             if (cb != null) mCallbacks.register(cb);   
  24.   
  25.         }   
  26.   
  27.         public void unregisterCallback(ITaskCallback cb) {   
  28.   
  29.             if (cb != null) mCallbacks.unregister(cb);   
  30.   
  31.         }   
  32.   
  33. };  
private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {        public void stopRunningTask() {            //@TODO        }                public boolean isTaskRunning() {            //@TODO            return false;        }                 public void registerCallback(ITaskCallback cb) {            if (cb != null) mCallbacks.register(cb);        }        public void unregisterCallback(ITaskCallback cb) {            if (cb != null) mCallbacks.unregister(cb);        }};



在MyActivity.java中ITaskCallback.aidl接口实现:

Java代码 复制代码
  1. private ITaskCallback mCallback = new ITaskCallback.Stub() {   
  2.   
  3.         public void actionPerformed(int id) {   
  4.   
  5.            //TODO   
  6.   
  7.             printf("callback id=" + id);   
  8.   
  9.         }   
  10.   
  11. };  
private ITaskCallback mCallback = new ITaskCallback.Stub() {        public void actionPerformed(int id) {           //TODO            printf("callback id=" + id);        }};


4、向客户端提供接口ITaskBinder, 如果写的是service,扩展该Service并重载onBind ()方法来返回一个实现上述接口的类的实例。这个地方返回的mBinder,就是上面通过内嵌了定义的那个. (MyService.java)

Java代码 复制代码
  1. public IBinder onBind(Intent t) {   
  2.   
  3.     printf("service on bind");   
  4.   
  5.     return mBinder;   
    public IBinder onBind(Intent t) {        printf("service on bind");        return mBinder;}



在Activity中, 可以通过Binder定义的接口, 来进行远程调用.

5、在服务器端回调客户端的函数. 前提是当客户端获取的IBinder接口的时候,要去注册回调函数, 只有这样, 服务器端才知道该调用那些函数在:MyService.java中:

Java代码 复制代码
  1. void callback(int val) {   
  2.   
  3.     final int N = mCallbacks.beginBroadcast();   
  4.   
  5.     for (int i=0; i<N; i++) {   
  6.   
  7.         try {   
  8.   
  9.             mCallbacks.getBroadcastItem(i).actionPerformed(val);   
  10.   
  11.         } catch (RemoteException e) {   
  12.   
  13.             // The RemoteCallbackList will take care of removing   
  14.   
  15.             // the dead object for us.   
  16.   
  17.         }   
  18.   
  19.     }   
  20.   
  21.     mCallbacks.finishBroadcast();   
    void callback(int val) {        final int N = mCallbacks.beginBroadcast();        for (int i=0; i<N; i++) {            try {                mCallbacks.getBroadcastItem(i).actionPerformed(val);            } catch (RemoteException e) {                // The RemoteCallbackList will take care of removing                // the dead object for us.            }        }        mCallbacks.finishBroadcast();}



AIDL的创建方法:

AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。 由于远程调用的需要, 这些参数和返回值并不是任何类型.下面是些AIDL支持的数据类型:

1. 不需要import声明的简单Java编程语言类型(int,boolean等)
2. String, CharSequence不需要特殊声明

3. List, Map和Parcelables类型, 这些类型内所包含的数据成员也只能是简单数据类型, String等其他比支持的类型.
(
(另外: 我没尝试Parcelables, 在Eclipse+ADT下编译不过, 或许以后会有所支持).
下面是AIDL语法:
// 文件名: SomeClass.aidl
// 文件可以有注释, 跟java的一样
// 在package以前的注释, 将会被忽略.
// 函数和变量以前的注释, 都会被加入到生产java代码中.

Java代码 复制代码
  1. package com.cmcc.demo;   
  2.  // import 引入语句   
  3.   
  4. import com.cmcc.demo.ITaskCallback;   
  5.   
  6.     
  7.   
  8. interface ITaskBinder {   
  9.   
  10.     //函数跟java一样, 可以有0到多个参数 ,可以有一个返回值   
  11.   
  12.     boolean isTaskRunning();   
  13.   
  14.            
  15.   
  16.     void stopRunningTask();       
  17.   
  18.     //参数可以是另外的一个aidl定义的接口   
  19.   
  20.     void registerCallback(ITaskCallback cb);       
  21.   
  22.       
  23.   
  24. void unregisterCallback(ITaskCallback cb);   
  25.   
  26. //参数可以是String, 可以用in表入输入类型, out表示输出类型.   
  27. int getCustomerList(in String branch, out String[] customerList);   
  28.   
  29.     
  30. }   
package com.cmcc.demo; // import 引入语句import com.cmcc.demo.ITaskCallback; interface ITaskBinder {    //函数跟java一样, 可以有0到多个参数 ,可以有一个返回值    boolean isTaskRunning();            void stopRunningTask();        //参数可以是另外的一个aidl定义的接口    void registerCallback(ITaskCallback cb);       void unregisterCallback(ITaskCallback cb);//参数可以是String, 可以用in表入输入类型, out表示输出类型.int getCustomerList(in String branch, out String[] customerList); } 



实现接口时有几个原则:
.抛出的异常不要返回给调用者. 跨进程抛异常处理是不可取的.
.IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。 也就是IPC调用会挂起应用程序导致界面失去响应. 这种情况应该考虑单起一个线程来处理.
.不能在AIDL接口中声明静态属性。

IPC的调用步骤:
1. 声明一个接口类型的变量,该接口类型在.aidl文件中定义。
2. 实现ServiceConnection。
3. 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递.
4. 在ServiceConnection.onServiceConnected()实现中,你会接收一个IBinder实例(被调用的Service). 调用
    YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换为YourInterface类型。
5. 调用接口中定义的方法。 你总要检测到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。
6. 断开连接,调用接口实例中的ApplicationContext.unbindService()



下面是整个程序:

Java代码 复制代码
  1. 1. ITaskCallback.aidl   
  2.   
  3.     
  4.   
  5. package com.cmcc.demo;   
  6.   
  7.     
  8.   
  9. interface ITaskCallback {   
  10.   
  11.     void actionPerformed(int actionId);   
  12.   
  13. }   
  14.   
  15.     
  16.   
  17. 2. ITaskBinder.aidl   
  18.   
  19. package com.cmcc.demo;   
  20.   
  21.     
  22.   
  23. import com.cmcc.demo.ITaskCallback;   
  24.   
  25.     
  26.   
  27. interface ITaskBinder {   
  28.   
  29.        
  30.   
  31.     boolean isTaskRunning();   
  32.   
  33.            
  34.   
  35.     void stopRunningTask();       
  36.   
  37.        
  38.   
  39.     void registerCallback(ITaskCallback cb);       
  40.   
  41.       
  42.   
  43.     void unregisterCallback(ITaskCallback cb);   
  44.   
  45. }   
  46.   
  47.     
  48.   
  49. 3.  MyService.java   
  50.   
  51. package com.cmcc.demo;   
  52.   
  53.     
  54.   
  55. import android.app.Service;   
  56.   
  57. import android.content.Intent;   
  58.   
  59. import android.os.IBinder;   
  60.   
  61. import android.os.RemoteCallbackList;   
  62.   
  63. import android.os.RemoteException;   
  64.   
  65. import android.util.Log;   
  66.   
  67.     
  68.   
  69. public class MyService extends Service {   
  70.   
  71.            
  72.   
  73.     @Override  
  74.   
  75.     public void onCreate() {   
  76.   
  77.         printf("service create");   
  78.   
  79.     }   
  80.   
  81.        
  82.   
  83.     @Override  
  84.   
  85.     public void onStart(Intent intent, int startId) {   
  86.   
  87.         printf("service start id=" + startId);   
  88.   
  89.         callback(startId);   
  90.   
  91.     }   
  92.   
  93.        
  94.   
  95.     @Override  
  96.   
  97.     public IBinder onBind(Intent t) {   
  98.   
  99.         printf("service on bind");   
  100.   
  101.         return mBinder;   
  102.   
  103.     }   
  104.   
  105.        
  106.   
  107.     @Override  
  108.   
  109.     public void onDestroy() {   
  110.   
  111.         printf("service on destroy");   
  112.   
  113.         super.onDestroy();   
  114.   
  115.     }   
  116.   
  117.     
  118.   
  119.     @Override  
  120.   
  121.     public boolean onUnbind(Intent intent) {   
  122.   
  123.         printf("service on unbind");   
  124.   
  125.         return super.onUnbind(intent);   
  126.   
  127.     }   
  128.   
  129.        
  130.   
  131.     public void onRebind(Intent intent) {   
  132.   
  133.         printf("service on rebind");   
  134.   
  135.         super.onRebind(intent);   
  136.   
  137.     }   
  138.   
  139.        
  140.   
  141.     private void printf(String str) {   
  142.   
  143.         Log.e("TAG""###################------ " + str + "------");   
  144.   
  145.     }   
  146.   
  147.        
  148.   
  149.     void callback(int val) {   
  150.   
  151.         final int N = mCallbacks.beginBroadcast();   
  152.   
  153.         for (int i=0; i<N; i++) {   
  154.   
  155.             try {   
  156.   
  157.                 mCallbacks.getBroadcastItem(i).actionPerformed(val);   
  158.   
  159.             } catch (RemoteException e) {   
  160.   
  161.                 // The RemoteCallbackList will take care of removing   
  162.   
  163.                 // the dead object for us.   
  164.   
  165.             }   
  166.   
  167.         }   
  168.   
  169.         mCallbacks.finishBroadcast();   
  170.   
  171.     }   
  172.   
  173.        
  174.   
  175.     private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {   
  176.   
  177.         public void stopRunningTask() {   
  178.   
  179.                
  180.   
  181.         }   
  182.   
  183.            
  184.   
  185.         public boolean isTaskRunning() {   
  186.   
  187.             return false;   
  188.   
  189.         }    
  190.   
  191.            
  192.   
  193.         public void registerCallback(ITaskCallback cb) {   
  194.   
  195.             if (cb != null) mCallbacks.register(cb);   
  196.   
  197.         }   
  198.   
  199.         public void unregisterCallback(ITaskCallback cb) {   
  200.   
  201.             if (cb != null) mCallbacks.unregister(cb);   
  202.   
  203.         }   
  204.   
  205.     };   
  206.   
  207.        
  208.   
  209.     final RemoteCallbackList<ITaskCallback> mCallbacks   
  210.   
  211.         = new RemoteCallbackList<ITaskCallback>();   
  212.   
  213. }   
  214.   
  215.     
  216.   
  217. 4. MyActivity.java   
  218.   
  219. package com.cmcc.demo;   
  220.   
  221.     
  222.   
  223. import android.app.Activity;   
  224.   
  225. import android.content.ComponentName;   
  226.   
  227. import android.content.Context;   
  228.   
  229. import android.content.Intent;   
  230.   
  231. import android.content.ServiceConnection;   
  232.   
  233. import android.graphics.Color;   
  234.   
  235. import android.os.Bundle;   
  236.   
  237. import android.os.IBinder;   
  238.   
  239. import android.os.RemoteException;   
  240.   
  241. import android.util.Log;   
  242.   
  243. import android.view.View;   
  244.   
  245. import android.view.ViewGroup;   
  246.   
  247. import android.view.View.OnClickListener;   
  248.   
  249. import android.widget.AbsoluteLayout;   
  250.   
  251. import android.widget.Button;   
  252.   
  253. import android.widget.LinearLayout;   
  254.   
  255. import android.widget.RelativeLayout;   
  256.   
  257. import android.widget.TextView;   
  258.   
  259.     
  260.   
  261. import java.io.BufferedReader;   
  262.   
  263. import java.io.File;   
  264.   
  265. import java.io.FileOutputStream;   
  266.   
  267. import java.io.FileReader;   
  268.   
  269. import java.io.PrintWriter;   
  270.   
  271.     
  272.   
  273. public class MyActivity extends Activity {   
  274.   
  275.        
  276.   
  277.     private Button btnOk;   
  278.   
  279.     private Button btnCancel;   
  280.   
  281.        
  282.   
  283.     @Override  
  284.   
  285.     public void onCreate(Bundle icicle) {   
  286.   
  287.         super.onCreate(icicle);   
  288.   
  289.            
  290.   
  291.         setContentView(R.layout.test_service);   
  292.   
  293.            
  294.   
  295.         btnOk = (Button)findViewById(R.id.btn_ok);   
  296.   
  297.         btnCancel = (Button)findViewById(R.id.btn_cancel);   
  298.   
  299.            
  300.   
  301.         btnOk.setText("Start Service");   
  302.   
  303.         btnCancel.setTag("Stop Service");   
  304.   
  305.            
  306.   
  307.         btnOk.setOnClickListener(new OnClickListener() {   
  308.   
  309.             public void onClick(View v) {   
  310.   
  311.                 onOkClick();   
  312.   
  313.             }   
  314.   
  315.         });   
  316.   
  317.     
  318.   
  319.         btnCancel.setOnClickListener(new OnClickListener() {   
  320.   
  321.             public void onClick(View v) {   
  322.   
  323.                 onCancelClick();   
  324.   
  325.             }   
  326.   
  327.         });    
  328.   
  329.     }   
  330.   
  331.        
  332.   
  333.     void onOkClick() {   
  334.   
  335.         Bundle args = new Bundle();           
  336.   
  337.            
  338.   
  339.         Intent intent = new Intent(this, MyService.class);   
  340.   
  341.         intent.putExtras(args);      
  342.   
  343.            
  344.   
  345.         //printf("send intent to start");   
  346.   
  347.            
  348.   
  349.         //startService(intent);   
  350.   
  351.         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);   
  352.   
  353.         startService(intent);   
  354.   
  355.     }   
  356.   
  357.        
  358.   
  359.     void onCancelClick() {   
  360.   
  361.         Intent intent = new Intent(this, MyService.class);   
  362.   
  363.         //printf("send intent to stop");   
  364.   
  365.            
  366.   
  367.         unbindService(mConnection);   
  368.   
  369.         //stopService(intent);   
  370.   
  371.     }   
  372.   
  373.        
  374.   
  375.     private void printf(String str) {   
  376.   
  377.         Log.e("TAG""###################------ " + str + "------");   
  378.   
  379.     }   
  380.   
  381.        
  382.   
  383.     ITaskBinder mService;   
  384.   
  385.        
  386.   
  387.     private ServiceConnection mConnection = new ServiceConnection() {   
  388.   
  389.         public void onServiceConnected(ComponentName className,   
  390.   
  391.                 IBinder service) {   
  392.   
  393.             mService = ITaskBinder.Stub.asInterface(service);   
  394.   
  395.             try {   
  396.   
  397.                 mService.registerCallback(mCallback);   
  398.   
  399.             } catch (RemoteException e) {   
  400.   
  401.             }   
  402.   
  403.     
  404.   
  405.         }   
  406.   
  407.            
  408.   
  409.         public void onServiceDisconnected(ComponentName className) {   
  410.   
  411.             mService = null;   
  412.   
  413.         }   
  414.   
  415.     };   
  416.   
  417.        
  418.   
  419.     private ITaskCallback mCallback = new ITaskCallback.Stub() {   
  420.   
  421.         public void actionPerformed(int id) {   
  422.   
  423.             printf("callback id=" + id);   
  424.   
  425.         }   
  426.   
  427.     };   
  428.   
  429. }  
posted @ 2010-11-01 09:26  without rest  阅读(490)  评论(0编辑  收藏  举报