Android Service使用拾遗[阿里工程师分享]
Service作为android的四大组件之一常用来帮助我们完成一些需要放在后台处理的任务,通过startService和bindService两种方式被调用。因为Service也是在主线程中运行的,所以如果处理耗时任务,一般在Service里再单独创建工作线程去执行耗时任务。使用Service的另一个用处是可以减少业务逻辑与界面的耦合,在产品演进中具备快速迭代的能力。
有的应用有服务需要一直常驻在内存中,如果UI和service在同一进程中,当按home键退到后台时,因为有常驻的service,整个进程的状态是service,即使在低内存时LMK也不会杀掉这个进程,使内存难以回收。这样就得把需要常驻内存的、(几乎)没有界面显示的业务逻辑单独拆出来放在一个进程里,有界面显示的放在另一个进程里。但这么做会占用更多的内存,只有当应用位于后台时也要处理大量任务时才应该考虑让app运行在多个进程中。
除此之外,在使用过程中还会碰到低内存时的情形,有一些service的知识平时可能接触的不多,在这里我做了一些简单的总结。
一、startService
1.1 onStartCommand的返回值
常用的返回值有3种,START_STICKY、START_NOT_STICKY和START_REDELIVER_INTENT。其中START_STICKY和START_REDELIVER_INTENT在service没有执行完就被系统杀掉后的一段时间内会被系统重启,被系统杀掉的情形可能是在系统内存不足或者某些ROM定制了管理后台任务的策略,比如锁屏一段时间后,不在白名单中的应用会被杀掉以释放内存。如果是service本身的错误导致在没有执行完就crash退出,是不会被系统重启的。
1)START_NOT_STICKY
如果onStartCommand返回START_NOT_STICKY,那即使service没有执行完,被杀掉后也不会被系统重启。如果这个service是用来为界面的Activity处理数据用的,那它不是必须一定要执行完的,大部分的情形是service所在的应用进程都已经被系统杀掉,这时没必要重启再次执行service。
2)START_STICKY
被杀掉后系统会重启service,并且onStartCommand一定会被调用,如果在重启期间没有任何启动命令被传递到service,那么参数Intent将为null。这里说的重启期间是指系统杀掉service后到系统再次启动该service的时间间隔,那么这段时间有多长呢,frameworks有专门处理重启service的代码。在ActiveServices.java的scheduleServiceRestartLocked函数里,
这个分支是处理非persistent的情况,只有系统应用才有权限把自己设为persistent,即在AndroidManifest.xml里设了android:persistent="true"。deliveredStarts中存放的是已经传递给service的启动参数,pendingStarts中存放的是还没有传递给service的启动参数。minDuration是被系统杀掉后被系统重启的时间间隔,resetTime是重置这个重启时间的间隔。如果Intent不为null,会更新这两个值,
然后重新计算service下一次被重启的时间。如果之前没被重启过,restartDelay为0,则把restartDelay设为minDuration。如果之前重启过,当前时间距上次重启的时间已经超过了resetTime,则把restartCount置为1,restartDelay设为minDuration;如果距上次重启时间还不到resetTime,则调大restartDelay。这是为了防止service被在内存不足的情况下被频繁重启,第一次内存不足时杀掉service,1s后重启该service,重启后又消耗了一部分内存造成内存再次不足再次杀掉service,这时1s后就不应该重启了,要往后推迟一段时间再尝试重启。
nextRestartTime就是下次重启service的时间了,然后postAtTime在nextRestartTime这个时间点重启service,并且更新nextRestartTime。
因为START_STICKY类型默认传入的Intent为null,所以在使用时我们要仔细考虑。如果service需要使用Intent里的参数,那很有可能被重启时并没有调用者能传入这个参数。比如,该service是在某场景下才会被本应用的其他组件所调用启动,那么有可能整个应用都被杀掉了,重启该service时,只会经过Application的onCreate和该service的onCreate、onStartCommand,没有经过调用启动的上下文。或者是收到broadcast而触发该service,则重启期间可能不会收到broadcast。只有当service是必须要完成的,并且不依赖于传入的Intent才需要把返回值设为START_STICKY。
3)START_REDELIVER_INTENT
在重启时会重传被杀时未完成的Intent。比如该startService调用了4次,第1、2次的任务已经被service处理完(比如调用了stopSelf或stopService),第3、4次还未被处理时就被杀掉了,重启时会按顺序传入第3、4个Intent。重启后调用stopSelf的顺序要注意startId的顺序。因为第3、4次任务可能会被service 交给不同线程去执行,可能4先被执行完,如果4执行完后调用stopSelf(startId4)的话,那么3会被立即停止,即使它还没被执行完。所以stopSelf的顺序要严格按照收到onStartCommand中的startId来执行。
1.2.IntentService
在这里推荐使用IntentService,它有一个工作线程和一个Handler,可以通过回调函数onHandleIntent依次处理onStartCommand收到的Intent,在onHandleIntent调完后会自己调stopSelf。
1.3. startForeground
为了防止处于后台的service在低内存时被系统杀掉,service可以调用startForeground()把自己放在前台进程中,但最好在完成任务后及时调用stopForeground把优先级调回来。
二、bindService
2.1 bindService的flag
bindService的第三个参数flags一般都会传0或BIND_AUTO_CREATE,跨进程调用bindService会在引起依赖,比如A进程的Activity中bindService调用B进程service,则B进程的service的oom_adj值依赖于A进程Activity的oom_adj值。如果activity在前台,它的oom_adj值为0,service的值为1,两者都难以被系统杀掉。但如果把flags设为一些“弱连接”类型,比如设为BIND_WAIVE_PRIORITY,则即使Activity位于前台,oom_adj为0,service的oom_adj值为15,也可以很容易被杀掉。其他一些flags还有:
BIND_ABOVE_CLIENT:调用bindService的应用的oomAdj的值比service本身的oom_adj更高,比如activity在后台时,oom_adj为10,service的oom_adj为9,调用者activity更容易被杀掉。
BIND_ADJUST_WITH_ACTIVITY:service的重要性跟调用它的activity一样。比如activity在前台时,oom_adj为0,service的oom_adj也为0。
2.2 DeadObjectException和RemoteException
Service异常终止或者被系统杀掉后会抛出DeadObjectException,binder的IPC过程中如果在server端发生异常抛出,client端这边也会有RemoteException,客户端在调用服务端的接口的过程中,在需要时要注意捕获这两个异常。捕获后一般意味着远程对象已经不可用了,died或异常无法继续运行下去,因此在catch后一般会重新启动服务,或重新再调一遍接口来保证高可用性。
2.3 利用bindService实现进程间通信
前台(Foreground)和后台(Background)进程要实现双向通信,即相互传输一些数据或命令,在Android上并无现成的拿来可用的框架。一个解决方案是利用service,在前台和后台进程中各创建一个service,它们两个之间互相bind。同时,前台和后台进程各自有一个transfer和handler,用来发送和接收数据。
流程简述如下:
1)前台进程的Application中bindService启动BackService。
2)在onServiceConnected中ForeTransfer发送一个启动后台服务的命令START。
3)后台进程的BackHandler通过BackService收到该命令后,bindService启动ForeService。这样前后台进程都有一个service bind到对方上。
4)后台进程的模块A要向前台进程的模块B发送数据,就通过BackTransferàBackService àForeService àForeHandler,被ForeHandler收到,模块B在ForeHandler中实现自己收到数据的处理函数即可。
5)前后进程的Handler中也可以注册回调函数,告知Transfer数据是否已发送处理完,这是因为binder调用是同步的,所以整个通信过程也是同步的。
- 嵌入式企鹅圈原创团队由阿里、魅族、nvidia、龙芯、炬力、拓尔思等资深工程师组成。百分百原创,每周两篇,分享嵌入式、Linux、物联网、GPU、Android、自动驾驶等技术。欢迎扫码关注微信公众号:嵌入式企鹅圈,实时推送原创文章!
Android Service使用拾遗[阿里工程师分享]的更多相关文章
- 阿里资深工程师分享支付宝热补丁技术—— AndFix原理
本文由嵌入式企鹅圈原创团队成员.阿里资深工程师Hao分享. 上次我们介绍了用dexposed方案实施热补丁的原理,它本质上就是hook要修改的函数,这样一来在正式版本发布时就不能直接拿热补丁的代码集成 ...
- 阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路
本文原始内容由作者“阳振坤”整理发布于OceanBase技术公众号. 1.引言 OceanBase 是蚂蚁金服自研的分布式数据库,在其 9 年的发展历程里,从艰难上线到找不到业务场景濒临解散,最后在双 ...
- 转:android service总结2
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...
- android service两种启动方式
android service的启动方式有以下两种: 1.Context.startService()方式启动,生命周期如下所示,启动时,startService->onCreate()-> ...
- 1、Android Studio集成极光推送(Jpush) 报错 java.lang.UnsatisfiedLinkError: cn.jpush.android.service.PushProtoco
Android studio 集成极光推送(Jpush) (华为手机)报错, E/JPush: [JPushGlobal] Get sdk version fail![获取sdk版本失败!] W/Sy ...
- Android Service完全解析,关于服务你所需知道的一切(下)
转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要 ...
- Android Service完全解析,关于服务你所需知道的一切(上)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...
- android service 的各种用法(IPC、AIDL)
http://my.oschina.net/mopidick/blog/132325 最近在学android service,感觉终于把service的各种使用场景和用到的技术整理得比较明白了,受益颇 ...
- Android service介绍和启动方式
1.Android service的作用: service通常是用来处理一些耗时操作,或后台执行不提供用户交互界面的操作,例如:下载.播放音乐. 2.Android service的生命周期: ser ...
随机推荐
- DbUtils使用例子
DbUtils: JDBC Utility Component Examples This page provides examples that show how DbUtils may be us ...
- Rendering Path
Rendering Path:渲染路径 设置:1.Player Setting,2.Camera(会覆盖PlayerSetting中的设置) 选择:根据渲染内容和目标平台来选择合适的Rendering ...
- 设置函数环境——setfenv
当我们在全局环境中定义变量时经常会有命名冲突,尤其是在使用一些库的时候,变量声明可能会发生覆盖,这时候就需要一个非全局的环境来解决这问题.setfenv函数可以满足我们的需求. setfenv(f, ...
- C10K问题渣翻译
The C10K problem [Help save the best Linux news source on the web -- subscribe to Linux Weekly News! ...
- DDD:Strategic Domain Driven Design with Context Mapping
Introduction Many approaches to object oriented modeling tend not to scale well when the application ...
- spring websocket源码分析
什么是websocket? 摘录于wiki[1]: WebSocket is a protocol providing full-duplex communication channels over ...
- iOS-App的启动页设置
一. 要求 1. 把一张图片设置成为启动页面. 二. 准备工作 1. 把准备的适配的(@1x,@2x,@3x)三张图片拖进工程中. 2. 打开LaunchScreen.storyboard. 在页面上 ...
- 开放产品开发(OPD):产品负责人的工作原则和方法
月26日我将在2014 WOT全球软件技术峰会做相关的一个主题演讲[产品负责人的工作原则和方法],个原则和相应的一些方法. 以下是本次分享内容: 完整版如下,如果你喜欢想下载的话,点击 http:// ...
- Android 学习笔记之使用多线程实现断点下载...
PS:莫名其妙的迷茫... 学习内容: 1.使用多线程实现文件下载... 多线程下载是加快下载速度的一种方式..通过开启多个线程去执行一个任务..可以使任务的执行速度变快..多线程的任务下载时常都 ...
- [ASP.NET]分析MVC5源码,并实现一个ASP.MVC
本节内容不是MVC入门教程,主要讲MVC原理,实现一个和ASP.NET MVC类似基本原理的项目. MVC原理是依赖于ASP.NET管道事件基础之上的.对于这块,可阅读上节内容 [ASP.NET]谈谈 ...