2019-01-22 关于Service的学习 记录

总体简介

服务是一种应用程序组件,表示应用程序希望在不与用户交互的情况下执行较长时间运行的操作,或者为其他应用程序提供使用的功能。每个服务类必须在AndroidManifest.xml 声明<service> 。可以使用Context.startService()和 启动服务 Context.bindService()。服务可由其他应用组件启动而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信(IPC)。例如,服务可以处理网络事务、播放音乐,执行文件I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

请注意,服务与其他应用程序对象一样,在其托管进程的主线程中运行。这意味着,如果您的服务要进行任何CPU密集型(例如MP3播放)或阻止(例如网络)操作,它应该生成自己的线程来执行该工作。有关此内容的更多信息,请参见 进程和线程。该IntentService类是作为一个标准的实施服务的,有它自己的线程在哪里做它安排其工作。 (ps:服务是在主线程运行的,耗时操作要在子线程操作。)

服务基本上分为两种形式:

启动

        当应用组件(如Activity)通过调用 startService()启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。操作完成后,服务会自行停止运行。

绑定

        当应用组件通过调用bindService()绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信(IPC)跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁

        虽然本文档是分开概括讨论这两种服务,但是您的服务可以同时以这两种方式运行,也就是说,它既可以是启动服务(以无限期运行),也允许绑定。问题只是在于您是否实现了一组回调方法:onStartCommand()(允许组件启动服务)和onBind()(允许绑定服务)。

        无论应用是处于启动状态还是绑定状态,抑或处于启动并且绑定状态,任何应用组件均可像使用Activity那样通过调用Intent来使用服务(即使此服务来自另一应用)。不过,您可以通过清单文件将服务声明为私有服务,并阻止其他应用访问。 使用清单文件声明服务部分将对此做更详尽的阐述。

什么是服务?

服务不是一个进程,也不是一个线程

服务提供以下两个功能(ps:还是围绕startService和bindService来说的)

 1、应用软件的一个组件,用来告诉系统,他想在后台做什么事情,对应startService启动方式,他要求系统为服务安排工作,直到被显式的停止。

2、应用程序向其他应用程序公开其功能的一种工具。这对应于对Context.bindService()的调用,该调用允许对服务进行长期连接,以便与之交互。

实例化服务实际上就是在主线程调用服务的onCreate方法,以及其他适当的回调,就是说,服务是运行在主线程滴,耗时操作自觉去建立子线程,避免ANR。

请注意,因为Service本身非常简单,所以您可以根据需要使它与它的交互变得简单或复杂:从将其视为本地Java对象,您可以直接进行方法调用(如本地服务示例所示),提供使用AIDL的完整远程接口。

生命周期

系统可以运行服务有两个原因。如果有人调用,Context.startService()那么系统将检索服务(创建它,并在需要的时候调用他的onCreate方法),然后onStartCommand(Intent, int, int)使用客户端提供的参数调用其方法。此服务将在此时继续运行,直到Context.stopService()或被 stopSelf()调用。请注意,对Context.startService()的多次调用不会嵌套(尽管它们会导致多次对onStartCommand()的相应调用),因此无论启动多少次,一旦调用Context.stopService()或stopSelf将停止服务(); 但是,服务可以使用它们的stopSelf(int)方法来确保在处理启动的意图之前不停止服务。(最后一句懵懂,大概是说,使用stopSelf(int)可以更安全的停止服务。)

有一个stopSelfResult的方法。如果您使用最近接收到的ID调用此函数,而在调用此函数之前还没有调用它之前已接收到的ID,则服务将立即停止。如果您可能无法按顺序处理id(例如在不同的线程上调度id),那么您有责任按照接收id的顺序停止(懵逼。。。)

上面说半天只说了sartService的生命周期,还是看下面的图计较全:


一个服务可以同时使用startService和bindService。在这种情况下,只要系统启动Context.BIND_AUTO_CREATE 标志有一个或多个连接 ,系统就会保持服务运行。一旦这些情况都不成立,onDestroy()就会调用服务的方法并有效终止服务。从onDestroy()返回时,应完成所有清理(停止线程,取消注册接收器)。

权限

在清单<service> 标签中声明服务时,可以强制执行对服务的全局访问。通过这样做,其他应用程序将需要<uses-permission> 在其自己的清单中声明相应的 元素,以便能够启动,停止或绑定到服务。

截至Build.VERSION_CODES.GINGERBREAD使用时 Context.startService(Intent),您还可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION使用Intent。这将授予服务对Intent中特定URI的临时访问权限。在服务调用stopSelf(int)该启动命令或稍后启动命令或服务已完全停止之前,访问将保持不变。这适用于授予对未请求保护服务权限的其他应用程序的访问权限,或甚至根本不导出服务的权限。

此外,服务可以通过ContextWrapper.checkCallingPermission(String) 在执行该调用的实现之前调用该方法来保护具有权限的单个IPC调用 。

有关 权限和安全性的更多信息,请参阅“ 安全和权限”文档。

流程生命周期

只要服务已启动或客户端绑定服务,Android系统就会尝试保持托管服务的进程。当内存不足并且需要杀死现有进程时,托管服务的进程的优先级将是以下可能性中的较高者:

1.如果服务当前正在其onCreate(),, onStartCommand()onDestroy()方法中执行代码 ,那么托管进程将是一个前台进程,以确保此代码可以执行而不会被杀死。

2.如果服务已经启动,则其托管过程被认为不如屏幕上用户当前可见的任何进程重要,但比任何不可见的进程更重要。由于用户通常只能看到少数几个进程,这意味着除了内存不足之外,不应该杀死该服务。但是,由于用户没有直接了解后台服务,因此在该状态下它 认为是有效的候选者,你应该为此做好准备。特别是,长期运行的服务将越来越可能被杀死,并且如果它们保持足够长的时间,则可以保证被杀死(如果合适,可以重新启动)。

3.如果有客户端绑定到该服务,那么该服务的托管进程永远不会比最重要的客户端重要。也就是说,如果其客户端之一对用户可见,则认为服务本身是可见的。该方法在客户端的重要性,影响到服务的重要性,可通过调整Context.BIND_ABOVE_CLIENT, Context.BIND_ALLOW_OOM_MANAGEMENTContext.BIND_WAIVE_PRIORITY, Context.BIND_IMPORTANT,和Context.BIND_ADJUST_WITH_ACTIVITY

4.启动的服务可以使用startForeground(int, Notification) API将服务置于前台状态,其中系统认为它是用户主动意识到的东西,因此在内存不足时不是候选者。(从理论上讲,服务在当前前台应用程序的极端内存压力下被杀死仍然是可能的,但实际上这不应该是一个问题。)

请注意,这意味着大多数情况下您的服务正在运行,如果系统处于严重的内存压力下,它可能会被系统杀死。如果发生这种情况,系统稍后将尝试重新启动该服务。这样做的一个重要结果是,如果您实现onStartCommand() 调度工作要异步完成或在另一个线程中完成,那么您可能希望让START_FLAG_REDELIVERY系统为您重新提供一个Intent,这样如果您的服务是在处理它时被杀死。

同一进程中的其他应用组件,如activity,可以总价总体进程的重要性,而不仅仅是service本身的重要性。

仅当内存过低必须回收系统资源以供前台 Activity 使用时,系统才会强制停止服务。如果将服务绑定到前台 Activity,则它不太可能会终止,如果将服务声明为在前台运行,则它几乎永远不会终止。或者,如果服务已启动并要长时间运行,则系统会随着时间的推移降低服务在后台任务列表中的位置,而服务也将随之变得非常容易被终止。如果服务是启动服务,则必须将其设计为能够妥善处理系统对它的重启。 如果系统终止服务,那么一旦资源变得再次可用,系统便会重启服务(这还取决于 onStartCommand() 的返回值)


关于startService和bindService同时使用的情况

当同时使用时,即使左右的activity都取消了绑定,也不能停止service,只能通过显示的调用,如stopService(),或者stopSelf()去停止。推荐使用stopSelf(int ),可以更加安全的停止服务,里面的int就是onStartCommand中的startId,他会自己赋值的,每次调用都会加1。在service里面写一个公共方法代用stopSelf(int ),如下图:


更多:https://www.jianshu.com/p/95ec2a23f300

示例代码地址:https://github.com/CHhuHa/serviceStudyCode  源码有部分代码是多余的,

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容