C# 使用正则表达式(dotnetcore)_"regex rx = new regex(@\"
赞
踩
AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言,它是用来实现进程间通讯的,本文使用AIDL写一个小demo来实现夸进程间通讯 。
本文接着这一篇文章写,这篇文章有个留着的——就是远程AIDL服务端注册解绑Listener。
先来看看服务端和客户端大体的AIDL-生成的java文件 。
如上图所示,蓝色框内是我们定义的AIDL文件,红色框内是自动编译生成的java文件。
这里只是个简单示意,接下来会在此基础上改。下面我们看下服务端的代码:
- /**
- * 音乐管理的服务类
- */
- public class MusicManagerService extends Service {
-
- private static final String TAG = MusicManagerService.class.getSimpleName();
-
- private ArrayList<Music> mMusicList = new ArrayList<>(); //生成的音乐列表
- private List<INewMusicArrivedListener> mListenerList = new ArrayList<>(); //客户端注册的接口列表
- private boolean isServiceDestroy = false; //当前服务是否结束
- private int num = 0;
-
- /**
- * 解绑服务
- * @param conn
- */
- @Override
- public void unbindService(ServiceConnection conn) {
- super.unbindService(conn);
- Log.e(TAG,"unbindService-----");
- }
-
- /**
- * 服务端通过Binder实现AIDL的IMusicManager.Stub接口
- * 这个类需要实现IMusicManager相关的抽象方法
- */
- private Binder mBinder = new IMusicManager.Stub() {
- @Override
- public List<Music> getMusicList() throws RemoteException {
-
- return mMusicList;
- }
-
- @Override
- public void addMusic(Music music) throws RemoteException {
- mMusicList.add(music);
- }
-
- @Override
- public void registerListener(INewMusicArrivedListener listener) throws RemoteException {
- mListenerList.add(listener);
- int num = mListenerList.size();
- Log.e(TAG, "添加完成, 注册接口数: " + num);
- }
-
- @Override
- public void unregisterListener(INewMusicArrivedListener listener) throws RemoteException {
- // 后文实现
- }
- };
-
-
- //新音乐到达后给客户端发送相关通知
- private void onNewMusicArrived(Music music) throws Exception {
- mMusicList.add(music);
- Log.e(TAG, "发送通知的数量: " + mMusicList.size());
- int num = mListenerList.size();
- for (int i = 0; i < num; ++i) {
- INewMusicArrivedListener listener = mListenerList.get(i);
- listener.onNewMusicArrived(music);
- }
- for (Music b : mMusicList){
- Log.e(TAG,b.name+" "+b.author);
- }
- }
-
- @Override public void onCreate() {
- super.onCreate();
- Log.e(TAG,"onCreate-------------");
- //首先添加两首歌曲
- mMusicList.add(new Music("《封锁我一生》", "王杰"));
- mMusicList.add(new Music("《稻香》", "周杰伦"));
- }
-
- @Override public void onDestroy() {
- isServiceDestroy = true;
- super.onDestroy();
- Log.e(TAG,"onDestroy-----");
- }
-
- @Nullable @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
- }
我们看下客户端的代码:这里需要注意,回调接口在客户端注册,目的是为了让服务端拿到回调接口,通知客户端。客户端拿到的是服务端的代理,注册回调可看作服务端收到回调listener。
- private TextView music_list;
-
- private IMusicManager mRemoteMusicManager; //音乐管理类 通过aidl文件编译生成的java类
-
-
- //监听新音乐的到达的接口
- private INewMusicArrivedListener musicArrivedListener = new INewMusicArrivedListener.Stub() {
- /**
- * 服务端有新音乐生成
- * @param newMusic
- * @throws RemoteException
- */
- @Override
- public void onNewMusicArrived(Music newMusic) throws RemoteException {
-
- }
- };
-
-
- //绑定服务时的链接参数
- private ServiceConnection mConnection = new ServiceConnection() {
- @Override public void onServiceConnected(ComponentName name, IBinder service) {
- IMusicManager musicManager = IMusicManager.Stub.asInterface(service);
-
- try {
- mRemoteMusicManager = musicManager;
- Music newMusic = new Music("《客户端音乐》", "rock");
- musicManager.addMusic(newMusic);
- musicManager.registerListener(musicArrivedListener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- @Override public void onServiceDisconnected(ComponentName name) {
- mRemoteMusicManager = null;
- Log.e(TAG, "绑定结束");
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- music_list = findViewById(R.id.music_list);
- }
-
- /**
- * 获取music列表
- *
- * @param view 视图
- */
- public void getMusicList(View view) {
- if (mRemoteMusicManager !=null){
- List<Music> list = null;
- try {
- list = mRemoteMusicManager.getMusicList();
- }catch (Exception e){
-
- }
- if (list!=null){
- String content = "";
- for (int i = 0; i < list.size(); ++i) {
- content += list.get(i).toString() + "\n";
- }
- music_list.setText(content);
- }
- }
- Toast.makeText(getApplicationContext(), "正在获取中...", Toast.LENGTH_SHORT).show();
- }
-
-
- /**
- * 绑定服务按钮的点击事件
- *
- * @param view 视图
- */
- public void bindService(View view) {
- Intent intent = new Intent(this, MusicManagerService.class);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
-
-
- /**
- * 解绑服务
- */
- public void unbindService(View view){
- unbindService(mConnection);
- }
绑定一个服务 ,然后注册一个新音乐到达的接口,当服务端有新音乐生成的时候就会触发这个接口。
在IPC的过程当中,可能会遇到如下状况须要考虑:
在AIDL中客户端向服务端注册一个回调方法时,服务端要考虑客户端是否意外退出(客户端由于错误应用Crash,或者被Kill掉了),服务端还不知道,去回调客户端,出现错误。
客户端和服务端进程状态处理
在进程间通讯过程当中,极可能出现一个进程死亡的状况。若是这时活着的一方不知道另外一方已经死了就会出现问题。那咱们如何在A进程中获取B进程的存活状态呢?
android确定给咱们提供了解决方式,那就是Binder的linkToDeath和unlinkToDeath方法,linkToDeath方法须要传入一个DeathRecipient对象,DeathRecipient类里面有个binderDied方法,当binder对象的所在进程死亡,binderDied方法就会被执行,咱们就能够在binderDied方法里面作一些异常处理,释放资源等操作了。
Android SDK提供一个封装好的对象:RemoteCallbackList,帮我们自动处理了Link-To-Death的问题。
这里,简单介绍一下RemoteCallbackList:
public class RemoteCallbackList
extends Object
java.lang.Object
↳ android.os.RemoteCallbackList<E extends android.os.IInterface>负责维护远程接口列表的繁琐工作,一般用于执行从Service到其客户端的回调 。特别是:
跟踪一组已注册的IInterface回调,通过其基础唯一性IBinder进行识别:IInterface#asBinder。
附加IBinder.DeathRecipient到每一个已注册的接口,以便在其过程消失时能够将其从列表中清除。
对接口的基础列表执行锁定以处理多线程传入的调用,并以线程安全的方式遍历该列表。
要使用此类,只需与服务一块儿建立一个实例,而后在客户端注册和取消注册服务时调用其register(E)和unregister(E)方法。回调到注册客户端,使用beginBroadcast(), getBroadcastItem(int)和finishBroadcast()。
当注册的回调在过程中消失了,该类将负责自动将其从列表中删除。若是要在这种状况下做其余工作,要建立一个实现该onCallbackDied(E)方法的子类。
RemoteCallbackList帮咱们避免了IPC两个进程在调用过程当中发生意外crash,致使回调失败或者进程crash的问题。
将上面服务端-客户端代码修改如下:
- /**
- * 音乐管理的服务类
- */
- public class MusicManagerService extends Service {
-
- private static final String TAG = MusicManagerService.class.getSimpleName();
-
- // 省略部分代码。。。
-
- private RemoteCallbackList<INewMusicArrivedListener> mListenerList = new RemoteCallbackList<>(); //客户端注册的接口列表
-
-
- //新音乐到达后给客户端发送相关通知
- private void onNewMusicArrived(Music music) throws Exception {
- mMusicList.add(music);
-
- mListenerList.beginBroadcast();
- //遍历全部注册的Listener,逐个调用它们的实现方法,也就是通知全部的注册者
- for(int i=0;i<mListenerList.getRegisteredCallbackCount();i++){
- INewMusicArrivedListener listener = mListenerList.getBroadcastItem(i);
- listener.onNewMusicArrived(music);
- }
- mListenerList.finishBroadcast();
-
- for (Music b : mMusicList){
- Log.e(TAG,b.name+" "+b.author);
- }
- }
-
- /**
- * 服务端通过Binder实现AIDL的IMusicManager.Stub接口
- * 这个类需要实现IMusicManager相关的抽象方法
- */
- private Binder mBinder = new IMusicManager.Stub() {
-
- // 省略部分代码。。。
-
- @Override
- public void registerListener(INewMusicArrivedListener listener) throws RemoteException {
-
- mListenerList.register(listener);
- }
-
- @Override
- public void unregisterListener(INewMusicArrivedListener listener) throws RemoteException {
-
- mListenerList.unregister(listener);
- }
- };
-
- }
- private IMusicManager mRemoteMusicManager; //音乐管理类 通过aidl文件编译生成的java类
-
-
- //监听新音乐的到达的接口
- private INewMusicArrivedListener musicArrivedListener = new INewMusicArrivedListener.Stub() {
- /**
- * 服务端有新音乐生成
- * @param newMusic
- * @throws RemoteException
- */
- @Override
- public void onNewMusicArrived(Music newMusic) throws RemoteException {
-
- }
- };
-
- //进程挂掉通知处理
- private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- if(mRemoteMusicManager != null){
- //Server端意外died
- try {
- mRemoteMusicManager.unRegistListener(musicArrivedListener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- mRemoteMusicManager.asBinder().unlinkToDeath(mDeathRecipient,0);
- mRemoteMusicManager = null;
- //TODO:bindService again
- bindService()
- }
- }
- };
-
-
- //绑定服务时的链接参数
- private ServiceConnection mConnection = new ServiceConnection() {
- @Override public void onServiceConnected(ComponentName name, IBinder service) {
- mRemoteMusicManager = IMusicManager.Stub.asInterface(service);
-
- try {
- //设定死亡接收器,这个是针对IBinder对象的
- service.linkToDeath(mDeathRecipient,0);
-
- Music newMusic = new Music("《客户端音乐》", "rock");
- mRemoteMusicManager.addMusic(newMusic);
- mRemoteMusicManager.registerListener(musicArrivedListener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- @Override public void onServiceDisconnected(ComponentName name) {
- mRemoteMusicManager = null;
- Log.e(TAG, "绑定结束");
- }
- };
-
-
- /**
- * 绑定服务按钮的点击事件
- *
- */
- public void bindService() {
- Intent intent = new Intent(this, MusicManagerService.class);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
-
-
- /**
- * 解绑服务
- */
- public void unbindService(View view){
-
- try {
- if(mRemoteMusicManager != null && mRemoteMusicManager.asBinder().isBinderAlive()){
- mRemoteMusicManager.unRegistListener(musicArrivedListener);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- unbindService(mConnection);
- }
这里须要说明的是在客户端进程中,经过DeathRecipient对象,调用IBinder的linkToDeath和unLinkToDeath方法,实现了 万一Server进程Died以后,对Service进行再次注册。
简单介绍一下linkToDeath和unlinkToDeath:
- /**
- * Interface for receiving a callback when the process hosting an IBinder
- * has gone away.
- *
- * @see #linkToDeath
- */
- public interface DeathRecipient {
- public void binderDied();
- }
-
- /**
- * Register the recipient for a notification if this binder
- * goes away. If this binder object unexpectedly goes away
- * (typically because its hosting process has been killed),
- * then the given {@link DeathRecipient}'s
- * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
- * will be called.
- *
- * <p>You will only receive death notifications for remote binders,
- * as local binders by definition can't die without you dying as well.
- *
- * @throws RemoteException if the target IBinder's
- * process has already died.
- *
- * @see #unlinkToDeath
- */
- public void linkToDeath(@NonNull DeathRecipient recipient, int flags)
- throws RemoteException;
-
- /**
- * Remove a previously registered death notification.
- * The recipient will no longer be called if this object
- * dies.
- *
- * @return {@code true} if the <var>recipient</var> is successfully
- * unlinked, assuring you that its
- * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
- * will not be called; {@code false} if the target IBinder has already
- * died, meaning the method has been (or soon will be) called.
- *
- * @throws java.util.NoSuchElementException if the given
- * <var>recipient</var> has not been registered with the IBinder, and
- * the IBinder is still alive. Note that if the <var>recipient</var>
- * was never registered, but the IBinder has already died, then this
- * exception will <em>not</em> be thrown, and you will receive a false
- * return value instead.
- */
- public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
使用它比较简单,只须要实现DeathRecipient的bindDied方法。
linkToDeath和unLinkToDeath是成对出现的,参照上面客户端中MainActivity的实现,在ServiceConnection中onServiceConnected的时候link,在bindDied方法中unlink。linkToDeath是为IBinder对象设置死亡代理,unLinkToDeath是解除以前设置的死亡代理,并能够在此时做从新绑定的动作。
如果不用remoteCallList绑定和解绑listener会怎样,为什么这么麻烦呢?
可以增加几条打印,看看解绑和绑定时能不能成功:
原因就在于我们的注册对象listener是在进程间传输的,Binder在服务端会把客户端传递过来的对象重新转换为新的对象,因而注册和解注册的根本就不是一个对象,当然不能达到解除注册的目的了;
我们在服务端解除listener的代码处添加下面两行代码:
- System.out.println("解绑定时候的listener: "+listener);
-
- System.out.println("解绑定时候的listener对应的Binder: "+listener.asBinder());
在服务端绑定listener的代码处下面两行代码:
- System.out.println("绑定时候的listener: "+listener);
-
- System.out.println("绑定时候的listener对应的Binder: "+listener.asBinder());
而后运行,查看Log输出:
从Log输出上面很明显的可以看到绑定和解绑定的listener并不是同一个对象,那么解除绑定肯定是失败的,但是从Log输出上可以看出listener所对应的Binder对象是相同的,他们都是BinderProxy对象,原因在于:跨进程间通信时,服务端要创建Binder对象,客户端要拿到服务端Binder对象在Binder驱动中的引用,对应到客户端就是BinderProxy对象了,我们这里的情况是服务端和客户端通信,那么此时的服务端将成为客户端,所以收到的Binder就是BinderProxy类型了,为什么绑定和解绑定过程中listener对象不同,但是listener对象对应的Binder对象却的相同的呢?这个我们需要看下系统为我们的IMessageManager.aidl生成的.java文件内容了:
- @Override
- public void registerLoginUser(com.hzw.messagesend.ILoginOnListener listener) throws android.os.RemoteException
- {
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
- mRemote.transact(Stub.TRANSACTION_registerLoginUser, _data, _reply, 0);
- _reply.readException();
- }
- finally {
- _reply.recycle();
- _data.recycle();
- }
- }
其他代码就不展示了。因此,如果不用RemoteCallList而用普通的Arralist的话,也可以:
- public void unRegisterListener(INewMusicArrivedListener listener)
- throws RemoteException {
- for(int i = list.size()-1; i >= 0; i--)
- {
- if(list.get(i).asBinder() == listener.asBinder())
- {
- //解除绑定注册
- System.out.println("解绑定时候的listener: "+listener);
- System.out.println("解绑定时候的listener对应的Binder: "+listener.asBinder());
- list.remove(list.get(i));
- break;
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。