Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android aidl Binder框架淺析

Android aidl Binder框架淺析

編輯:關於Android編程

1、概述

Binder能干什麼?Binder可以提供系統中任何程序都可以訪問的全局服務。這個功能當然是任何系統都應該提供的,下面我們簡單看一下Android的Binder的框架

Android Binder框架分為服務器接口、Binder驅動、以及客戶端接口;簡單想一下,需要提供一個全局服務,那麼全局服務那端即是服務器接口,任何程序即客戶端接口,它們之間通過一個Binder驅動訪問。

服務器端接口:實際上是Binder類的對象,該對象一旦創建,內部則會啟動一個隱藏線程,會接收Binder驅動發送的消息,收到消息後,會執行Binder對象中的onTransact()函數,並按照該函數的參數執行不同的服務器端代碼。

Binder驅動:該對象也為Binder類的實例,客戶端通過該對象訪問遠程服務。

客戶端接口:獲得Binder驅動,調用其transact()發送消息至服務器

如果大家對上述不了解,沒關系,下面會通過例子來更好的說明,實踐是檢驗真理的唯一標准嘛

2、AIDL的使用

如果對Android比較熟悉,那麼一定使用過AIDL,如果你還不了解,那麼也沒關系,下面會使用一個例子展示AIDL的用法。

我們使用AIDL實現一個跨進程的加減法調用

1、服務端

新建一個項目,創建一個包名:com.zhy.calc.aidl,在包內創建一個ICalcAIDL文件:

 

[java]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1. packagecom.zhy.calc.aidl;
  2. interfaceICalcAIDL
  3. {
  4. intadd(intx,inty);
  5. intmin(intx,inty);
  6. }
注意,文件名為ICalcAIDL.aidl

 

然後在項目的gen目錄下會生成一個ICalcAIDL.java文件,暫時不貼這個文件的代碼了,後面會詳細說明

然後我們在項目中新建一個Service,代碼如下:

 

[java]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1. packagecom.example.zhy_binder;
  2.  
  3. importcom.zhy.calc.aidl.ICalcAIDL;
  4.  
  5. importandroid.app.Service;
  6. importandroid.content.Intent;
  7. importandroid.os.IBinder;
  8. importandroid.os.RemoteException;
  9. importandroid.util.Log;
  10.  
  11. publicclassCalcServiceextendsService
  12. {
  13. privatestaticfinalStringTAG="server";
  14.  
  15. publicvoidonCreate()
  16. {
  17. Log.e(TAG,"onCreate");
  18. }
  19.  
  20. publicIBinderonBind(Intentt)
  21. {
  22. Log.e(TAG,"onBind");
  23. returnmBinder;
  24. }
  25.  
  26. publicvoidonDestroy()
  27. {
  28. Log.e(TAG,"onDestroy");
  29. super.onDestroy();
  30. }
  31.  
  32. publicbooleanonUnbind(Intentintent)
  33. {
  34. Log.e(TAG,"onUnbind");
  35. returnsuper.onUnbind(intent);
  36. }
  37.  
  38. publicvoidonRebind(Intentintent)
  39. {
  40. Log.e(TAG,"onRebind");
  41. super.onRebind(intent);
  42. }
  43.  
  44. privatefinalICalcAIDL.StubmBinder=newICalcAIDL.Stub()
  45. {
  46.  
  47. @Override
  48. publicintadd(intx,inty)throwsRemoteException
  49. {
  50. returnx+y;
  51. }
  52.  
  53. @Override
  54. publicintmin(intx,inty)throwsRemoteException
  55. {
  56. returnx-y;
  57. }
  58.  
  59. };
  60.  
  61. }
在此Service中,使用生成的ICalcAIDL創建了一個mBinder的對象,並在Service的onBind方法中返回[html]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
這裡我們指定了一個name,因為我們一會會在別的應用程序中通過Intent來查找此Service;這個不需要Activity,所以我也就沒寫Activity,安裝完成也看不到安裝圖標,悄悄在後台運行著。[html]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.  
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:orientation="vertical">
  5.  
  6. android:layout_width="fill_parent"
  7. android:layout_height="wrap_content"
  8. android:onClick="bindService"
  9. android:text="BindService"/>
  10.  
  11. android:layout_width="fill_parent"
  12. android:layout_height="wrap_content"
  13. android:onClick="unbindService"
  14. android:text="UnbindService"/>
  15.  
  16. android:layout_width="fill_parent"
  17. android:layout_height="wrap_content"
  18. android:onClick="addInvoked"
  19. android:text="12+12"/>
  20.  
  21. android:layout_width="fill_parent"
  22. android:layout_height="wrap_content"
  23. android:onClick="minInvoked"
  24. android:text="50-12"/>
  25.  

主Activity

[java]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. packagecom.example.zhy_binder_client;
  2.  
  3. importandroid.app.Activity;
  4. importandroid.content.ComponentName;
  5. importandroid.content.Context;
  6. importandroid.content.Intent;
  7. importandroid.content.ServiceConnection;
  8. importandroid.os.Bundle;
  9. importandroid.os.IBinder;
  10. importandroid.util.Log;
  11. importandroid.view.View;
  12. importandroid.widget.Toast;
  13.  
  14. importcom.zhy.calc.aidl.ICalcAIDL;
  15.  
  16. publicclassMainActivityextendsActivity
  17. {
  18. privateICalcAIDLmCalcAidl;
  19.  
  20. privateServiceConnectionmServiceConn=newServiceConnection()
  21. {
  22. @Override
  23. publicvoidonServiceDisconnected(ComponentNamename)
  24. {
  25. Log.e("client","onServiceDisconnected");
  26. mCalcAidl=null;
  27. }
  28.  
  29. @Override
  30. publicvoidonServiceConnected(ComponentNamename,IBinderservice)
  31. {
  32. Log.e("client","onServiceConnected");
  33. mCalcAidl=ICalcAIDL.Stub.asInterface(service);
  34. }
  35. };
  36.  
  37. @Override
  38. protectedvoidonCreate(BundlesavedInstanceState)
  39. {
  40. super.onCreate(savedInstanceState);
  41. setContentView(R.layout.activity_main);
  42.  
  43. }
  44.  
  45. /**
  46. *點擊BindService按鈕時調用
  47. *@paramview
  48. */
  49. publicvoidbindService(Viewview)
  50. {
  51. Intentintent=newIntent();
  52. intent.setAction("com.zhy.aidl.calc");
  53. bindService(intent,mServiceConn,Context.BIND_AUTO_CREATE);
  54. }
  55. /**
  56. *點擊unBindService按鈕時調用
  57. *@paramview
  58. */
  59. publicvoidunbindService(Viewview)
  60. {
  61. unbindService(mServiceConn);
  62. }
  63. /**
  64. *點擊12+12按鈕時調用
  65. *@paramview
  66. */
  67. publicvoidaddInvoked(Viewview)throwsException
  68. {
  69.  
  70. if(mCalcAidl!=null)
  71. {
  72. intaddRes=mCalcAidl.add(12,12);
  73. Toast.makeText(this,addRes+"",Toast.LENGTH_SHORT).show();
  74. }else
  75. {
  76. Toast.makeText(this,"服務器被異常殺死,請重新綁定服務端",Toast.LENGTH_SHORT)
  77. .show();
  78.  
  79. }
  80.  
  81. }
  82. /**
  83. *點擊50-12按鈕時調用
  84. *@paramview
  85. */
  86. publicvoidminInvoked(Viewview)throwsException
  87. {
  88.  
  89. if(mCalcAidl!=null)
  90. {
  91. intaddRes=mCalcAidl.min(58,12);
  92. Toast.makeText(this,addRes+"",Toast.LENGTH_SHORT).show();
  93. }else
  94. {
  95. Toast.makeText(this,"服務端未綁定或被異常殺死,請重新綁定服務端",Toast.LENGTH_SHORT)
  96. .show();
  97.  
  98. }
  99.  
  100. }
  101.  
  102. }

 

很標准的綁定服務的代碼。

直接看運行結果:

\

我們首先點擊BindService按鈕,查看log

 

[html]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1. 08-0922:56:38.959:E/server(29692):onCreate
  2. 08-0922:56:38.959:E/server(29692):onBind
  3. 08-0922:56:38.959:E/client(29477):onServiceConnected
可以看到,點擊BindService之後,服務端執行了onCreate和onBind的方法,並且客戶端執行了onServiceConnected方法,標明服務器與客戶端已經聯通

[html]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. 08-0922:59:25.567:E/server(29692):onUnbind
  2. 08-0922:59:25.567:E/server(29692):onDestroy
  3.  
由於我們當前只有一個客戶端綁定了此Service,所以Service調用了onUnbind和onDestory

 

然後我們繼續點擊12+12,50-12,通過上圖可以看到,依然可以正確執行,也就是說即使onUnbind被調用,連接也是不會斷開的,那麼什麼時候會端口呢?

即當服務端被異常終止的時候,比如我們現在在手機的正在執行的程序中找到該服務:

\

點擊停止,此時查看log

 

[html]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1. 08-0923:04:21.433:E/client(30146):onServiceDisconnected
可以看到調用了onServiceDisconnected方法,此時連接被斷開,現在點擊12+12,50-12的按鈕,則會彈出Toast服務端斷開的提示。[java]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. privatefinalICalcAIDL.StubmBinder=newICalcAIDL.Stub()
  2. {
  3.  
  4. @Override
  5. publicintadd(intx,inty)throwsRemoteException
  6. {
  7. returnx+y;
  8. }
  9.  
  10. @Override
  11. publicintmin(intx,inty)throwsRemoteException
  12. {
  13. returnx-y;
  14. }
  15.  
  16. };
ICalcAILD.Stub來執行的,讓我們來看看Stub這個類的聲明:
publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.zhy.calc.aidl.ICalcAIDL

清楚的看到這個類是Binder的子類,是不是符合我們文章開通所說的服務端其實是一個Binder類的實例

[java]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @OverridepublicbooleanonTransact(intcode,android.os.Parceldata,android.os.Parcelreply,intflags)throwsandroid.os.RemoteException
  2. {
  3. switch(code)
  4. {
  5. caseINTERFACE_TRANSACTION:
  6. {
  7. reply.writeString(DESCRIPTOR);
  8. returntrue;
  9. }
  10. caseTRANSACTION_add:
  11. {
  12. data.enforceInterface(DESCRIPTOR);
  13. int_arg0;
  14. _arg0=data.readInt();
  15. int_arg1;
  16. _arg1=data.readInt();
  17. int_result=this.add(_arg0,_arg1);
  18. reply.writeNoException();
  19. reply.writeInt(_result);
  20. returntrue;
  21. }
  22. caseTRANSACTION_min:
  23. {
  24. data.enforceInterface(DESCRIPTOR);
  25. int_arg0;
  26. _arg0=data.readInt();
  27. int_arg1;
  28. _arg1=data.readInt();
  29. int_result=this.min(_arg0,_arg1);
  30. reply.writeNoException();
  31. reply.writeInt(_result);
  32. returntrue;
  33. }
  34. }
  35. returnsuper.onTransact(code,data,reply,flags);
  36. }

文章開頭也說到服務端的Binder實例會根據客戶端依靠Binder驅動發來的消息,執行onTransact方法,然後由其參數決定執行服務端的代碼。

 

可以看到onTransact有四個參數

code , data ,replay , flags

code 是一個整形的唯一標識,用於區分執行哪個方法,客戶端會傳遞此參數,告訴服務端執行哪個方法

data客戶端傳遞過來的參數

replay服務器返回回去的值

flags標明是否有返回值,0為有(雙向),1為沒有(單向)

我們仔細看caseTRANSACTION_min中的代碼

data.enforceInterface(DESCRIPTOR);與客戶端的writeInterfaceToken對用,標識遠程服務的名稱

int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();

接下來分別讀取了客戶端傳入的兩個參數

int _result = this.min(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);

然後執行this.min,即我們實現的min方法;返回result由reply寫回。

add同理,可以看到服務端通過AIDL生成Stub的類,封裝了服務端本來需要寫的代碼。

2、客戶端

客戶端主要通過ServiceConnected與服務端連接

 

[java]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1. privateServiceConnectionmServiceConn=newServiceConnection()
  2. {
  3. @Override
  4. publicvoidonServiceDisconnected(ComponentNamename)
  5. {
  6. Log.e("client","onServiceDisconnected");
  7. mCalcAidl=null;
  8. }
  9.  
  10. @Override
  11. publicvoidonServiceConnected(ComponentNamename,IBinderservice)
  12. {
  13. Log.e("client","onServiceConnected");
  14. mCalcAidl=ICalcAIDL.Stub.asInterface(service);
  15. }
  16. };

如果你比較敏銳,應該會猜到這個onServiceConnected中的IBinder實例,其實就是我們文章開通所說的Binder驅動,也是一個Binder實例

 

在ICalcAIDL.Stub.asInterface中最終調用了:

 

[java]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1. returnnewcom.zhy.calc.aidl.ICalcAIDL.Stub.Proxy(obj);

這個Proxy實例傳入了我們的Binder驅動,並且封裝了我們調用服務端的代碼,文章開頭說,客戶端會通過Binder驅動的transact()方法調用服務端代碼[java]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @Overridepublicintadd(intx,inty)throwsandroid.os.RemoteException
  2. {
  3. android.os.Parcel_data=android.os.Parcel.obtain();
  4. android.os.Parcel_reply=android.os.Parcel.obtain();
  5. int_result;
  6. try{
  7. _data.writeInterfaceToken(DESCRIPTOR);
  8. _data.writeInt(x);
  9. _data.writeInt(y);
  10. mRemote.transact(Stub.TRANSACTION_add,_data,_reply,0);
  11. _reply.readException();
  12. _result=_reply.readInt();
  13. }
  14. finally{
  15. _reply.recycle();
  16. _data.recycle();
  17. }
  18. return_result;
  19. }

首先聲明兩個Parcel對象,一個用於傳遞數據,一個用戶接收返回的數據[java]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. packagecom.example.zhy_binder;
  2.  
  3. importandroid.app.Service;
  4. importandroid.content.Intent;
  5. importandroid.os.Binder;
  6. importandroid.os.IBinder;
  7. importandroid.os.Parcel;
  8. importandroid.os.RemoteException;
  9. importandroid.util.Log;
  10.  
  11. publicclassCalcPlusServiceextendsService
  12. {
  13. privatestaticfinalStringDESCRIPTOR="CalcPlusService";
  14. privatestaticfinalStringTAG="CalcPlusService";
  15.  
  16. publicvoidonCreate()
  17. {
  18. Log.e(TAG,"onCreate");
  19. }
  20.  
  21. @Override
  22. publicintonStartCommand(Intentintent,intflags,intstartId)
  23. {
  24. Log.e(TAG,"onStartCommand");
  25. returnsuper.onStartCommand(intent,flags,startId);
  26. }
  27.  
  28. publicIBinderonBind(Intentt)
  29. {
  30. Log.e(TAG,"onBind");
  31. returnmBinder;
  32. }
  33.  
  34. publicvoidonDestroy()
  35. {
  36. Log.e(TAG,"onDestroy");
  37. super.onDestroy();
  38. }
  39.  
  40. publicbooleanonUnbind(Intentintent)
  41. {
  42. Log.e(TAG,"onUnbind");
  43. returnsuper.onUnbind(intent);
  44. }
  45.  
  46. publicvoidonRebind(Intentintent)
  47. {
  48. Log.e(TAG,"onRebind");
  49. super.onRebind(intent);
  50. }
  51.  
  52. privateMyBindermBinder=newMyBinder();
  53.  
  54. privateclassMyBinderextendsBinder
  55. {
  56. @Override
  57. protectedbooleanonTransact(intcode,Parceldata,Parcelreply,
  58. intflags)throwsRemoteException
  59. {
  60. switch(code)
  61. {
  62. case0x110:
  63. {
  64. data.enforceInterface(DESCRIPTOR);
  65. int_arg0;
  66. _arg0=data.readInt();
  67. int_arg1;
  68. _arg1=data.readInt();
  69. int_result=_arg0*_arg1;
  70. reply.writeNoException();
  71. reply.writeInt(_result);
  72. returntrue;
  73. }
  74. case0x111:
  75. {
  76. data.enforceInterface(DESCRIPTOR);
  77. int_arg0;
  78. _arg0=data.readInt();
  79. int_arg1;
  80. _arg1=data.readInt();
  81. int_result=_arg0/_arg1;
  82. reply.writeNoException();
  83. reply.writeInt(_result);
  84. returntrue;
  85. }
  86. }
  87. returnsuper.onTransact(code,data,reply,flags);
  88. }
  89.  
  90. };
  91.  
  92. }

我們自己實現服務端,所以我們自定義了一個Binder子類,然後復寫了其onTransact方法,我們指定服務的標識為CalcPlusService,然後0x110為乘,0x111為除;

 

記得在AndroidMenifest中注冊

 

[html]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1.  
  2.  
  3.  
  4.  

服務端代碼結束。

 

2、客戶端代碼

單獨新建了一個項目,代碼和上例很類似

首先布局文件:

 

[html]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1.  
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:orientation="vertical">
  5.  
  6. android:layout_width="fill_parent"
  7. android:layout_height="wrap_content"
  8. android:onClick="bindService"
  9. android:text="BindService"/>
  10.  
  11. android:layout_width="fill_parent"
  12. android:layout_height="wrap_content"
  13. android:onClick="unbindService"
  14. android:text="UnbindService"/>
  15.  
  16. android:layout_width="fill_parent"
  17. android:layout_height="wrap_content"
  18. android:onClick="mulInvoked"
  19. android:text="50*12"/>
  20.  
  21. android:layout_width="fill_parent"
  22. android:layout_height="wrap_content"
  23. android:onClick="divInvoked"
  24. android:text="36/12"/>
  25.  

可以看到加入了乘和除[java]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. packagecom.example.zhy_binder_client03;
  2.  
  3. importandroid.app.Activity;
  4. importandroid.content.ComponentName;
  5. importandroid.content.Context;
  6. importandroid.content.Intent;
  7. importandroid.content.ServiceConnection;
  8. importandroid.os.Bundle;
  9. importandroid.os.IBinder;
  10. importandroid.os.RemoteException;
  11. importandroid.util.Log;
  12. importandroid.view.View;
  13. importandroid.widget.Toast;
  14.  
  15. publicclassMainActivityextendsActivity
  16. {
  17.  
  18. privateIBindermPlusBinder;
  19. privateServiceConnectionmServiceConnPlus=newServiceConnection()
  20. {
  21. @Override
  22. publicvoidonServiceDisconnected(ComponentNamename)
  23. {
  24. Log.e("client","mServiceConnPlusonServiceDisconnected");
  25. }
  26.  
  27. @Override
  28. publicvoidonServiceConnected(ComponentNamename,IBinderservice)
  29. {
  30.  
  31. Log.e("client","mServiceConnPlusonServiceConnected");
  32. mPlusBinder=service;
  33. }
  34. };
  35.  
  36. @Override
  37. protectedvoidonCreate(BundlesavedInstanceState)
  38. {
  39. super.onCreate(savedInstanceState);
  40. setContentView(R.layout.activity_main);
  41.  
  42. }
  43.  
  44. publicvoidbindService(Viewview)
  45. {
  46. IntentintentPlus=newIntent();
  47. intentPlus.setAction("com.zhy.aidl.calcplus");
  48. booleanplus=bindService(intentPlus,mServiceConnPlus,
  49. Context.BIND_AUTO_CREATE);
  50. Log.e("plus",plus+"");
  51. }
  52.  
  53. publicvoidunbindService(Viewview)
  54. {
  55. unbindService(mServiceConnPlus);
  56. }
  57.  
  58. publicvoidmulInvoked(Viewview)
  59. {
  60.  
  61. if(mPlusBinder==null)
  62. {
  63. Toast.makeText(this,"未連接服務端或服務端被異常殺死",Toast.LENGTH_SHORT).show();
  64. }else
  65. {
  66. android.os.Parcel_data=android.os.Parcel.obtain();
  67. android.os.Parcel_reply=android.os.Parcel.obtain();
  68. int_result;
  69. try
  70. {
  71. _data.writeInterfaceToken("CalcPlusService");
  72. _data.writeInt(50);
  73. _data.writeInt(12);
  74. mPlusBinder.transact(0x110,_data,_reply,0);
  75. _reply.readException();
  76. _result=_reply.readInt();
  77. Toast.makeText(this,_result+"",Toast.LENGTH_SHORT).show();
  78.  
  79. }catch(RemoteExceptione)
  80. {
  81. e.printStackTrace();
  82. }finally
  83. {
  84. _reply.recycle();
  85. _data.recycle();
  86. }
  87. }
  88.  
  89. }
  90.  
  91. publicvoiddivInvoked(Viewview)
  92. {
  93.  
  94. if(mPlusBinder==null)
  95. {
  96. Toast.makeText(this,"未連接服務端或服務端被異常殺死",Toast.LENGTH_SHORT).show();
  97. }else
  98. {
  99. android.os.Parcel_data=android.os.Parcel.obtain();
  100. android.os.Parcel_reply=android.os.Parcel.obtain();
  101. int_result;
  102. try
  103. {
  104. _data.writeInterfaceToken("CalcPlusService");
  105. _data.writeInt(36);
  106. _data.writeInt(12);
  107. mPlusBinder.transact(0x111,_data,_reply,0);
  108. _reply.readException();
  109. _result=_reply.readInt();
  110. Toast.makeText(this,_result+"",Toast.LENGTH_SHORT).show();
  111.  
  112. }catch(RemoteExceptione)
  113. {
  114. e.printStackTrace();
  115. }finally
  116. {
  117. _reply.recycle();
  118. _data.recycle();
  119. }
  120. }
  121.  
  122. }
  123. }

為了明了,我直接在mulInvoked裡面寫了代碼,和服務端都沒有抽象出一個接口。首先綁定服務時,通過onServiceConnected得到Binder驅動即mPlusBinder。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved