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. java与javac版本不一致问题

    问题描述:    设置了环境变量JAVA_HOME为jdk1.5.0_14的安装目录,并且在PATH变量中加入了%JAVA_HOME%\bin,但在Windows命令行下,执行java -versio ...

  2. Linux - 获取Shell命令帮助信息

    Manual Page Chapter List 1:所有用户可以操作的指令或可执行文件 2:系统核心调用的函数与工具 3:子调用,常用的函数与函数库 4:设备,硬件文件说明,通常是/dev/的文件 ...

  3. [git]解决:git config --global push.default matching

    解决:git config --global push.default matching 这个警告的意思是:需要设置默认push的分支,所以设置好全局push的默认分支就好了.命令如下: 在有git目 ...

  4. suricata抓包方式之一 AF_PACKET

    1.前言 linux提供了原始套接字RAW_SOCKET,可以抓取数据链路层的报文.这样可以对报文进行深入分析.今天介绍一下AF_PACKET的用法,分为两种方式.第一种方法是通过套接字,打开指定的网 ...

  5. 用Qt写软件系列二:QCookieViewer(浏览器Cookie查看器)

    预备 继上篇<浏览器缓存查看器QCacheViewer>之后,本篇开始QCookieViewer的编写.Cookie技术作为网站收集用户隐私信息.分析用户偏好的一种手段,广泛应用于各大网站 ...

  6. 可编译为 UNICODE 和 ANSI 版本的遍历目录树程序_0.1

    路径暂时是写死的 编译两个版本的程序: g++  treeT.cpp -municode -D_UNICODE -o treeT_UNIg++  treeT.cpp -o treeT_ASC 为了观察 ...

  7. P6 Professional Installation and Configuration Guide (Microsoft SQL Server Database) 16 R1

    P6 Professional Installation and Configuration Guide (Microsoft SQL Server Database) 16 R1       May ...

  8. 交通银行 Java Socket 服务启动 管理 WINDOWS 版

    按照交通银行提供的无界面启动方法试验了很多次,都没有成功,所以自己动手用C# 知识写了一个. 小工具可以判断 交通银行 JAVA SOCKET 服务是否启动,并可以启动/关闭服务 主要代码如下: 判断 ...

  9. PHP框架中最喜欢的WindFramework

    题外话, 像我这样从小到大作文打0分居多的人,写文章,实在是没有耐心的,抱歉. 尽管自己也山寨过许多PHP框架,但被山寨的对象中,最喜欢的是WindFramework. Yii其实更好,但太大而全. ...

  10. [moka摘录]查看邮件是否已被阅读

    原文地址:http://www.php100.com/html/php/hanshu/2013/1101/6347.html 查看邮件是否已被阅读 当你在发送邮件时,你或许很想知道该邮件是否被对方已阅 ...