Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android進階:AIDL實現IPC使用詳解

Android進階:AIDL實現IPC使用詳解

編輯:Android開發實例

使用AIDL設計遠程接口(Designing a Remote Interface Using AIDL)

由於每個應用程序都運行在自己的進程空間,並且可以從應用程序UI運行另一個服務進程,而且經常會在不同的進程間傳遞對象。在Android平台,一個進程通常不能訪問另一個進程的內存空間,所以要想對話,需要將對象分解成操作系統可以理解的基本單元,並且有序的通過進程邊界。

通過代碼來實現這個數據傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工作。

AIDL (Android Interface Definition Language)是一種IDL 語言,用於生成可以在Android設備上兩個進程之間進行進程間通信(IPC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。

AIDL IPC機制是面向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。

 

使用AIDL實現IPC(Implementing IPC Using AIDL)

使用AIDL實現IPC服務的步驟是:

1.         創建.aidl文件-該文件(YourInterface.aidl)定義了客戶端可用的方法和數據的接口。

2.         在makefile文件中加入.aidl文件-(Eclipse中的ADT插件提供管理功能)Android包括名為AIDL的編譯器,位於tools/文件夾。

3.         實現接口-AIDL編譯器從AIDL接口文件中利用Java語言創建接口,該接口有一個繼承的命名為Stub的內部抽象類(並且實現了一些IPC調用的附加方法),要做的就是創建一個繼承於YourInterface.Stub的類並且實現在.aidl文件中聲明的方法。

4.         向客戶端公開接口-如果是編寫服務,應該繼承Service並且重載Service.onBind(Intent) 以返回實現了接口的對象實例

 

創建.aidl文件(Create an .aidl File)

AIDL使用簡單的語法來聲明接口,描述其方法以及方法的參數和返回值。這些參數和返回值可以是任何類型,甚至是其他AIDL生成的接口。重要的是必須導入所有非內置類型,哪怕是這些類型是在與接口相同的包中。下面是AIDL能支持的數據類型:

l  Java編程語言的主要類型 (int, boolean等) — 不需要 import 語句。

l  以下的類 (不需要import 語句):

n  String

List -列表中的所有元素必須是在此列出的類型,包括其他AIDL生成的接口和可打包類型。List可以像一般的類(例如List<String>)那樣使用,另一邊接收的具體類一般是一個ArrayList,這些方法會使用List接口。

Map - Map中的所有元素必須是在此列出的類型,包括其他AIDL生成的接口和可打包類型。一般的maps(例如Map<String,Integer>)不被支持,另一邊接收的具體類一般是一個HashMap,這些方法會使用Map接口。

CharSequence -該類是被TextView和其他控件對象使用的字符序列。

l  通常引引用方式傳遞的其他AIDL生成的接口,必須要import 語句聲明

l  實現了Parcelable protocol 以及按值傳遞的自定義類,必須要import 語句聲明。


 

實現接口(Implementing the Interface)

         AIDL生成了與.aidl文件同名的接口,如果使用Eclipse插件,AIDL會做為編譯過程的一部分自動運行(不需要先運行AIDL再編譯項目),如果沒有插件,就要先運行AIDL。

         生成的接口包含一個名為Stub的抽象的內部類,該類聲明了所有.aidl中描述的方法,Stub還定義了少量的輔助方法,尤其是asInterface(),通過它或以獲得IBinder(當applicationContext.bindService()成功調用時傳遞到客戶端的onServiceConnected())並且返回用於調用IPC方法的接口實例

         要實現自己的接口,就從YourInterface.Stub類繼承,然後實現相關的方法(可以創建.aidl文件然後實現stub方法而不用在中間編譯,Android編譯過程會在.java文件之前處理.aidl文件)。

         這個例子實現了對IRemoteService接口的調用,這裡使用了匿名對象並且只有一個getPid()接口。


 

         這裡是實現接口的幾條說明:

l  不會有返回給調用方的異常

l  默認IPC調用是同步的。如果已知IPC服務端會花費很多毫秒才能完成,那就不要在Activity或View線程中調用,否則會引起應用程序掛起(Android可能會顯示“應用程序未響應”對話框),可以試著在獨立的線程中調用。

l  AIDL接口中只支持方法,不能聲明靜態成員。

 

向客戶端暴露接口(Exposing Your Interface to Clients)

         在完成了接口的實現後需要向客戶端暴露接口了,也就是發布服務,實現的方法是繼承 Service,然後實現以Service.onBind(Intent)返回一個實現了接口的類對象。下面的代碼片斷表示了暴露IRemoteService接口給客戶端的方式。


 
  1. public class RemoteService extends Service {  
  2.  
  3. ...  
  4.  
  5. @Override 
  6.  
  7.     public IBinder onBind(Intent intent) {  
  8.  
  9.         // Select the interface to return.  If your service only implements  
  10.  
  11.         // a single interface, you can just return it here without checking  
  12.  
  13.         // the Intent.  
  14.  
  15.         if (IRemoteService.class.getName().equals(intent.getAction())) {  
  16.  
  17.             return mBinder;  
  18.  
  19.         }  
  20.  
  21.         if (ISecondary.class.getName().equals(intent.getAction())) {  
  22.  
  23.             return mSecondaryBinder;  
  24.  
  25.         }  
  26.  
  27.         return null;  
  28.  
  29.     }  
  30.  
  31.    
  32.  
  33.     /**  
  34.  
  35.      * The IRemoteInterface is defined through IDL  
  36.  
  37.      */ 
  38.  
  39.     private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {  
  40.  
  41.         public void registerCallback(IRemoteServiceCallback cb) {  
  42.  
  43.             if (cb != null) mCallbacks.register(cb);  
  44.  
  45.         }  
  46.  
  47.         public void unregisterCallback(IRemoteServiceCallback cb) {  
  48.  
  49.             if (cb != null) mCallbacks.unregister(cb);  
  50.  
  51.         }  
  52.  
  53.     };  
  54.  
  55.    
  56.  
  57.     /**  
  58.  
  59.      * A secondary interface to the service.  
  60.  
  61.      */ 
  62.  
  63.     private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() {  
  64.  
  65.         public int getPid() {  
  66.  
  67.             return Process.myPid();  
  68.  
  69.         }  
  70.  
  71.         public void basicTypes(int anInt, long aLong, boolean aBoolean,  
  72.  
  73.                 float aFloat, double aDouble, String aString) {  
  74.  
  75.         }  
  76.  
  77.     };  
  78.  
  79.    
  80.  


 

 

使用可打包接口傳遞參數Pass by value Parameters using Parcelables

如果有類想要能過AIDL在進程之間傳遞,這一想法是可以實現的,必須確保這個類在IPC的兩端的有效性,通常的情形是與一個啟動的服務通信。

這裡列出了使類能夠支持Parcelable的4個步驟:【譯者注:原文為5,但列表為4項,疑為作者筆誤】

1.         使該類實現Parcelabel接口。

2.         實現public void writeToParcel(Parcel out) 方法,以便可以將對象的當前狀態寫入包裝對象中。

3.         增加名為CREATOR的構造器到類中,並實現Parcelable.Creator接口。

4.         最後,但同樣重要的是,創建AIDL文件聲明這個可打包的類(見下文),如果使用的是自定義的編譯過程,那麼不要編譯此AIDL文件,它像C語言的頭文件一樣不需要編譯。

AIDL會使用這些方法的成員序列化和反序列化對象。

這個例子演示了如何讓Rect類實現Parcelable接口。

  1. import android.os.Parcel;  
  2. import android.os.Parcelable;  
  3. public final class Rect implements Parcelable {  
  4. public int left;  
  5. public int top;  
  6. public int right;  
  7. public int bottom;  
  8. public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {  
  9. public Rect createFromParcel(Parcel in) {  
  10. return new Rect(in);  
  11. }  
  12. public Rect[] newArray(int size) {  
  13. return new Rect[size];  
  14. }  
  15. };  
  16. public Rect() {  
  17. }  
  18. private Rect(Parcel in) {  
  19. readFromParcel(in);  
  20. }  
  21. public void writeToParcel(Parcel out) {  
  22. out.writeInt(left);  
  23. out.writeInt(top);  
  24. out.writeInt(right);  
  25. out.writeInt(bottom);  
  26. }  
  27. public void readFromParcel(Parcel in) {  
  28. left = in.readInt();  
  29. top = in.readInt();  
  30. right = in.readInt();  
  31. bottom = in.readInt();  
  32. }  


 

         這個是Rect.aidl文件。

        

         序列化Rect類的工作相當簡單,對可打包的其他類型的數據可以參見Parcel類。


 

 

調用IPC方法(Calling an IPC Method)

         這裡給出了調用遠端接口的步驟:

1.         聲明.aidl文件中定義的接口類型的變量。

2.         實現ServiceConnection

3.         調用Context.bindService(),傳遞ServiceConnection的實現

4.         在ServiceConnection.onServiceConnected()方法中會接收到IBinder對象,調用YourInterfaceName.Stub.asInterface((IBinder)service)將返回值轉換為YourInterface類型

5.         調用接口中定義的方法。應該總是捕獲連接被打斷時拋出的DeadObjectException異常,這是遠端方法唯一的異常。

6.         調用Context.unbindService()斷開連接

這裡是幾個調用IPC服務的提示:

l  對象是在進程間進行引用計數

l  可以發送匿名對象作為方法參數

         以下是演示調用AIDL創建的服務,可以在ApiDemos項目中獲取遠程服務的示例。


 
  1. public static class Binding extends Activity {  
  2.  
  3.  
  4. /** The primary interface we will be calling on the service. */ 
  5.  
  6. IRemoteService mService = null;  
  7.  
  8. /** Another interface we use on the service. */ 
  9.  
  10. ISecondary mSecondaryService = null;  
  11.  
  12. Button mKillButton;  
  13.  
  14. TextView mCallbackText;  
  15.  
  16. private boolean mIsBound;  
  17.  
  18. /**  
  19.  
  20. * Standard initialization of this activity.  Set up the UI, then wait  
  21.  
  22. * for the user to poke it before doing anything.  
  23.  
  24. */ 
  25.  
  26. @Override 
  27.  
  28. protected void onCreate(Bundle savedInstanceState) {  
  29.  
  30.     super.onCreate(savedInstanceState);  
  31.  
  32.     setContentView(R.layout.remote_service_binding);  
  33.  
  34.     // Watch for button clicks.  
  35.  
  36.     Button button = (Button)findViewById(R.id.bind);  
  37.  
  38.     button.setOnClickListener(mBindListener);  
  39.  
  40.     button = (Button)findViewById(R.id.unbind);  
  41.  
  42.     button.setOnClickListener(mUnbindListener);  
  43.  
  44.     mKillButton = (Button)findViewById(R.id.kill);  
  45.  
  46.     mKillButton.setOnClickListener(mKillListener);  
  47.  
  48.     mKillButton.setEnabled(false);   
  49.  
  50.    
  51.  
  52.     mCallbackText = (TextView)findViewById(R.id.callback);  
  53.  
  54.    mCallbackText.setText("Not attached.");  
  55.  
  56. }  
  57.  
  58.    
  59.  
  60. /**  
  61.  
  62.   * Class for interacting with the main interface of the service.  
  63.  
  64.   */ 
  65.  
  66. private ServiceConnection mConnection = new ServiceConnection() {  
  67.  
  68.    public void onServiceConnected(ComponentName className, IBinder service) {  
  69.  
  70.         // This is called when the connection with the service has been  
  71.  
  72.         // established, giving us the service object we can use to  
  73.  
  74.         // interact with the service.  We are communicating with our  
  75.  
  76.         // service through an IDL interface, so get a client-side  
  77.  
  78.         // representation of that from the raw service object.  
  79.  
  80.         mService = IRemoteService.Stub.asInterface(service);  
  81.  
  82.         mKillButton.setEnabled(true);  
  83.  
  84.         mCallbackText.setText("Attached.");  
  85.  
  86.         // We want to monitor the service for as long as we are  
  87.  
  88.         // connected to it.  
  89.  
  90.         try {  
  91.  
  92.                 mService.registerCallback(mCallback);  
  93.  
  94.         } catch (RemoteException e) {  
  95.  
  96.             // In this case the service has crashed before we could even  
  97.  
  98.             // do anything with it; we can count on soon being  
  99.  
  100.             // disconnected (and then reconnected if it can be restarted)  
  101.  
  102.             // so there is no need to do anything here.  
  103.  
  104.         }  
  105.  
  106.    
  107.  
  108.        // As part of the sample, tell the user what happened.  
  109.  
  110.        Toast.makeText(Binding.this, R.string.remote_service_connected,  
  111.  
  112.              Toast.LENGTH_SHORT).show();  
  113.  
  114.     }  
  115.  
  116.    
  117.  
  118.     public void onServiceDisconnected(ComponentName className) {  
  119.  
  120.         // This is called when the connection with the service has been  
  121.  
  122.         // unexpectedly disconnected -- that is, its process crashed.  
  123.  
  124.         mService = null;  
  125.  
  126.         mKillButton.setEnabled(false);  
  127.  
  128.         mCallbackText.setText("Disconnected.");  
  129.  
  130.    
  131.  
  132.         // As part of the sample, tell the user what happened.  
  133.  
  134.         Toast.makeText(Binding.this, R.string.remote_service_disconnected,  
  135.  
  136.              Toast.LENGTH_SHORT).show();  
  137.  
  138.     }  
  139.  
  140. };  
  141.  
  142.    
  143.  
  144. /**  
  145.  
  146.   * Class for interacting with the secondary interface of the service.  
  147.  
  148.   */ 
  149.  
  150. private ServiceConnection mSecondaryConnection = new ServiceConnection() {  
  151.  
  152.    public void onServiceConnected(ComponentName className, IBinder service) {  
  153.  
  154.         // Connecting to a secondary interface is the same as any  
  155.  
  156.         // other interface.  
  157.  
  158.         mSecondaryService = ISecondary.Stub.asInterface(service);  
  159.  
  160.         mKillButton.setEnabled(true);  
  161.  
  162.     }  
  163.  
  164.    
  165.  
  166.     public void onServiceDisconnected(ComponentName className) {  
  167.  
  168.         mSecondaryService = null;  
  169.  
  170.         mKillButton.setEnabled(false);  
  171.  
  172.     }  
  173.  
  174. };  
  175.  
  176.    
  177.  
  178. private OnClickListener mBindListener = new OnClickListener() {  
  179.  
  180.    public void onClick(View v) {  
  181.  
  182.         // Establish a couple connections with the service, binding  
  183.  
  184.         // by interface names.  This allows other applications to be  
  185.  
  186.         // installed that replace the remote service by implementing  
  187.  
  188.         // the same interface.  
  189.  
  190.        bindService(new Intent(IRemoteService.class.getName()),  
  191.  
  192.               mConnection, Context.BIND_AUTO_CREATE);  
  193.  
  194.             bindService(new Intent(ISecondary.class.getName()),  
  195.  
  196.              mSecondaryConnection, Context.BIND_AUTO_CREATE);  
  197.  
  198.          mIsBound = true;  
  199.  
  200.          mCallbackText.setText("Binding.");  
  201.  
  202.     }  
  203.  
  204. };  
  205.  
  206.    
  207.  
  208. private OnClickListener mUnbindListener = new OnClickListener() {  
  209.  
  210.     public void onClick(View v) {  
  211.  
  212.        if (mIsBound) {  
  213.  
  214.             // If we have received the service, and hence registered with  
  215.  
  216.             // it, then now is the time to unregister.  
  217.  
  218.             if (mService != null) {  
  219.  
  220.                  try {  
  221.  
  222.                      mService.unregisterCallback(mCallback);  
  223.  
  224.                  } catch (RemoteException e) {  
  225.  
  226.                     // There is nothing special we need to do if the service  
  227.  
  228.                     // has crashed.  
  229.  
  230.                 }  
  231.  
  232.            }  
  233.  
  234.    
  235.  
  236.            // Detach our existing connection.  
  237.  
  238.             unbindService(mConnection);  
  239.  
  240.             unbindService(mSecondaryConnection);  
  241.  
  242.             mKillButton.setEnabled(false);  
  243.  
  244.             mIsBound = false;  
  245.  
  246.             mCallbackText.setText("Unbinding.");  
  247.  
  248.        }  
  249.  
  250.    }  
  251.  
  252. };  
  253.  
  254.    
  255.  
  256. private OnClickListener mKillListener = new OnClickListener() {  
  257.  
  258.     public void onClick(View v) {  
  259.  
  260.          // To kill the process hosting our service, we need to know its  
  261.  
  262.          // PID.  Conveniently our service has a call that will return  
  263.  
  264.          // to us that information.  
  265.  
  266.          if (mSecondaryService != null) {  
  267.  
  268.              try {  
  269.  
  270.                 int pid = mSecondaryService.getPid();  
  271.  
  272.                 // Note that, though this API allows us to request to  
  273.  
  274.                 // kill any process based on its PID, the kernel will  
  275.  
  276.                 // still impose standard restrictions on which PIDs you  
  277.  
  278.                 // are actually able to kill.  Typically this means only  
  279.  
  280.                 // the process running your application and any additional  
  281.  
  282.                 // processes created by that app as shown here; packages  
  283.  
  284.                 // sharing a common UID will also be able to kill each  
  285.  
  286.                 // other's processes.  
  287.  
  288.                 Process.killProcess(pid);  
  289.                     mCallbackText.setText("Killed service process.");  
  290.  
  291.           } catch (RemoteException ex) {  
  292.  
  293.                 // Recover gracefully from the process hosting the  
  294.  
  295.                 // server dying.  
  296.  
  297.                 // Just for purposes of the sample, put up a notification.  
  298.  
  299.                 Toast.makeText(Binding.this,  
  300.  
  301.                        R.string.remote_call_failed,  
  302.  
  303.                        Toast.LENGTH_SHORT).show();  
  304.  
  305.                 }  
  306.  
  307.             }  
  308.  
  309.         }  
  310.  
  311.     };  
  312.  
  313.    
  314.  
  315. // ----------------------------------------------------------------------  
  316.  
  317.  // Code showing how to deal with callbacks.  
  318.  
  319.  // ----------------------------------------------------------------------  
  320.  
  321.    
  322.  
  323. /**  
  324.  
  325.   * This implementation is used to receive callbacks from the remote  
  326.  
  327.   * service.  
  328.  
  329. */ 
  330.  
  331. private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {  
  332.  
  333.    /**  
  334.  
  335.      * This is called by the remote service regularly to tell us about  
  336.  
  337.      * new values.  Note that IPC calls are dispatched through a thread  
  338.  
  339.      * pool running in each process, so the code executing here will  
  340.  
  341.      * NOT be running in our main thread like most other things -- so,  
  342.  
  343.      * to update the UI, we need to use a Handler to hop over there.  
  344.  
  345.     */ 
  346.  
  347.    public void valueChanged(int value) {  
  348.  
  349.        mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));  
  350.  
  351.    }  
  352.  
  353. };  
  354.  
  355. private static final int BUMP_MSG = 1;  
  356.  
  357. private Handler mHandler = new Handler() {  
  358.  
  359.    @Override public void handleMessage(Message msg) {  
  360.  
  361.        switch (msg.what) {  
  362.  
  363.            case BUMP_MSG:  
  364.  
  365.                mCallbackText.setText("Received from service: " + msg.arg1);  
  366.  
  367.                break;  
  368.  
  369.            default:  
  370.  
  371.                super.handleMessage(msg);  
  372.  
  373.         }  
  374.  
  375.    }  
  376.  
  377. };  
  378.  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved