Android bind service使用Binder 池的方法
一、aidl 服务端实现
1.创建aidl文件,创建单独的aidl仓库,在aidl仓库添加如下aidl文件 包括:
1.1 主aidl文件 IIpcService.aidl(IpcService.onBind直接返回IIpcService接口实现),提供Binder 池获取方法。
import android.os.ParcelFileDescriptor;
import com.android.demo.aidl.IIpcServiceListener;
// Declare any non-default types here with import statementsinterface IIpcService {/*** 根据binder代码查询对应的Binder对象* @param binderCode 服务标识码* @return 对应的Binder对象*/IBinder queryBinder(int binderCode);/*** 注册客户端监听器到服务端* @param packageName 客户端包名* @param ipcServiceListener 客户端监听器*/void register2Server(String packageName,IIpcServiceListener ipcServiceListener);/*** 从服务端注销客户端监听器* @param packageName 客户端包名*/void unregister2Server(String packageName);/*** 处理客户端请求并返回结果* @param packageName 客户端包名* @param clientRequest 客户端请求内容* @param pfd 用于传输大数据的文件描述符* @return 处理结果*/String processClientRequest(String packageName,String clientRequest,inout ParcelFileDescriptor pfd);/*** 处理客户端请求(单向异步且串行调用,无返回值)* @param packageName 客户端包名* @param clientRequest 客户端请求内容*/oneway void processClientRequestOneway(String packageName,String clientRequest);
1.2 客户端监听aidl文件 IIpcServiceListener.aidl,用于客户端向服务端注册监听。
interface IIpcServiceListener {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/
// void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
// double aDouble, String aString);String processServiceRequest(String serviceRequest);
}
1.3 Binder 池中模块1 aidl文件 IBinderModule1.aidl,处理模块1业务。
interface IBinderModule1 {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/
// void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
// double aDouble, String aString);String doWork(String serviceRequest);
}
1.4 Binder 池中模块2 aidl文件 IBinderModule2.aidl,处理模块2业务。
interface IBinderModule2 {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/
// void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
// double aDouble, String aString);String doWork(String serviceRequest);
}
2.创建service IpcService.java 及 服务端binder请求处理类LocalIpcRepositoryImpl。
IpcService.java 中创建了IIpcService.Stub实现类,该实现类queryBinder方法,基于客户端请求的binderCode,返回创建的对应的binder模块实现类。
public class IpcService extends BaseService {private static final String TAG = AppConstants.APP_TAG + "IpcService ";private NetBroadcastReceiver mNetBroadcastReceiver;HandlerThread mHandlerThread = new HandlerThread("IpcService");Handler mWorkHandler;@Overridepublic IBinder onBind(Intent intent) {return mIpcService;}@Overridepublic void onCreate() {super.onCreate();Log.d(TAG, "onCreate() ");//registerReceiver();}@Overridepublic void onDestroy() {super.onDestroy();Log.d(TAG, "onDestroy");//unregisterReceiver();}private IIpcService.Stub mIpcService = new IIpcService.Stub() {@Overridepublic IBinder queryBinder(int binderCode) throws RemoteException {IBinder binder = null;switch (binderCode) {case BINDER_MODULE1:binder = new BinderModule1Impl();break;case BINDER_MODULE2:binder = new BinderModule2Impl();break;default:Log.d(TAG, "queryBinder invalid binderCode=" + binderCode);break;}return binder;}@Overridepublic void register2Server(String packageName, IIpcServiceListener ipcServiceListener) throws RemoteException {Looper.prepare();LocalIpcRepositoryImpl.getInstance().register2Server(packageName, ipcServiceListener);}@Overridepublic void unregister2Server(String packageName) throws RemoteException {LocalIpcRepositoryImpl.getInstance().unregister2Server(packageName);}@Overridepublic String processClientRequest(String packageName, String clientRequest, ParcelFileDescriptor pfd) throws RemoteException {Log.d(TAG, "processClientRequest");return LocalIpcRepositoryImpl.getInstance().processClientRequest(packageName, clientRequest,pfd);}@Overridepublic void processClientRequestOneway(String packageName, String clientRequest) throws RemoteException {Log.d(TAG, "processClientRequestOneway");LocalIpcRepositoryImpl.getInstance().processClientRequestOneway(packageName, clientRequest);}};}
public class LocalIpcRepositoryImpl implements LocalIpcRepository {private static final String TAG = AppConstants.APP_TAG + "LocalIpcRepositoryImpl";private HashMap<String, IIpcServiceListener> mServiceListenerMap = new HashMap<>();private IUiShow mUiShow = null;private static volatile LocalIpcRepositoryImpl mInstance;private MutableLiveData<String> mTestLiveData1 = new MutableLiveData<>();private MutableLiveData<String> mTestLiveData2 = new MutableLiveData<>();//在创建一个聚合类MediatorLiveDataprivate MediatorLiveData<String> mMediatorLiveData = new MediatorLiveData<>();private Handler mHandler = new Handler();public LocalIpcRepositoryImpl() {//分别把LiveData合并到mediatorLiveData中mMediatorLiveData.addSource(mTestLiveData1, new Observer<String>() {@Overridepublic void onChanged(String testLiveData1Str) {Log.i(TAG, "mMediatorLiveData.onChanged mTestLiveData1 testLiveData1Str:" + testLiveData1Str);mMediatorLiveData.postValue(testLiveData1Str);}});mMediatorLiveData.addSource(mTestLiveData2, new Observer<String>() {@Overridepublic void onChanged(String testLiveData2Str) {Log.i(TAG, "mMediatorLiveData.onChanged mTestLiveData2 testLiveData2Str:" + testLiveData2Str);mMediatorLiveData.postValue(testLiveData2Str);}});}public static LocalIpcRepositoryImpl getInstance() {if (mInstance == null) {synchronized (LocalIpcRepositoryImpl.class) {if (mInstance == null) {mInstance = new LocalIpcRepositoryImpl();}}}return mInstance;}@Overridepublic void register2Server(String packageName, IIpcServiceListener iFtIpcServiceListener) {boolean containsKey = mServiceListenerMap.containsKey(packageName);Log.i(TAG, "register2Server packageName:" + packageName + " containsKey?" + containsKey);mServiceListenerMap.put(packageName, iFtIpcServiceListener);}@Overridepublic void unregister2Server(String packageName) {boolean containsKey = mServiceListenerMap.containsKey(packageName);Log.i(TAG, "unregister2Server packageName:" + packageName + " containsKey?" + containsKey);if (containsKey) {mServiceListenerMap.remove(packageName);}}public void registerUiShow(IUiShow iUiShow) {mUiShow = iUiShow;}/*** 处理客户端请求** @param packageName package name.* @param clientRequest response json from client.* @return*/@Overridepublic String processClientRequest(String packageName, String clientRequest, ParcelFileDescriptor pfd) {Log.i(TAG, "processClientRequest 11 packageName:" + packageName+ " clientRequest:" + clientRequest + " pfd:" + pfd);String ret = clientRequest;FileDescriptor fileDescriptor = pfd.getFileDescriptor();FileInputStream fis = null;try {fis = new FileInputStream(fileDescriptor);if ("text".equals(clientRequest)) {byte[] content = new byte[5];fis.read(content);Log.i(TAG, "processClientRequest 111 content:" + content);for (int i = 0; i < content.length; i++) {Log.i(TAG, "processClientRequest 113 content[" + i + "]=" + content[i]);}} else {Bitmap rawBitmap = BitmapFactory.decodeStream(fis);ret += " process success!";Log.i(TAG, "processClientRequest 222 rawBitmap ByteCount:" + rawBitmap.getByteCount() + " mUiShow:" + mUiShow);if (null != mUiShow) {mUiShow.showBitmap(rawBitmap);}}} catch (Exception e) {Log.i(TAG, "processClientRequest 33 error:" + e);e.printStackTrace();} finally {try {if (fis != null) {fis.close();}} catch (IOException e) {Log.i(TAG, "processClientRequest 44 error:" + e);}}Log.i(TAG, "processClientRequest 55 end ret:" + ret);return ret;}/*** 处理客户端请求** @param packageName package name.* @param clientRequest response json from client.* @return*/@Overridepublic boolean processClientRequestOneway(String packageName, String clientRequest) {Log.i(TAG, "processClientRequestOneway 11 packageName:" + packageName+ " clientRequest:" + clientRequest);try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}Log.i(TAG, "processClientRequestOneway 22 end clientRequest:" + clientRequest);return true;}@Overridepublic String callServiceClient(String serviceRequest) {//TODO base on requestJson to call dvr or cvbox IIpcServiceListener process request,if (null == mServiceListenerMap || mServiceListenerMap.size() == 0) {Log.i(TAG, "callServiceClient 00 mServiceListenerMap size is 0 return!");return null;}Log.i(TAG, "callServiceClient 11 mServiceListenerMap.size():"+ mServiceListenerMap.size());String clientResponse = null;Iterator it = mServiceListenerMap.entrySet().iterator();boolean dvrConnected = false;while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String packageName = (String) entry.getKey();IIpcServiceListener ftIpcServiceListener = (IIpcServiceListener) entry.getValue();Log.i(TAG, "callServiceClient * 22 packageName:" + packageName+ " ftIpcServiceListener" + ftIpcServiceListener);try {clientResponse = ftIpcServiceListener.processServiceRequest(serviceRequest);} catch (DeadObjectException e) {Log.i(TAG, "callServiceClient 33 call it.remove error:" + e);it.remove();e.printStackTrace();} catch (RemoteException e) {e.printStackTrace();}Log.i(TAG, "callServiceClient 44 mServiceListenerMap.size():"+ mServiceListenerMap.size());}return clientResponse;}public LiveData<String> getTestLiveData() {
// testDelaySendLiveData1(3);
// testDelaySendLiveData2(6);return mMediatorLiveData;}private void testDelaySendLiveData1(int second) {Log.i(TAG, "testDelaySendLiveData1 11 second:" + second);mHandler.postDelayed(() -> {Log.i(TAG, "testDelaySendLiveData1 22 call setValue second:" + second);mTestLiveData1.setValue("delay " + second + " seconds");}, 1000 * second);}private void testDelaySendLiveData2(int second) {Log.i(TAG, "testDelaySendLiveData2 11 second:" + second);mHandler.postDelayed(() -> {Log.i(TAG, "testDelaySendLiveData2 22 call setValue second:" + second);mTestLiveData2.setValue("delay " + second + " seconds");}, 1000 * second);}//模拟数据变化调用public void delaySetValue() {Log.i(TAG, "delaySetValue ");testDelaySendLiveData1(3);testDelaySendLiveData2(6);}}
3.创建服务端IBinderModule1.aidl实现类BinderModule1Impl和IBinderModule2.aidl实现类BinderModule2Impl
public class BinderModule1Impl extends IBinderModule1.Stub {private static final String TAG = "BinderModule1Impl ";@Overridepublic String doWork(String serviceRequest) throws RemoteException {Log.i(TAG, "doWork: " + "serviceRequest=" + serviceRequest);return "BinderModule1Impl.doWork";}
}
public class BinderModule2Impl extends IBinderModule2.Stub {private static final String TAG = "BinderModule2Impl ";@Overridepublic String doWork(String serviceRequest) throws RemoteException {Log.i(TAG, "doWork: " + "serviceRequest=" + serviceRequest);return "BinderModule2Impl.doWork";}
}
二、aidl 请求工具类
1.创建绑定服务请求封装类IpcManager.java 和绑定服务常量类IpcServiceConstant.java添加到 aidl仓库。
public class IpcManager {private static final String TAG = "IpcManager ";private static volatile IpcManager mInstance;private Context mContext;private String mPackageName;private IIpcService mIpcService;private IIpcServiceListener mIpcServiceListener;private ServiceConnectListener mServiceConnectListener;public static final int BINDER_MODULE1 = 1;public static final int BINDER_MODULE2 = 2;private IpcManager(Context context) {mContext = context;mPackageName = (null == mContext ? null : mContext.getPackageName());}/*** getInstance.** @param context context.* @return FileTransmissionManager.*/public static IpcManager getInstance(Context context) {if (null == mInstance) {synchronized (IpcManager.class) {if (null == mInstance) {mInstance = new IpcManager(context);}}}return mInstance;}/*** connectService.* should call this function first.** @param serviceConnectListener ServiceConnectListener.* @param iFtIpcServiceListener IIpcServiceListener.*/public void connectService(ServiceConnectListener serviceConnectListener,IIpcServiceListener iFtIpcServiceListener) {Log.i(TAG, "connectService: " + "mPackageName=" + mPackageName);mServiceConnectListener = serviceConnectListener;mIpcServiceListener = iFtIpcServiceListener;bindService();}/*** disconnectService.*/public void disconnectService() {Log.i(TAG, "disconnectService");unregister(mPackageName);unbindService();}/*** processClientRequest** @param clientRequest*/public String processClientRequest(String clientRequest, ParcelFileDescriptor pfd) {Log.i(TAG, "processClinentRequest 11 ** mPackageName:" + mPackageName+ " mFtIpcService:" + mIpcService + " clientRequest:" + clientRequest + " pfd:" + pfd);if (null != mIpcService) {try {return mIpcService.processClientRequest(mPackageName, clientRequest, pfd);} catch (RemoteException e) {Log.e(TAG, "processClinentRequest 22 e:" + e);e.printStackTrace();}}return "error";}/*** processClientRequest** @param clientRequest*/public void processClientRequestOneway(String clientRequest) {Log.i(TAG, "processClientRequestOneway 11 ** mPackageName:" + mPackageName+ " mFtIpcService:" + mIpcService + " clientRequest:" + clientRequest);if (null != mIpcService) {try {mIpcService.processClientRequestOneway(mPackageName, clientRequest);} catch (RemoteException e) {Log.e(TAG, "processClientRequestOneway 22 e:" + e);e.printStackTrace();}}}//region 客户端Binder池调用测试/*** 从服务端Binder池中获取binderCode对应的IBinder,并进行测试调用。** @param binderCode*/public String queryBinderAndTestCall(int binderCode) {String ret = "";IBinder binder = null;try {binder = mIpcService.queryBinder(binderCode);} catch (RemoteException e) {throw new RuntimeException(e);}if (null == binder) {Log.e(TAG, "queryBinderAndTestCall binder is null for binderCode:" + binderCode);return "null == binder";}switch (binderCode) {case BINDER_MODULE1:ret = testBinderModule1(binder);break;case BINDER_MODULE2:ret = testBinderModule2(binder);break;}return ret;}private String testBinderModule1(IBinder binder) {String result = "";IBinderModule1 module1 = IBinderModule1.Stub.asInterface(binder);try {result = module1.doWork("testBinderModule1");Log.i(TAG, "testBinderModule1 result:" + result);} catch (RemoteException e) {Log.e(TAG, "testBinderModule1 e:" + e);e.printStackTrace();}return result;}private String testBinderModule2(IBinder binder) {String result = "";IBinderModule2 module2 = IBinderModule2.Stub.asInterface(binder);try {result = module2.doWork("testBinderModule2");Log.i(TAG, "testBinderModule2 result:" + result);} catch (RemoteException e) {Log.e(TAG, "testBinderModule2 e:" + e);e.printStackTrace();}return result;}//endregionprivate void bindService() {Log.i(TAG, "bindService mPackageName:" + mPackageName);Intent intent = new Intent();intent.setComponent(new ComponentName(IpcServiceConstant.SERVICE_PKG_NAME,IpcServiceConstant.SERVICE_DATA_COLLECTION_NAME));startService(intent);mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}private void unbindService() {Log.i(TAG, "unbindService mPackageName:" + mPackageName);if (isServiceConnected()) {mContext.unbindService(mConnection);mIpcService = null;}}private void startService(Intent intent) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {mContext.startForegroundService(intent);} else {mContext.startService(intent);}}private void reConnect() {if (!isServiceConnected()) {Log.i(TAG, "reConnect ");bindService();}}private boolean isServiceConnected() {return null != this.mIpcService;}private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.i(TAG, "onServiceConnected name = " + name);mIpcService = IIpcService.Stub.asInterface(service);if (null != mServiceConnectListener) {mServiceConnectListener.onConnected();register(mPackageName, mIpcServiceListener);}}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.i(TAG, "onServiceDisconnected name = " + name + " mServiceConnectListener:" + mServiceConnectListener);mIpcService = null;if (null != mServiceConnectListener) {mServiceConnectListener.onDisconnected();unregister(mPackageName);}reConnect();}};private void register(String packageName, IIpcServiceListener iFtIpcServiceListener) {Log.i(TAG, "register: packageName = " + packageName+ " iFtIpcServiceListener:" + iFtIpcServiceListener);if (isServiceConnected()) {try {mIpcService.register2Server(packageName, iFtIpcServiceListener);} catch (RemoteException e) {Log.e(TAG, e.getMessage(), e);}}}private void unregister(String packageName) {Log.i(TAG, "unregister: packageName = " + packageName);if (isServiceConnected()) {try {mIpcService.unregister2Server(packageName);} catch (RemoteException e) {Log.e(TAG, e.getMessage(), e);}}}public interface ServiceConnectListener {void onConnected();void onDisconnected();}}
public class IpcServiceConstant {public static final String SERVICE_PKG_NAME = "com.android.demo.lileidemo";public static final String SERVICE_DATA_COLLECTION_NAME = SERVICE_PKG_NAME + ".service.IpcService";
}
三、aidl 客户端
客户端引用 aidl仓库编译的aidl-debug.aar
1.创建客户端请求类 IpcUtil.java
public class IpcUtil {private static final String TAG = AppConstants.APP_TAG + "IpcUtil";private static volatile IpcUtil mInstance;private IpcManager mFtIpcManager;public static IpcUtil getInstance() {if (null == mInstance) {synchronized (IpcUtil.class) {if (null == mInstance) {mInstance = new IpcUtil();}}}return mInstance;}public void disconnectService() {Log.i(TAG, "disconnectService() ");if (null != mFtIpcManager) {mFtIpcManager.disconnectService();}}public void connectService() {Log.i(TAG, "connectService() ");if (null == mFtIpcManager) {mFtIpcManager = IpcManager.getInstance(FtClientApp.getAppContext());}mFtIpcManager.connectService(mServiceConnectListener, mFtIpcServiceListener);}public void testBinderPool(int binderCode) {Log.i(TAG, "testBinderPool() 11 binderCode:" + binderCode);String serverRet = mFtIpcManager.queryBinderAndTestCall(binderCode);Log.i(TAG, "testBinderPool() 22 serverRet:" + serverRet);}public Bitmap getTestBitmap() {File file = new File(path);if (!file.exists()) {Log.i(TAG, "getTestImage() file is null for file:" + file);return null;}// First decode with inJustDecodeBounds=true to check dimensionsfinal BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeFile(file.getAbsolutePath(), options);options.inJustDecodeBounds = false;return BitmapFactory.decodeFile(file.getAbsolutePath(), options);}String path = "/sdcard/lilei/20230207161749238.jpg";public ParcelFileDescriptor getImagePfd() {ParcelFileDescriptor pfd = null;try {pfd = ParcelFileDescriptor.open(new File(path), MODE_READ_WRITE);} catch (FileNotFoundException e) {throw new RuntimeException(e);}Log.i(TAG, "getPfd() pfd:" + pfd);return pfd;}public ParcelFileDescriptor getBitmapPfd() {ParcelFileDescriptor pfd = null;Bitmap bitmap = BitmapFactory.decodeResource(FtClientApp.getAppContext().getResources(), R.drawable.large2);//将Bitmap转成字节数组ByteArrayOutputStream stream = new ByteArrayOutputStream();bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);byte[] byteArray = stream.toByteArray();try {MemoryFile memoryFile = new MemoryFile("test", bitmap.getByteCount());Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);pfd = ParcelFileDescriptor.dup(des);//向内存中写入字节数组memoryFile.getOutputStream().write(byteArray);//关闭流memoryFile.getOutputStream().close();memoryFile.close();} catch (IOException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}Log.i(TAG, "getPfd() pfd:" + pfd + " getByteCount:" + bitmap.getByteCount());return pfd;}public ParcelFileDescriptor getTextPfd() {ParcelFileDescriptor pfd = null;try {MemoryFile memoryFile = new MemoryFile("test", 1024);Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);pfd = ParcelFileDescriptor.dup(des);//向内存中写入字节数组memoryFile.getOutputStream().write(new byte[]{1, 2, 5, 4, 3});//关闭流memoryFile.getOutputStream().close();memoryFile.close();} catch (IOException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}Log.i(TAG, "getTextPfd() pfd:" + pfd);return pfd;}public void sendFileOneway(String responseJson) {Log.i(TAG, "sendFileOneway() responseJson:" + responseJson);if (null != mFtIpcManager) {mFtIpcManager.processClientRequestOneway(responseJson);}}/*** DVR or CVBOX generates the JSON format required by* the mobile app from the files to be sent.** @param requestJson json to be send to phone.*/public String sendFile(String requestJson) {Log.i(TAG, "sendFile() requestJson:" + requestJson);if (null != mFtIpcManager) {return mFtIpcManager.processClientRequest(requestJson, getBitmapPfd());}return "error";}private IpcManager.ServiceConnectListener mServiceConnectListener = new IpcManager.ServiceConnectListener() {@Overridepublic void onConnected() {Log.i(TAG, "onConnected() ");}@Overridepublic void onDisconnected() {Log.i(TAG, "onDisconnected() recall connectService");connectService();}};private IIpcServiceListener.Stub mFtIpcServiceListener = new IIpcServiceListener.Stub() {@Overridepublic String processServiceRequest(String serviceRequest) throws RemoteException {String response = "response from ft client";Log.i(TAG, "processJsonRequest() serviceRequest:" + serviceRequest);return response;}// @Override
// public String processJsonRequest(String requestJson) throws RemoteException {
// String responseJson = IpcTestDataUtil.getInstance().processJsonRequest(requestJson);
// Log.i(TAG, "processJsonRequest() requestJson:" + requestJson);
// return responseJson;
// }};}
2.界面调用Binder 池中的对象及接口方法。
IpcUtil.getInstance().testBinderPool(IpcManager.BINDER_MODULE1);
IpcUtil.getInstance().testBinderPool(IpcManager.BINDER_MODULE2);
