Android aidl Binder框架淺析

一、概述

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

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

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

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

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

若是你們對上述不瞭解,不要緊,下面會經過例子來更好的說明,實踐是檢驗真理的惟一標準嘛ide

二、AIDL的使用

若是對Android比較熟悉,那麼必定使用過AIDL,若是你還不瞭解,那麼也不要緊,下面會使用一個例子展現AIDL的用法。函數

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

一、服務端

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

 

 
  1. package com.zhy.calc.aidl;this

  2. interface ICalcAIDL

  3. {

  4. int add(int x , int y);

  5. int min(int x , int y );

  6. }


注意,文件名爲ICalcAIDL.aidl

 

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

而後咱們在項目中新建一個Service,代碼以下:

 

 
  1. package com.example.zhy_binder;

  2.  
  3. import com.zhy.calc.aidl.ICalcAIDL;

  4.  
  5. import android.app.Service;

  6. import android.content.Intent;

  7. import android.os.IBinder;

  8. import android.os.RemoteException;

  9. import android.util.Log;

  10.  
  11. public class CalcService extends Service

  12. {

  13. private static final String TAG = "server";

  14.  
  15. public void onCreate()

  16. {

  17. Log.e(TAG, "onCreate");

  18. }

  19.  
  20. public IBinder onBind(Intent t)

  21. {

  22. Log.e(TAG, "onBind");

  23. return mBinder;

  24. }

  25.  
  26. public void onDestroy()

  27. {

  28. Log.e(TAG, "onDestroy");

  29. super.onDestroy();

  30. }

  31.  
  32. public boolean onUnbind(Intent intent)

  33. {

  34. Log.e(TAG, "onUnbind");

  35. return super.onUnbind(intent);

  36. }

  37.  
  38. public void onRebind(Intent intent)

  39. {

  40. Log.e(TAG, "onRebind");

  41. super.onRebind(intent);

  42. }

  43.  
  44. private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub()

  45. {

  46.  
  47. @Override

  48. public int add(int x, int y) throws RemoteException

  49. {

  50. return x + y;

  51. }

  52.  
  53. @Override

  54. public int min(int x, int y) throws RemoteException

  55. {

  56. return x - y;

  57. }

  58.  
  59. };

  60.  
  61. }

在此Service中,使用生成的ICalcAIDL建立了一個mBinder的對象,並在Service的onBind方法中返回

 

最後記得在AndroidManifest中註冊

 

 
  1. <service android:name="com.example.zhy_binder.CalcService" >

  2. <intent-filter>

  3. <action android:name="com.zhy.aidl.calc" />

  4.  
  5. <category android:name="android.intent.category.DEFAULT" />

  6. </intent-filter>

  7. </service>


這裏咱們指定了一個name,由於咱們一會會在別的應用程序中經過Intent來查找此Service;這個不須要Activity,因此我也就沒寫Activity,安裝完成也看不到安裝圖標,悄悄在後臺運行着。

 

到此,服務端編寫完畢。下面開始編寫客戶端

二、客戶端

客戶端的代碼比較簡單,建立一個佈局,裏面包含4個按鈕,分別爲綁定服務,解除綁定,調用加法,調用減法

佈局文件:

 

 
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  2. xmlns:tools="http://schemas.android.com/tools"

  3. android:layout_width="match_parent"

  4. android:layout_height="match_parent"

  5. android:orientation="vertical" >

  6.  
  7. <Button

  8. android:layout_width="fill_parent"

  9. android:layout_height="wrap_content"

  10. android:onClick="bindService"

  11. android:text="BindService" />

  12.  
  13. <Button

  14. android:layout_width="fill_parent"

  15. android:layout_height="wrap_content"

  16. android:onClick="unbindService"

  17. android:text="UnbindService" />

  18.  
  19. <Button

  20. android:layout_width="fill_parent"

  21. android:layout_height="wrap_content"

  22. android:onClick="addInvoked"

  23. android:text="12+12" />

  24.  
  25. <Button

  26. android:layout_width="fill_parent"

  27. android:layout_height="wrap_content"

  28. android:onClick="minInvoked"

  29. android:text="50-12" />

  30.  
  31. </LinearLayout>


主Activity

 

 

 
  1. package com.example.zhy_binder_client;

  2.  
  3. import android.app.Activity;

  4. import android.content.ComponentName;

  5. import android.content.Context;

  6. import android.content.Intent;

  7. import android.content.ServiceConnection;

  8. import android.os.Bundle;

  9. import android.os.IBinder;

  10. import android.util.Log;

  11. import android.view.View;

  12. import android.widget.Toast;

  13.  
  14. import com.zhy.calc.aidl.ICalcAIDL;

  15.  
  16. public class MainActivity extends Activity

  17. {

  18. private ICalcAIDL mCalcAidl;

  19.  
  20. private ServiceConnection mServiceConn = new ServiceConnection()

  21. {

  22. @Override

  23. public void onServiceDisconnected(ComponentName name)

  24. {

  25. Log.e("client", "onServiceDisconnected");

  26. mCalcAidl = null;

  27. }

  28.  
  29. @Override

  30. public void onServiceConnected(ComponentName name, IBinder service)

  31. {

  32. Log.e("client", "onServiceConnected");

  33. mCalcAidl = ICalcAIDL.Stub.asInterface(service);

  34. }

  35. };

  36.  
  37. @Override

  38. protected void onCreate(Bundle savedInstanceState)

  39. {

  40. super.onCreate(savedInstanceState);

  41. setContentView(R.layout.activity_main);

  42.  
  43. }

  44.  
  45. /**

  46. * 點擊BindService按鈕時調用

  47. * @param view

  48. */

  49. public void bindService(View view)

  50. {

  51. Intent intent = new Intent();

  52. intent.setAction("com.zhy.aidl.calc");

  53. bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);

  54. }

  55. /**

  56. * 點擊unBindService按鈕時調用

  57. * @param view

  58. */

  59. public void unbindService(View view)

  60. {

  61. unbindService(mServiceConn);

  62. }

  63. /**

  64. * 點擊12+12按鈕時調用

  65. * @param view

  66. */

  67. public void addInvoked(View view) throws Exception

  68. {

  69.  
  70. if (mCalcAidl != null)

  71. {

  72. int addRes = 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. * @param view

  85. */

  86. public void minInvoked(View view) throws Exception

  87. {

  88.  
  89. if (mCalcAidl != null)

  90. {

  91. int addRes = 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

 

 
  1. 08-09 22:56:38.959: E/server(29692): onCreate

  2. 08-09 22:56:38.959: E/server(29692): onBind

  3. 08-09 22:56:38.959: E/client(29477): onServiceConnected

能夠看到,點擊BindService以後,服務端執行了onCreate和onBind的方法,而且客戶端執行了onServiceConnected方法,標明服務器與客戶端已經聯通

 

而後點擊12+12,50-12能夠成功的調用服務端的代碼並返回正確的結果

下面咱們再點擊unBindService

 

 
  1. 08-09 22:59:25.567: E/server(29692): onUnbind

  2. 08-09 22:59:25.567: E/server(29692): onDestroy

因爲咱們當前只有一個客戶端綁定了此Service,因此Service調用了onUnbind和onDestory

 

而後咱們繼續點擊12+12,50-12,經過上圖能夠看到,依然能夠正確執行,也就是說即便onUnbind被調用,鏈接也是不會斷開的,那麼何時會端口呢?

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

點擊中止,此時查看log

 

08-09 23:04:21.433: E/client(30146): onServiceDisconnected

能夠看到調用了onServiceDisconnected方法,此時鏈接被斷開,如今點擊12+12,50-12的按鈕,則會彈出Toast服務端斷開的提示。

 

說了這麼多,彷佛和Binder框架沒什麼關係,下面咱們來具體看一看AIDL爲何作了些什麼。

三、分析AIDL生成的代碼

 

一、服務端

先看服務端的代碼,能夠看到咱們服務端提供的服務是由

 

 
  1. private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub()

  2. {

  3.  
  4. @Override

  5. public int add(int x, int y) throws RemoteException

  6. {

  7. return x + y;

  8. }

  9.  
  10. @Override

  11. public int min(int x, int y) throws RemoteException

  12. {

  13. return x - y;

  14. }

  15.  
  16. };


ICalcAILD.Stub來執行的,讓咱們來看看Stub這個類的聲明:

 

 

 
  1. public static abstract class Stub extends android.os.Binder implements com.zhy.calc.aidl.ICalcAIDL

  2.  


清楚的看到這個類是Binder的子類,是否是符合咱們文章開通所說的服務端實際上是一個Binder類的實例

 

接下來看它的onTransact()方法:

 

 
  1. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException

  2. {

  3. switch (code)

  4. {

  5. case INTERFACE_TRANSACTION:

  6. {

  7. reply.writeString(DESCRIPTOR);

  8. return true;

  9. }

  10. case TRANSACTION_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. return true;

  21. }

  22. case TRANSACTION_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. return true;

  33. }

  34. }

  35. return super.onTransact(code, data, reply, flags);

  36. }


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

 

能夠看到onTransact有四個參數

code , data ,replay , flags

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

data客戶端傳遞過來的參數

replay服務器返回回去的值

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

咱們仔細看case TRANSACTION_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的類,封裝了服務端原本須要寫的代碼。

二、客戶端

客戶端主要經過ServiceConnected與服務端鏈接

 

 
  1. private ServiceConnection mServiceConn = new ServiceConnection()

  2. {

  3. @Override

  4. public void onServiceDisconnected(ComponentName name)

  5. {

  6. Log.e("client", "onServiceDisconnected");

  7. mCalcAidl = null;

  8. }

  9.  
  10. @Override

  11. public void onServiceConnected(ComponentName name, IBinder service)

  12. {

  13. Log.e("client", "onServiceConnected");

  14. mCalcAidl = ICalcAIDL.Stub.asInterface(service);

  15. }

  16. };


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

 

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

 

return new com.zhy.calc.aidl.ICalcAIDL.Stub.Proxy(obj);


這個Proxy實例傳入了咱們的Binder驅動,而且封裝了咱們調用服務端的代碼,文章開頭說,客戶端會經過Binder驅動的transact()方法調用服務端代碼

 

直接看Proxy中的add方法

 

 
  1. @Override public int add(int x, int y) throws android.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對象,一個用於傳遞數據,一個用戶接收返回的數據

 

_data.writeInterfaceToken(DESCRIPTOR);與服務器端的enforceInterfac對應

_data.writeInt(x);
_data.writeInt(y);寫入須要傳遞的參數

mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

終於看到了咱們的transact方法,第一個對應服務端的code,_data,_repay分別對應服務端的data,reply,0表示是雙向的

_reply.readException();
_result = _reply.readInt();

最後讀出咱們服務端返回的數據,而後return。能夠看到和服務端的onTransact基本是一行一行對應的。

到此,咱們已經經過AIDL生成的代碼解釋了Android Binder框架的工做原理。Service的做用其實就是爲咱們建立Binder驅動,即服務端與客戶端鏈接的橋樑。

AIDL其實經過咱們寫的aidl文件,幫助咱們生成了一個接口,一個Stub類用於服務端,一個Proxy類用於客戶端調用。那麼咱們是否能夠不經過寫AIDL來實現遠程的通訊呢?下面向你們展現如何徹底不依賴AIDL來實現客戶端與服務端的通訊。

四、不依賴AIDL實現程序間通信

 

一、服務端代碼

咱們新建一個CalcPlusService.java用於實現兩個數的乘和除

 

 
  1. package com.example.zhy_binder;

  2.  
  3. import android.app.Service;

  4. import android.content.Intent;

  5. import android.os.Binder;

  6. import android.os.IBinder;

  7. import android.os.Parcel;

  8. import android.os.RemoteException;

  9. import android.util.Log;

  10.  
  11. public class CalcPlusService extends Service

  12. {

  13. private static final String DESCRIPTOR = "CalcPlusService";

  14. private static final String TAG = "CalcPlusService";

  15.  
  16. public void onCreate()

  17. {

  18. Log.e(TAG, "onCreate");

  19. }

  20.  
  21. @Override

  22. public int onStartCommand(Intent intent, int flags, int startId)

  23. {

  24. Log.e(TAG, "onStartCommand");

  25. return super.onStartCommand(intent, flags, startId);

  26. }

  27.  
  28. public IBinder onBind(Intent t)

  29. {

  30. Log.e(TAG, "onBind");

  31. return mBinder;

  32. }

  33.  
  34. public void onDestroy()

  35. {

  36. Log.e(TAG, "onDestroy");

  37. super.onDestroy();

  38. }

  39.  
  40. public boolean onUnbind(Intent intent)

  41. {

  42. Log.e(TAG, "onUnbind");

  43. return super.onUnbind(intent);

  44. }

  45.  
  46. public void onRebind(Intent intent)

  47. {

  48. Log.e(TAG, "onRebind");

  49. super.onRebind(intent);

  50. }

  51.  
  52. private MyBinder mBinder = new MyBinder();

  53.  
  54. private class MyBinder extends Binder

  55. {

  56. @Override

  57. protected boolean onTransact(int code, Parcel data, Parcel reply,

  58. int flags) throws RemoteException

  59. {

  60. switch (code)

  61. {

  62. case 0x110:

  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. return true;

  73. }

  74. case 0x111:

  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. return true;

  85. }

  86. }

  87. return super.onTransact(code, data, reply, flags);

  88. }

  89.  
  90. };

  91.  
  92. }


咱們本身實現服務端,因此咱們自定義了一個Binder子類,而後複寫了其onTransact方法,咱們指定服務的標識爲CalcPlusService,而後0x110爲乘,0x111爲除;

 

記得在AndroidMenifest中註冊

 

 
  1. <service android:name="com.example.zhy_binder.CalcPlusService" >

  2. <intent-filter>

  3. <action android:name="com.zhy.aidl.calcplus" />

  4. <category android:name="android.intent.category.DEFAULT" />

  5. </intent-filter>

  6. </service>


服務端代碼結束。

 

二、客戶端代碼

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

首先佈局文件:

 

 
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  2. xmlns:tools="http://schemas.android.com/tools"

  3. android:layout_width="match_parent"

  4. android:layout_height="match_parent"

  5. android:orientation="vertical" >

  6.  
  7. <Button

  8. android:layout_width="fill_parent"

  9. android:layout_height="wrap_content"

  10. android:onClick="bindService"

  11. android:text="BindService" />

  12.  
  13. <Button

  14. android:layout_width="fill_parent"

  15. android:layout_height="wrap_content"

  16. android:onClick="unbindService"

  17. android:text="UnbindService" />

  18.  
  19. <Button

  20. android:layout_width="fill_parent"

  21. android:layout_height="wrap_content"

  22. android:onClick="mulInvoked"

  23. android:text="50*12" />

  24.  
  25. <Button

  26. android:layout_width="fill_parent"

  27. android:layout_height="wrap_content"

  28. android:onClick="divInvoked"

  29. android:text="36/12" />

  30.  
  31. </LinearLayout>


能夠看到加入了乘和除

 

而後是Activity的代碼

 

 
  1. package com.example.zhy_binder_client03;

  2.  
  3. import android.app.Activity;

  4. import android.content.ComponentName;

  5. import android.content.Context;

  6. import android.content.Intent;

  7. import android.content.ServiceConnection;

  8. import android.os.Bundle;

  9. import android.os.IBinder;

  10. import android.os.RemoteException;

  11. import android.util.Log;

  12. import android.view.View;

  13. import android.widget.Toast;

  14.  
  15. public class MainActivity extends Activity

  16. {

  17.  
  18. private IBinder mPlusBinder;

  19. private ServiceConnection mServiceConnPlus = new ServiceConnection()

  20. {

  21. @Override

  22. public void onServiceDisconnected(ComponentName name)

  23. {

  24. Log.e("client", "mServiceConnPlus onServiceDisconnected");

  25. }

  26.  
  27. @Override

  28. public void onServiceConnected(ComponentName name, IBinder service)

  29. {

  30.  
  31. Log.e("client", " mServiceConnPlus onServiceConnected");

  32. mPlusBinder = service;

  33. }

  34. };

  35.  
  36. @Override

  37. protected void onCreate(Bundle savedInstanceState)

  38. {

  39. super.onCreate(savedInstanceState);

  40. setContentView(R.layout.activity_main);

  41.  
  42. }

  43.  
  44. public void bindService(View view)

  45. {

  46. Intent intentPlus = new Intent();

  47. intentPlus.setAction("com.zhy.aidl.calcplus");

  48. boolean plus = bindService(intentPlus, mServiceConnPlus,

  49. Context.BIND_AUTO_CREATE);

  50. Log.e("plus", plus + "");

  51. }

  52.  
  53. public void unbindService(View view)

  54. {

  55. unbindService(mServiceConnPlus);

  56. }

  57.  
  58. public void mulInvoked(View view)

  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 (RemoteException e)

  80. {

  81. e.printStackTrace();

  82. } finally

  83. {

  84. _reply.recycle();

  85. _data.recycle();

  86. }

  87. }

  88.  
  89. }

  90.  
  91. public void divInvoked(View view)

  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 (RemoteException e)

  113. {

  114. e.printStackTrace();

  115. } finally

  116. {

  117. _reply.recycle();

  118. _data.recycle();

  119. }

  120. }

  121.  
  122. }

  123. }


爲了明瞭,我直接在mulInvoked裏面寫了代碼,和服務端都沒有抽象出一個接口。首先綁定服務時,經過onServiceConnected獲得Binder驅動即mPlusBinder;

 

而後準備數據,調用transact方法,經過code指定執行服務端哪一個方法,代碼和上面的分析一致。

下面看運行結果:

是否是很好的實現了咱們兩個應用程序間的通信,並無使用aidl文件,也從側面分析了咱們上述分析是正確的。

 

好了,就到這裏,相信你們看完這篇博文,對aidl和Binder的理解也會更加深入。

測試代碼點擊下載

代碼先安裝server端的代碼,而後再安裝client端的。。。

相關文章
相關標籤/搜索