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使用拾遗[阿里工程师分享]的更多相关文章

  1. 阿里资深工程师分享支付宝热补丁技术—— AndFix原理

    本文由嵌入式企鹅圈原创团队成员.阿里资深工程师Hao分享. 上次我们介绍了用dexposed方案实施热补丁的原理,它本质上就是hook要修改的函数,这样一来在正式版本发布时就不能直接拿热补丁的代码集成 ...

  2. 阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路

    本文原始内容由作者“阳振坤”整理发布于OceanBase技术公众号. 1.引言 OceanBase 是蚂蚁金服自研的分布式数据库,在其 9 年的发展历程里,从艰难上线到找不到业务场景濒临解散,最后在双 ...

  3. 转:android service总结2

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

  4. android service两种启动方式

    android service的启动方式有以下两种: 1.Context.startService()方式启动,生命周期如下所示,启动时,startService->onCreate()-> ...

  5. 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 ...

  6. Android Service完全解析,关于服务你所需知道的一切(下)

    转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要 ...

  7. Android Service完全解析,关于服务你所需知道的一切(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

  8. android service 的各种用法(IPC、AIDL)

    http://my.oschina.net/mopidick/blog/132325 最近在学android service,感觉终于把service的各种使用场景和用到的技术整理得比较明白了,受益颇 ...

  9. Android service介绍和启动方式

    1.Android service的作用: service通常是用来处理一些耗时操作,或后台执行不提供用户交互界面的操作,例如:下载.播放音乐. 2.Android service的生命周期: ser ...

随机推荐

  1. ios auto layout demystified (一)

    Ambiguous Layout 在开发过程中,你可以通过调用hasAmbiguousLayout 来测试你的view约束是否足够的.这个会返回boolean值.如果有一个不同的frame就会返回ye ...

  2. CentOS linux系统搭建LAMP环境

    准备工作: 1.配置防火墙,开启80端口.3306端口vi /etc/sysconfig/iptables-A INPUT -m state –state NEW -m tcp -p tcp –dpo ...

  3. 专门为码农定制的14款创意的T裇(T-Shirt)设计

    T裇衫是人们在各种场合都可穿着的服装,如在T裇衫上作适当的装饰,即可增添无穷的韵味.通过图案直接反映人类的精神风貌,你可以把日常生活中的兴趣.习惯.喜怒哀乐.嗜好等展露无疑,张扬个性.秀出自我.对于码 ...

  4. iOS- NSDateFormatter (自定义时间格式)

    一. NSDateFormatter解释 1. 日期(NSDate)是NSString类的格式(stringWithFormat),也可以改变输出,如果需要输出年代信息等则需要进行转换,等等. 2.  ...

  5. JS魔法堂:追忆那些原始的选择器

    一.前言                                                                                                 ...

  6. Python+Selenium进行UI自动化测试项目中,常用的小技巧2:读取配置文件(configparser,.ini文件)

    在自动化测试项目中,可能会碰到一些经常使用的但 很少变化的配置信息,下面就来介绍使用configparser来读取配置信息config.ini 读取的信息(config.ini)如下: [config ...

  7. android 获取当前位置

    1. Android开发位置感知应用程序方式:1. GPS 定位     精确度高,仅适用于户外,严重消耗电量.如果手机内置GPS接受模块,即使手机处于信号盲区,依然可以获取位置信息. 2. NETW ...

  8. Use the PDFs below or the HTML contents to the left to install and configure P6 EPPM and its additional components.

    Welcome to Your Documentation   Use the PDFs below or the HTML contents to the left to install and c ...

  9. background的属性和背景图片定位的实例

    本文内容: 1.背景图片定位示例 2.background常用的属性值 3.background-repeat新增的round.space属性 4.background-size的属性值(着重介绍co ...

  10. 解决打印机报错:操作无法完成(错误0x00000709)。

    解决:操作无法完成(错误0x00000709).再次检查打印机名称,并确保打印机已连接到... 上午同时说,网络打印机打印不了,于是首先看一下打印服务器IP是不是给换了,结果没换. 接着尝试重新添加一 ...