Android的IPC機制(三)——Binder連線池
綜述
前兩篇說到AIDL的使用方法,但是當我們的專案很大時,很多模組都需要用到Service,我們總不能為每一個模組都建立一個Service吧,這樣一來我們的應用就會顯得很笨重。那麼有沒有一種解決方案叫我們只需要建立一個Service,然後去管理AIDL呢?在任玉剛的《Android開發藝術探索》中給出了一個解決方案,那就是Binder連線池。在這裡我們看一下他是怎麼實現的。
Binder連線池的實現
在前面說到AIDL的使用及原理的時候,我們可以看到在服務端只是建立了一個Binder然後返回給客戶端使用而已。於是我們可以想到是不是我們可以只有一個Service,對於不同可客戶端我們只是去返回一個不同的Binder即可,這樣就避免了建立了大量的Service。在任玉剛的《Android開發藝術探索》給出了一個Binder連線池的概念,很巧妙的避免了Service的多次建立。這個Binder連線池類似於設計模式中的工廠方法模式。為每一個客戶端建立他們所需要的Binder物件。那麼下面我們看一下它是如何實現的。
首先我們建立一個名為BinderPool的AIDL介面。
// IBinderPool.aidl
package com.example.ljd_pc.binderpool;
// Declare any non-default types here with import statements
interface IBinderPool {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
IBinder queryBinder(int binderCode);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在這個AIDL介面中我們根據傳入的binderId返回了一個IBinder介面物件。我們再建立兩個AIDL介面作為功能顯示。
建立名為ICalculate的AIDL介面用作計算算術的加減法。
// ICalculate.aidl
package com.example.ljd_pc.binderpool;
// Declare any non-default types here with import statements
interface ICalculate {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int add(int first, int second);
int sub(int first, int second);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
再建立一個名為IRect作為求矩形的面積與周長。
// IRect.aidl
package com.example.ljd_pc.binderpool;
// Declare any non-default types here with import statements
interface IRect {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int area(int length,int width);
int perimeter(int length,int width);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
然後我們繼承系統自動生成的Stub類去實現介面中的方法。下面是ICalculate實現。
package com.example.ljd_pc.binderpool;
import android.os.RemoteException;
public class ICalculateImpl extends ICalculate.Stub {
@Override
public int add(int first, int second) throws RemoteException {
return first + second;
}
@Override
public int sub(int first, int second) throws RemoteException {
return first - second;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
IRect的實現。
package com.example.ljd_pc.binderpool;
import android.os.RemoteException;
public class IRectImpl extends IRect.Stub {
@Override
public int area(int length, int width) throws RemoteException {
return length * width;
}
@Override
public int perimeter(int length, int width) throws RemoteException {
return length*2 + width*2;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
然後我們在看一下這個BinderPool是怎麼實現的。
package com.example.ljd_pc.binderpool;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import java.util.concurrent.CountDownLatch;
public class BinderPool {
public static final int BINDER_NONE = -1;
public static final int BINDER_CALCULATE = 0;
public static final int BINDER_RECT = 1;
private Context mContext;
private static IBinderPool mBinderPool;
private static BinderPool mInstance;
private CountDownLatch mCountDownLatch;
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}
public static BinderPool getInstance(Context context){
if (mBinderPool == null){
synchronized (BinderPool.class){
if (mBinderPool == null){
mInstance = new BinderPool(context);
}
}
}
return mInstance;
}
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient,0);
} catch (RemoteException e) {
e.printStackTrace();
}
mCountDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient,0);
mBinderPool = null;
connectBinderPoolService();
}
};
private synchronized void connectBinderPoolService(){
mCountDownLatch = new CountDownLatch(1);
Intent service = new Intent("com.ljd.binder.BINDER_POOL_SERVICE");
mContext.bindService(service,mBinderPoolConnection,Context.BIND_AUTO_CREATE);
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public IBinder queryBinder(int binderCode){
IBinder binder = null;
if (mBinderPool != null){
try {
binder = mBinderPool.queryBinder(binderCode);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return binder;
}
public static class BinderPoolImpl extends IBinderPool.Stub {
public BinderPoolImpl(){
super();
}
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode){
case BINDER_CALCULATE:
binder = new ICalculateImpl();
break;
case BINDER_RECT:
binder = new IRectImpl();
break;
default:
break;
}
return binder;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
在這個BinderPool中我們首先為這兩個AIDL介面建立一個id。又建立了一個BinderPoolImpl的內部類,這個BinderPoolImpl內部類根據傳入的id返回相對應的Binder。在BinderPool中採用單例項模式,而在BinderPool的構造方法中繫結了Service。對客戶端提供了一個queryBinder方法來獲取所需要的Binder物件。而對於服務端只需要new BinderPool.BinderPoolImpl()即可。從這我們就可以看出這個BinderPool也就是對客戶端和服務端的程式碼進行了一次封裝。然後進行對不同的客戶端在Service中返回不同的Binder。由於AIDL支援多執行緒併發訪問的,所以在繫結Service中做了採用synchronized和CountDownLatch做了執行緒同步處理。
由於BinderPool對程式碼進行了封裝,所以服務端的程式碼就很簡單了,只需要new BinderPool.BinderPoolImpl()返回即可。
package com.example.ljd_pc.binderpool;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class BinderPoolService extends Service {
private Binder mBinderPool = new BinderPool.BinderPoolImpl();
public BinderPoolService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinderPool;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
下面在看一下客戶端程式碼。
package com.example.ljd_pc.binderpool;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import butterknife.Bind;
import butterknife.ButterKnife;
public class BinderPoolActivity extends AppCompatActivity implements View.OnClickListener{
@Bind(R.id.add_btn)
Button addBtn;
@Bind(R.id.sub_btn)
Button subBtn;
@Bind(R.id.area_btn)
Button areaBtn;
@Bind(R.id.per_btn)
Button perBtn;
private final String TAG = "BinderPoolActivity";
private BinderPool mBinderPool;
private ICalculate mCalculate;
private IRect mRect;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
addBtn.setOnClickListener(BinderPoolActivity.this);
subBtn.setOnClickListener(BinderPoolActivity.this);
areaBtn.setOnClickListener(BinderPoolActivity.this);
perBtn.setOnClickListener(BinderPoolActivity.this);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder_pool);
ButterKnife.bind(this);
getBinderPool();
}
@Override
protected void onDestroy() {
super.onDestroy();
ButterKnife.unbind(this);
}
private void getBinderPool(){
new Thread(new Runnable() {
@Override
public void run() {
mBinderPool = BinderPool.getInstance(BinderPoolActivity.this);
mHandler.obtainMessage().sendToTarget();
}
}).start();
}
@Override
public void onClick(final View v) {
try {
switch (v.getId()){
case R.id.add_btn:
mCalculate = ICalculateImpl.asInterface(mBinderPool.queryBinder(BinderPool.BINDER_CALCULATE));
Log.e(TAG,String.valueOf(mCalculate.add(3,2)));
Toast.makeText(BinderPoolActivity.this,String.valueOf(mCalculate.add(3,2)),Toast.LENGTH_SHORT).show();
break;
case R.id.sub_btn:
mCalculate = ICalculateImpl.asInterface(mBinderPool.queryBinder(BinderPool.BINDER_CALCULATE));
Log.e(TAG,String.valueOf(mCalculate.sub(3,2)));
Toast.makeText(BinderPoolActivity.this,String.valueOf(mCalculate.sub(3,2)),Toast.LENGTH_SHORT).show();
break;
case R.id.area_btn:
mRect = IRectImpl.asInterface(mBinderPool.queryBinder(BinderPool.BINDER_RECT));
Log.e(TAG,String.valueOf(mRect.area(3,2)));
Toast.makeText(BinderPoolActivity.this,String.valueOf(mRect.area(3,2)),Toast.LENGTH_SHORT).show();
break;
case R.id.per_btn:
mRect = IRectImpl.asInterface(mBinderPool.queryBinder(BinderPool.BINDER_RECT));
Log.e(TAG,String.valueOf(mRect.perimeter(3,2)));
Toast.makeText(BinderPoolActivity.this,String.valueOf(mRect.perimeter(3,2)),Toast.LENGTH_SHORT).show();
break;
default:
break;
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
在這裡我們要注意使用CountDownLatch對bindService這一非同步操作變為同步,所以在獲取BinderPool物件時不能再主執行緒中操作。並且在這一步中作者建議我們在Application中去初始化這個BinderPool物件。
佈局程式碼如下。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical">
<Button
android:id="@+id/add_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add"
/>
<Button
android:id="@+id/sub_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/sub"/>
<Button
android:id="@+id/area_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/area"/>
<Button
android:id="@+id/per_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/per"/>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
演示
總結
這樣以來我們就能在我們的應用中只建立一個Service就足夠了。當我們新增一個AIDL介面的時候只需要在BinderPool中新增一個id,然後根據這個id,在BinderPoolImpl中建立一個對應的Binder物件即可。這樣就很大程度上簡化了我們的工作。但是我認為這樣做依然存在一個問題。那就是當我們建立一個BinderPool物件時,我們的客戶端就已經繫結了Service,之後只是根據不同的id獲取不同的Binder。也就是說從我們繫結Service那時起,這個Service程式就一直在後臺執行,即使這個應用已經不再前臺使用。除非系統將這個Service程式殺死。但是總的來說,在我們的應用中若是用到了很多的AIDL,那麼使用Binder連線池還是很好的選擇。到這裡有關IPC的AIDL內容就說完了。
相關文章
- Android的IPC機制BinderAndroid
- Android IPC機制(三):淺談Binder的使用Android
- android binder ipcAndroid
- Android Binder IPC分析Android
- 理解 Android Binder 機制(三):Java層AndroidJava
- Binder通訊機制與IPC通訊.md
- Android中的IPC機制Android
- android-IPC/Binder/D-BUS(Binder/Messager/AIDL)程式間通訊(訊息機制)AndroidAI
- Android IPC 機制分析Android
- Android Binder機制淺析Android
- 圖解Android中的binder機制圖解Android
- Android程式間通訊(IPC)機制Binder簡要介紹和學習計劃Android
- Android進階(六)Binder機制Android
- android Binder機制深入淺出Android
- Android Binder機制文章轉載Android
- Binder機制
- 真香!為什麼Android要採用Binder作為IPC機制?全套教學資料Android
- Android的IPC機制(一)——AIDL的使用AndroidAI
- Android的IPC機制(六)—— BroadcastReceiver的使用AndroidAST
- Android的IPC機制(五)—— ContentProvider的使用AndroidIDE
- 理解 Android Binder 機制(二):C++層AndroidC++
- Android系統之Binder通訊機制Android
- 理解 Android Binder 機制(一):驅動篇Android
- Binder學習(二)Binder機制解析
- 02.Android之IPC機制問題Android
- 藉助 AIDL 理解 Android Binder 機制——Binder 來龍去脈AIAndroid
- 3分鐘帶你看懂android的Binder機制Android
- 詳解 Android 中的 IPC 機制:基礎篇Android
- Binder通訊機制
- 【Android原始碼】Binder機制和AIDL分析Android原始碼AI
- Android的IPC機制(七)—— Socket的原理簡析與使用Android
- Binder機制分析(1)——Binder結構簡介
- Android 系統原始碼-2:Binder 通訊機制Android原始碼
- Android程式間通訊–訊息機制及IPC機制實現薦Android
- Binder機制之AIDLAI
- Binder機制的細節補充
- Android的IPC機制(二)——AIDL實現原理簡析AndroidAI
- Android的IPC機制(四)—— Messenger的使用及原始碼分析AndroidMessenger原始碼