最近有个项目刚好使用了Service,特别是AIDL远程服务,经过这次项目对Service有了更好的理解,在这里作个总结。
startService / bindService 混合使用
如果startService / bindService 混合使用 那Service的生命同期会怎样呢,其实只要记住以上的思路,这种问题很好理解。首先 startService 与 stopService 对应 ,没有stopService 之前不会 destroy Service , bindService 与 unbindService对应 ,没有 unbindService 前也不会destroy Service。
为服务添加权限
相信大家做的服务都是公共的,即所有应用都可以调用。但是如果我想我的服务只给特定的应用调用,应该如何设置呢?我们可以给服务添加权限。关于权限,Android系统给权限为了四个类别:
<uses-permission android:name="android.permission.custom.XXX"/>
如果我们想让自己开发的Service只能被特定的Client调用,那就可以添加自定义的权限。比如危险级,我们可以在AndroidManifest.xml中声明对应的权限,只有应用也设置了这个权限,才能正常启动服务。
关于AIDL远程服务
所谓的AIDL远程服务 就是运行在另一个进程的服务,平时我们调用的服务都运行在主线程。要使用AIDL服务就必须写AIDL接口,向外暴露接口就可以与远程服务进行交互了。对于AIDL有如下几个值得注意的地方:
AIDL的接口如何升级?
在做一个比较大型的项目,那项目会不断迭代,那就有可能增加、修改AIDL接口,那如何保证AIDL接口和老的接口不会混乱呢,根据我的经验有如下总结:
Service管理多个客户端
如果Service有多个客户端,如何安全地与它们通信呢?如何给各个客户端回调结果呢? 在这里我要说说我在最近项目出现的一个问题,我在项目中要做一个公共的服务,类似于指纹解锁,其它应用通过调用我的服务来获取结果,我设计了start(callback), stop()两个接口,一开始我就用单回调的方式,即在代码中定义一个callback的属性,谁调用了start就把callback设置成谁,只有最后一个调用start的Client能够获得回调,代码如下 :
private Callbak mCallback; public void start(Callback callback) { this.mCallback = callback; } public void stop() { this.mCallback = null; }
这种方式,在单个应用时是很有效的,在多个应用时,只要应用能按顺序执行start、stop 那这个接口的设计也没什么问题。但是事情没想象中那么简单,如果Client1调用了start,跟着Client2也调用了start,这时Client1 要stop,那会怎样,那整个服务都stop了。这个就是我设计的服务中出现的大问题,之后我想着为我的服务接口作一些改变,以适应这种多应用的不按顺序的调用 。我第一个想法就是用register、unregister的方式,用一个list收集所有的callback ,回调时可以轮循,stop时也可以通过判断list的个数,如果是小于等于1,那就执行stop :
private List<Callbak> mCallbacks; public void start(Callback callback) { mCallbacks.add(callback); } public void stop(Callback callback) { mCallbacks.remove(callback); if(list.size() >= 1) return; }
考虑到接口的升级,这个改动是最小的,只给stop添加了一个参数。但是这个方式也有毛病,我们服务对Callback的引用是强引用,如果Client异常退出了,那引用还在并且会越积越多,在回调的时候,也可能出现DeadObjectException的错误。通过网络查找资料,我找到了RemoteCallbackList,RemoteCallbackList也是一个列表,保存的是回调接口,使用Link-To-Death回调 (在Sevice中接受到这个Binder对象,并且使用 binder.linkToDeath(),注册一个DeathRecipient回调;实现DeathRecipient。当Client意外退出的时候,DeathRecipient.binderDied()将被回调,我们可以在这里释放相关的资源。)。最终代码如下:
private RemoteCallbackList<Callback> mCallbacks = new RemoteCallbackList<>(); public void start(Callback callback) { mCallbacks.register(callback); } public void stop(Callback callback) { mCallbacks.unregister(callback); if(mCallbacks.getBroadcastItem() >= 1) return; } private void notifyResult(String result) { final int len = mCallbacks.beginBroadcast(); for (int i = 0; i < len; i++) { try { mCallbacks.getBroadcastItem(i).onResult(result); } catch (RemoteException e) { e.printStackTrace(); } } mCallbacks.finishBroadcast(); }
使用Messenger 实现 Servie与Client端通信
Messenger是基于Handler的,通过为Messenger添加Handler来传递处理数据,之后Service与Client的通信都是通过传递的Handler来进行。用这种方式可以不需要定义AIDL接口,也就不出现因为修改AIDl接口所造成的接口版本不对应的麻烦。
Messenger的使用就是通过 Handler传递消息, 客户端send方法发送的是一个Message,这个Message.replyTo指向的是一个Messenger,Messenger又持有客户端的一个Binder对象(MessengerImpl),服务端正是利用这个Binder对象做的与客户端的通信。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍基于django ManyToMany 使用的注意事项详解,包括了基于django ManyToMany 使用的注意事项详解的使用技巧和注意事项,需要的朋友参考一下 使用场景一: 如果在一张表中ManayTOManay字段关联的是自身,也就是出项这样的代码: ManyToManyField(self) 那么,你需要注意一点,当你采用add方法将一个自身对象添加到该字段时,则自动建立两
本文向大家介绍jQuery中on方法使用注意事项详解,包括了jQuery中on方法使用注意事项详解的使用技巧和注意事项,需要的朋友参考一下 on(eventType,[childSelector],[data],fn) 采用事件委托机制绑定事件,好处是子元素动态加入时无需再次绑定。 on方法可以传入childSelector指定添加事件处理函数的具体子元素,还可以传入对象格式的数据data,fn函
本文向大家介绍详解C语言位域的使用与注意事项,包括了详解C语言位域的使用与注意事项的使用技巧和注意事项,需要的朋友参考一下 位域的定义 有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分
主要内容:if语句使用,布尔运算,isin()操作,reindex()操作Pandas 基于 NumPy 构建,它遵循 NumPy 设定的一些规则。因此,当您在使用 Pandas 时,需要额外留意一些事项,避免出现一些不必要的错误。 if语句使用 在 if 语句中,如果您需要将 Pandas 对象转换为布尔值时,需要格外留意,这种操作会引起 ValueError 异常, 下面通过一组示例做简单说明: 输出结果: 从输出结果可以看出,上述代码引发了 ValueError
本文向大家介绍JavaScript"模拟事件"的注意要点详解,包括了JavaScript"模拟事件"的注意要点详解的使用技巧和注意事项,需要的朋友参考一下 DOM中的事件模拟 三个步骤: 首先通过document.createEvent()方法创建event对象,接收一个参数,即表示要创建的事件类型的字符串: UIEvents(DOM3中的UIEvent)鼠标和键盘事件; MouseEvents(
2.6.1 npm init 使用 npm init 初始化一个空项目是一个好的习惯,即使你对 package.json 及其他属性非常熟悉,npm init 也是你开始写新的 Node.js 应用或模块的一个快捷的办法。npm init 有智能的默认选项,比如从根目录名称推断模块名称,通过 ~/.npmrc 读取你的信息,用你的 Git 设置来确定 repository 等等。 2.6.2 np