谈谈Android多进程通信---AIDL

  • 如何使用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基本上是客户端调用服务端,即客户端向服务端通信,但很多项目很多场景也需要服务端向客户端通信,这里提供三种思路

  1. 使用广播
  2. 客户端持有Service的实例直接调用Service服务端的public方法(不适合多进程)。
  3. 服务端向客户端回调:使用RemoteCallbackList()。
  4. 使用第三方库: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回调方法也是在子线程哦。

欢迎关注公众号,给你更多技术干货:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值