学习基础和材料来源于《Android开发艺术探索》;本篇主要简单介绍一下在Android中实现IPC是Binder连接池的使用;原理性的东西介绍不多主要介绍一下使用场景和使用步骤;其实看懂下面两张图感觉就没有什么好说的;除了Binder连接池核心代码注意一下,其他的都是最基础的知识。
1,常见的服务配合AIDL的使用场景
如上图所示,服务端有n个aidl接口,每一个aidl接口对应一个服务,客户端有m个aidl接口,那么,如果想在客户端获取p个aidl接口,那么服务端就得创建对应的p个服务的实例,这是我们经常使用的情况,也没有什么问题,一个服务用来维护一个功能模块也很合理。
2,服务配合AIDL使用的特殊场景
如上图所示,现在服务端有n个aidl接口(对应于n个互不耦合的功能模块);有m个客户端,依次需要服务端的n个模块功能中p1,p2,...,pm个;如果按照第一种方式,每一个功能模块对应于一个服务,那么我们需要最多创建m*n个服务实例;这个数量很大,我先只想创建一个服务实例来管理所有的功能模块,下面就要使用到Binder连接池的技术了。
上图中,我也给出了Binder连接池的思想:
(1)构建一个管理所有其他功能模块(aidl接口)的管理模块(aidl接口),提供一个根据功能模块标识码获取对应aidl接口实例的功能函数
(2)在每一个客户端创建一个Binder连接池,在构造器中完成服务绑定工作,从而获取管理模块(aidl接口)实例,在自定义的方法传递服务端功能模块的标识码给管理模块的上述功能函数,即可获取对应模块功能标识码的接口实例;
听起来很抽象,其实真的很简单,下面就来说一下实现步骤:
服务端:
第一步:在服务端创建多个aidl文件,Build工程生成对应接口,
第二步:自定义对应数目的java类分别继承上述生成接口里面的静态内部类Stub,并实现在aidl中定义的接口方法
第三步:在定义一个管理上述aidl接口的aidl文件,Build一下工程,生成一个对应接口
第四步:创建一个服务,在里面创建一个内部类继承上述aidl接口中的Stub静态类,并实现里面的接口方法(主要就是根据功能模块标识码返回对应功能模块的aidl接口实例)
至此,服务端就完成了。
客户端:
第一步:根据功能需求,复制部分或者全部服务端的aidl文件至客户端工程并Build一下工程
第二步:复制服务端管理模块的aidl文件至客户端工程并Build一下工程
第三步:创建Binder连接池,实现里面的几个业务功能,特别注意里面定义的功能模块标识码一定要以服务端定义的为准
第四步:创建Binder连接池实例,调用里面的自定义函数并传入在服务端中定义的功能模块标识码即可获取相应功能模块的aidl接口实例
至此,客户端就完成了。
3,代码示例
如果还是感觉很模糊,我只能用一个很简单实例来演示一下了;这个示例包含一个服务端,两个客户端共三个应用,服务端中有两个功能模块,客户端1中两个功能模块都需要,客户端2中只需要其中一个功能模块,下面来看具体的代码:
服务端:
服务端代码结构如下:
IPayInterface.aidl和ITravelInterface.aidl文件内容如下:
// IPayInterface.aidl
package com.hfut.operationbinderpool;
// Declare any non-default types here with import statements
interface IPayInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
boolean pay(int money);
}
// ITravelInterface.aidl
package com.hfut.operationbinderpool;
// Declare any non-default types here with import statements
interface ITravelInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String toWhere(String destination);
}
IBinderPool.aidl文件内容如下:
// IBinderPool.aidl
package com.hfut.operationbinderpool;
// 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 getAidlInterface(int interfaceCode);
}
MyPayInterface.java代码:
import com.hfut.operationbinderpool.IPayInterface;
/**
* author:why
* created on: 2018/7/18 8:53
* description:
*/
public class MyPayInerface extends IPayInterface.Stub {
@Override
public boolean pay(int money) throws RemoteException {
if(money>100){
return true;
}
else{
return false;
}
}
}
MyTravelInterface.java代码:
package com.hfut.operationbinderpool.aidlInterface;
import android.os.RemoteException;
import com.hfut.operationbinderpool.ITravelInterface;
/**
* author:why
* created on: 2018/7/18 8:57
* description:
*/
public class MyTravelInterface extends ITravelInterface.Stub {
@Override
public String toWhere(String destination) throws RemoteException {
return "去"+destination+"请做车";
}
}
MyService.java代码:
package com.hfut.operationbinderpool;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import com.hfut.operationbinderpool.IBinderPool.Stub;
import com.hfut.operationbinderpool.aidlInterface.MyPayInerface;
import com.hfut.operationbinderpool.aidlInterface.MyTravelInterface;
/**
* @author why
* @date 2018-7-18 9:02:38
*/
public class MyService extends Service {
public static final int PAY_INTERFACE_CODE = 0;
public static final int TRAVEL_INTERFACE_CODE = 1;
public MyService() {
}
private Binder bindInterface = new IBinderPool.Stub() {
@Override
public IBinder getAidlInterface(int interfaceCode) throws RemoteException {
Binder binder = null;
switch (interfaceCode) {
case PAY_INTERFACE_CODE:
binder = new MyPayInerface();
break;
case TRAVEL_INTERFACE_CODE:
binder = new MyTravelInterface();
break;
default:
break;
}
return binder;
}
};
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return bindInterface;
}
}
MainActivity.java代码:
package com.hfut.operationbinderpool;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
/**
* @author why
* @date 2018-7-18 8:47:32
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
主配置AndroidManifest.xml文件代码:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hfut.operationbinderpool">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="hfut.com.why"></action>
</intent-filter>
</service>
</application>
</manifest>
客户端1:
客户端1代码结构如下:
其中功能模块和管理模块内容和服务端一模一样,这里不再添加;
BinderPool.java代码:
package com.hfut.operationbinderdown;
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 android.util.Log;
import com.hfut.operationbinderpool.IBinderPool;
import java.util.concurrent.CountDownLatch;
/**
* author:why
* created on: 2018/7/18 9:20
* description:
*/
public class BinderPool {
private static final String TAG = "BinderPool";
private static final String ACTION = "hfut.com.why";
private static final String PACKAGENAME = "com.hfut.operationbinderpool";
public static final int PAY_INTERFACE_CODE = 0;
public static final int TRAVEL_INTERFACE_CODE = 1;
private Context mContext;
private IBinderPool mBinderPool;
private static volatile BinderPool sInstance;
private CountDownLatch mConnectBinderPoolCountDownLatch;
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}
public static BinderPool getInsance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
private synchronized void connectBinderPoolService() {
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent();
service.setAction(ACTION);
service.setPackage(PACKAGENAME);
mContext.bindService(service, mBinderPoolConnection,
Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* query binder by binderCode from binder pool
*
* @param binderCode the unique token of binder
* @return binder who's token is binderCode<br>
* return null when not found or BinderPoolService died.
*/
public IBinder getAidlInterface(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.getAidlInterface(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// ignored.
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.w(TAG, "binder died.");
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
}
MainActivity.java代码:
package com.hfut.operationbinderdown;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.hfut.operationbinderpool.IBinderPool;
import com.hfut.operationbinderpool.IPayInterface;
import com.hfut.operationbinderpool.ITravelInterface;
/**
* @author why
* @date 2018-7-18 9:14:24
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
@Override
public void run() {
test();
}
}.start();
}
private void test() {
BinderPool pool=BinderPool.getInsance(this);
IBinder payBinderInterface= pool.getAidlInterface(BinderPool.PAY_INTERFACE_CODE);
IPayInterface payInterface=IPayInterface.Stub.asInterface(payBinderInterface);
try {
Log.d(TAG, "test: "+payInterface.pay(50));
} catch (RemoteException e) {
e.printStackTrace();
}
IBinder travelBinderInterface=pool.getAidlInterface(BinderPool.TRAVEL_INTERFACE_CODE);
ITravelInterface travelInterface=ITravelInterface.Stub.asInterface(travelBinderInterface);
try {
Log.d(TAG, "test: "+travelInterface.toWhere("上海"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
主配置AndroidManifest.xml文件内容:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hfut.operationbinderdown">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
客户端2:
客户端2代码结构:
其中功能模块和管理模块内容和服务端一模一样,BinderPool和客户端基本一样,除了包名:
BinderPool代码:
package com.hfut.binderpooltestdown1;
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 android.util.Log;
import com.hfut.operationbinderpool.IBinderPool;
import java.util.concurrent.CountDownLatch;
/**
* author:why
* created on: 2018/7/18 9:20
* description:
*/
public class BinderPool {
private static final String TAG = "BinderPool";
private static final String ACTION = "hfut.com.why";
private static final String PACKAGENAME = "com.hfut.operationbinderpool";
public static final int PAY_INTERFACE_CODE = 0;
public static final int TRAVEL_INTERFACE_CODE = 1;
private Context mContext;
private IBinderPool mBinderPool;
private static volatile BinderPool sInstance;
private CountDownLatch mConnectBinderPoolCountDownLatch;
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}
public static BinderPool getInsance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
private synchronized void connectBinderPoolService() {
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent();
service.setAction(ACTION);
service.setPackage(PACKAGENAME);
mContext.bindService(service, mBinderPoolConnection,
Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* query binder by binderCode from binder pool
*
* @param binderCode the unique token of binder
* @return binder who's token is binderCode<br>
* return null when not found or BinderPoolService died.
*/
public IBinder getAidlInterface(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.getAidlInterface(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// ignored.
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.w(TAG, "binder died.");
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
}
MainActivity.java代码:
package com.hfut.binderpooltestdown1;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.hfut.operationbinderpool.ITravelInterface;
/**
* @author why
* @date 2018-7-18 9:54:47
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
@Override
public void run() {
test();
}
}.start();
}
private void test() {
BinderPool pool=BinderPool.getInsance(this);
// IBinder payBinderInterface= pool.getAidlInterface(BinderPool.PAY_INTERFACE_CODE);
// IPayInterface payInterface=IPayInterface.Stub.asInterface(payBinderInterface);
// try {
// Log.d(TAG, "test: "+payInterface.pay(50));
// } catch (RemoteException e) {
// e.printStackTrace();
// }
IBinder travelBinderInterface=pool.getAidlInterface(BinderPool.TRAVEL_INTERFACE_CODE);
ITravelInterface travelInterface=ITravelInterface.Stub.asInterface(travelBinderInterface);
try {
Log.d(TAG, "test: "+travelInterface.toWhere("南京"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
主配置文件AndroidManifest.xml内容:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hfut.binderpooltestdown1">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
4,运行结果
运行服务端应用,运行客户端1应用,在运行客户端应用,查看日志如下: