- 如何使用AIDL进行多进程通信
- 如何在使用AIDL时保证Service在主进程被杀死的情况下自己不被杀死
- 在使用AIDL的过程中如何双向通信
这种场景在音乐播放器上会比较多,主要是把播放音乐的核心逻辑放到单独的进程(注意aidl包名一样)或者同一个项目里单独的子进程去做,这样如果应用在退出,或者进程意外终止,播放也会正常进行。同时放到单独的进程,从某种意义上讲也可以减轻主项目的负担,也算是一种解耦吧。
对于上面三个问题下面提供了思路,ok,来吧,面对疾风吧…
1.如何使用AIDL进行多进程通信
这个不用多说,网上很多例子,我来show my code 就好。
IRecordService.aidl:
// IRecordService.aidl
package com.recorderboard;
import com.recorderboard.INotifyCallback;
// Declare any non-default types here with import statements
interface IRecordService {
void startRecord();
void stopRecord();
void registerCallBack(INotifyCallback cb);
void unregisterCallBack(INotifyCallback cb);
}
INotifyCallback.aidl:
// INotifyCallback.aidl
package com.recorderboard;
// Declare any non-default types here with import statements
interface INotifyCallback {
void notifyClient(int flag);
}
RecordService.java:
package com.recorderboard.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import com.recorderboard.INotifyCallback;
import com.recorderboard.IRecordService;
import java.io.IOException;
public class RecordService extends Service {
private static final String TAG = "RecordService";
private boolean isRecording = false;
private MediaRecorder mMediaRecorder;
private RemoteCallbackList<INotifyCallback> mCallBacks = new RemoteCallbackList<>();
@Override
public void onCreate() {
Log.i(TAG,"onCreate");
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,"onBind");
return new RecordServiceImpl();
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG,"onUnbind");
return super.onUnbind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG,"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onRebind(Intent intent) {
Log.i(TAG,"onRebind");
super.onRebind(intent);
}
@Override
public void onDestroy() {
Log.i(TAG,"onDestroy");
mCallBacks.kill();
stopSelf();
}
private class RecordServiceImpl extends IRecordService.Stub{
@Override
public void startRecord() throws RemoteException {
Log.i(TAG,"startRecord...");
}
@Override
public void stopRecord() throws RemoteException {
}
@Override
public void registerCallBack(INotifyCallback cb) throws RemoteException {
mCallBacks.register(cb);
}
@Override
public void unregisterCallBack(INotifyCallback cb) throws RemoteException {
mCallBacks.unregister(cb);
}
}
private void notifyCallBack(int flag){
final int len = mCallBacks.beginBroadcast();
for(int i = 0 ;i<len;i++){
try {
mCallBacks.getBroadcastItem(i).notifyClient(flag);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallBacks.finishBroadcast();
}
}
RecordManager.java:
package com.recorderboard.logic;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.recorderboard.INotifyCallback;
import com.recorderboard.IRecordService;
import com.recorderboard.service.RecordService;
import static android.content.Context.BIND_AUTO_CREATE;
public class RecordManager {
private Activity context;
private static RecordManager mInstance = null;
private IRecordService mService;
private INotifyCallback notifyCallback = new INotifyCallback.Stub() {
@Override
public void notifyClient(int flag) throws RemoteException {
Log.i("xxx","flag:"+ flag);
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IRecordService.Stub.asInterface(service);
try {
mService.registerCallBack(notifyCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
try {
mService.unregisterCallBack(notifyCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
mService = null;
}
};
public static RecordManager getInstance(Activity context) {
if (mInstance == null) {
synchronized (RecordManager.class) {
if (mInstance == null) {
mInstance = new RecordManager(context);
}
}
}
return mInstance;
}
private RecordManager(Activity activity) {
this.context = activity;
}
public void bindRecordService() {
context.startService(new Intent(context, RecordService.class));
context.bindService(new Intent(context, RecordService.class), mConnection, BIND_AUTO_CREATE);
}
public void unbindRecordService() {
context.unbindService(mConnection);
}
public void startRecord(){
if(mService!=null){
try {
mService.startRecord();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void stopRecord(){
if(mService!=null){
try {
mService.stopRecord();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
AndroidMenifest.xml:
<service
android:name=".service.RecordService"
android:label="@string/app_name"
android:process=":record" />
2.如何在使用AIDL时保证Service在主进程被杀死的情况下自己不被杀死
第一步的代码如果看到RecordManager的话,里面有这样的方法,对,就是这样,先startService,再bindService,虽然是两次不同的启动,经验证,两个service实例是同一个,也就是说,startService保证了Service在主进程被杀死的情况下自己不被杀死,再bindService保证了我们可以用aidl进行正常通信。
public void bindRecordService() {
context.startService(new Intent(context, RecordService.class));
context.bindService(new Intent(context, RecordService.class), mConnection, BIND_AUTO_CREATE);
}
我看了几个开源播放器也是这样用的,这里代码简单,不排除其他意外BUG,本文仅提供了思路。
3.在使用AIDL的过程中如何双向通信
普通的aidl基本上是客户端调用服务端,即客户端向服务端通信,但很多项目很多场景也需要服务端向客户端通信,这里提供三种思路
- 使用广播
- 客户端持有Service的实例直接调用Service服务端的public方法(不适合多进程)。
- 服务端向客户端回调:使用RemoteCallbackList()。
- 使用第三方库:HermesEventBus
这里只讲第二个,因为广播是容易想到的多进程通信的组件,通信很多的话建议广播。HermesEventBus是网上的一个三方库,具体没怎么研究。
使用RemoteCallbackList的方法:
其实就是观察者模式在AIDL中的运用
1.开头的代码INotifyCallback.aidl:
interface INotifyCallback {
void notifyClient(int flag);//回调客户端的aidl方法
}
2.在IRecordService.aidl中添加
void registerCallBack(INotifyCallback cb);
void unregisterCallBack(INotifyCallback cb);
3.实例化RemoteCallbackList的list变量,目的是把客户端传过来的callback(这里是INotifyCallback) 添加到这个list中,方便以后的回调
private RemoteCallbackList<INotifyCallback> mCallBacks = new RemoteCallbackList<>();
4.在RecordServiceImpl类里增加实现:
@Override
public void registerCallBack(INotifyCallback cb) throws RemoteException {
mCallBacks.register(cb);//添加到list中
}
@Override
public void unregisterCallBack(INotifyCallback cb) throws RemoteException {
mCallBacks.unregister(cb);//从list中移除
}
5.在RecorderService中增加notifyCallBack方法,int flag标识可以根据自己的业务修改:
private void notifyCallBack(int flag){
final int len = mCallBacks.beginBroadcast();
for(int i = 0 ;i<len;i++){
try {
mCallBacks.getBroadcastItem(i).notifyClient(flag);//遍历,回调所有注册到服务端的客户端
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallBacks.finishBroadcast();
}
6.在RecordeService的onDestory()中增加:
mCallBacks.kill();
这样在服务端需要回调给客户端信息的时候就可以调用notifyCallBack()方法,接下来再看看客户端如何接收服务端的回调:
1.在绑定service的类(RecordManager.java)中增加:
private INotifyCallback notifyCallback = new INotifyCallback.Stub() {
@Override
public void notifyClient(int flag) throws RemoteException {
//recevice data from service
}
};
2.在bindService时,ServiceConnection对象的onServiceConnected回调方法里添加注册回调:
try {
mService.registerCallBack(notifyCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
2.在unbindService时,ServiceConnection对象的onServiceDisconnected回调方法里解除注册回调:
try {
mService.unregisterCallBack(notifyCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
这样,在notifyCallback的notifyClient回调方法里就接收到了服务端Service的数据。
大功告成!
等等,还要注意一下,如果在service里回调时是在子线程(调用notifyCallBack()方法是在子线程),那么别忘了客户端的notifyClient回调方法也是在子线程哦。