在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. }  


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

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


注意: 理论上, 参数可以传递基本数据类型和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. };  


在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. };  

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;  


在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();  


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. }   


实现接口时有几个原则: 
.抛出的异常不要返回给调用者. 跨进程抛异常处理是不可取的. 
.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 on 2010-02-20 09:44  清水湾  阅读(486)  评论(0编辑  收藏  举报