自我提升(基础技术篇)——service、intentService、远程service

前言,服务这东西,说实在的,个人用得不太多,这个玩意主要是做后台服务的,而先在市面上的app大多数都是以界面显示和交互为主的,不过,毕竟是Android的重要组件,所以还是有必要学习一番的

Serivce(服务)

简单介绍

服务是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

开启服务的方式

启动

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

绑定

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

小总结一下

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

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

注意:服务在其托管进程的主线程中运行,它既创建自己的线程,也在单独的进程中运行(除非另行指定)。 这意味着,如果服务将执行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或联网),则应在服务内创建新线程来完成这项工作。通过使用单独的线程,可以降低发生“应用无响应”(ANR) 错误的风险,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互。(这里,简单一句话,服务也是会阻塞ui线程的)

service的基本API

要创建服务,您必须创建Service的子类(或使用它的一个现有子类,简单来说,不能直接使用service)。在实现中,您需要重写一些回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务。 应重写的最重要的回调方法包括:

onStartCommand()

当另一个组件(如 Activity)通过调用startService()请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用stopSelf()或stopService()来停止服务。

onBind()

当另一个组件想通过调用bindService()与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,您必须通过返回IBinder提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。

onCreate()

首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用onStartCommand()或onBind()之前)。如果服务已在运行,则不会调用此方法。

onDestory()

当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。

小总结一番

如果组件通过调用startService启动服务(这会导致对onStartCommand的调用),则服务将一直运行,直到服务使用stopSelf自行停止运行,或由其他组件通过调用stopService停止它为止。

如果组件是通过调用bindService来创建服务(且调用onStartCommand,则服务只会在该组件与其绑定时运行。一旦该服务与所有客户端之间的绑定全部取消,系统便会销毁它。

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

在下文中,讲述如何创建各类服务以及如何从其他应用组件使用服务。

申明服务(注册服务)

如同 Activity(以及其他组件)一样,您必须在应用的清单文件中声明所有服务。

要声明服务,请添加元素作为元素的子元素。例如:


但是,这是一个最简单的服务申明了,不过我们实际应用中,通常会添加一些属性,就像广播和activity那样


这是一个完整的service,里面有很多属性,下面我尽可能简单的介绍这些属性吧

android:name

这实现了服务类的名称。这应该是一个完全限定类名。不过,地球人知道,用.代替前面那一大堆

android:description

描述用户的服务。标签应该被设置为对字符串资源的引用,这样它就可以像用户界面中的其他字符串那样本地化。

android:directBootAware

服务是否是直接引导感知的,也就是说,它是否可以在用户解锁设备之前运行。注意:在直接引导过程中,应用程序中的服务只能访问存储在设备保护存储中的数据默认值为“false”。

android:enabled

服务是否可以由系统实例化,如果可以,则为“true”,如果没有,则为“false”。默认值为“true”。元素有自己的启用属性,它适用于所有应用程序组件,包括服务。对于要启用的服务,<应用>和< > >属性必须是“true”(因为它们都是默认的)。如果两者都是“false”,则禁用该服务;它不能被实例化。

android:exported

其他应用程序的组件是否可以调用该服务或与其交互,如果可以,则为“true”,如果没有,则为“false”。当值为“false”时,只有同一应用程序或具有相同用户ID的应用程序的组件才能启动该服务或绑定到它。默认值取决于服务是否包含意图过滤器。没有任何筛选器意味着只能通过指定其确切类名来调用它。这意味着该服务仅用于应用程序内部使用(因为其他人不知道类名)。所以在这种情况下,默认值为“假”。另一方面,至少有一个过滤器的存在意味着该服务是为外部使用,所以默认值是“true”。此属性不是限制服务对其他应用程序的暴露的唯一方法。您还可以使用权限来限制可以与服务交互的外部实体。

android:icon

表示服务的图标。这个属性必须设置为参考,包含图像定义的可绘制资源。如果没有设置,则使用整个应用程序指定的图标。服务的图标——无论是在这里设置还是在应用程序元素中——都是所有服务的意图过滤器的默认图标。

android:isolatedProcess

如果设置为true,此服务将在与系统其他部分隔离的特殊进程下运行,并且没有自己的权限。与它的唯一通信是通过服务API(绑定和启动)。所以默认设置为false

android:label

可以显示给用户的服务的名称。如果不设置此属性,则将整个应用程序设置的标签改为使用。服务的标签——无论是在这里设置的还是应用程序>元素——都是所有服务的意图过滤器的默认标签。标签应该被设置为对字符串资源的引用,这样它就可以像用户界面中的其他字符串那样本地化。然而,在开发应用程序时,作为一个方便,它也可以被设置为一个原始字符串。

android:permission

为了启动服务或绑定到某个实体而必须具有的权限的名称。如果调用的startservice(),bindservice(),或stopservice(),没有被授予该权限,该方法将不工作,Intent对象将不会被发送给服务。如果未设置此属性,则应用程序元素的权限属性设置的权限应用于服务。如果不设置这两个属性,则服务将不受权限保护。

android:process

服务是运行的进程的名称。通常,应用程序的所有组件都运行在为应用程序创建的默认进程中。它与应用程序包相同的名称。元素的过程属性可以为所有组件设置不同的默认值。但是组件可以用自己的进程属性重写默认值,允许您跨多个进程传播应用程序。如果分配给该属性的名称以冒号(“:”)开头,则在应用程序需要时创建一个新进程,并在该进程中运行服务。如果进程名以小写字符开头,则服务将在该名称的全局进程中运行,只要它有权限这样做。这允许不同应用程序中的组件共享进程,从而减少资源使用。


但是,,,其实,我们平时根本用不到这么多属性,这里只是简单介绍,如果真的使用,还需要大家自己去摸索,下面还是用实例说话吧。

IntentService介绍和使用

说到启动服务,先聊点东西,我们大多数人都是用service,但是,却很少有人用一个叫做IntentService的东西,这个东西真的是一个不错的东西,简化操作,而且自己处理了很多东西。好吧,那下面就来介绍一下这个intentService吧。

首先:IntentService是Service的子类

然后:由于大多数启动服务都不必同时处理多个请求(实际上,这种多线程情况可能很危险),因此使用IntentService类实现服务也许是最好的选择

那么,intentservice都干了些什么呢?(本来想上源码的,结果发现看不了,所以就算了),直接出结论吧:

创建默认的工作线程,用于在应用的主线程外执行传递给onStartCommand的所有 Intent。

创建工作队列,用于将 Intent 逐一传递给onHandleIntent实现,这样您就永远不必担心多线程问题。

在处理完所有启动请求后停止服务,因此您永远不必调用stopSelf

提供 onBind()的默认实现(返回 null)。

提供 onStartCommand的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent实现。

ok,下面让我们来用一次吧,毕竟实践出真知嘛,这里就直接截图了

首先是我们的intentservice类



然后是Activity,看具体怎么操作吧



嗯,看一下xml吧,很简单的e


最后别忘了,service是需要注册的:

不解释太多,自己敲一遍就知道了。

ok,intentservice就介绍到这里,下次,再补充service的东西

Service的基本用法

启动服务的方法和启动活动很类似,都需要借助意图来实现,下面我们就通过一个具体的例子来看一下。

首先我们构造一个服务,当然,别忘记注册。


然后在前面的例子中添加新监听


然后分别做停止服务和启动服务的action,这里的action0就是一个接口里面就是一个call方法。

结果,如下

相信,看到这里,已经大致知道service是怎么启动和关闭的了吧(不管底层实现前提下),但是,这里面那个onBind方法好像没有用到,对不对。那么接下来,就来聊聊这个onBind方法。

说这个方法之前,先来说说为什么有有个方法。如果,service只有上面那一个方法,那么,我们的activity,就只是通知service,告诉他,你可以开始了,或者停止了。如果,需要二者交互通信的话,之前的广播也是一种方法,但那样做是不是太麻烦了呢?有没有一种更简单,更快速的方法呢?答案当然是有的,毕竟google那么牛逼不是。ok,下面就讲讲activity和service交互的问题。简单来说,就是activity打电话给service叫他干什么什么,然后service干完之后回个电话,activity老大,我干完了。这就是交互。

service和activity的交互

接着上面的代码,加新的东西


然后,就到我们的activity中去控制和接收吧。

ok,试一下绑定不解绑吧:

绑定,会走creat,bind和去执行我们写的方法。

解绑,这里就销毁了这个service

那么现在我们加上前面的start方法和stop方法再来看看


我们看到,当我们start和bind都点击的时候,实际上,是走了3个系统的方法和我们自己写的方法

那么我们想要销毁的时候,就必须stop一下,和unbind一下。

同时,我们结合前面的日志可以看出来,在onbind方法时,并没有走onstartcommond方法。所以,得出一个结论,onstratcommond方法,是在调用stratService方法时执行,onbin方法,是在执行bindservices的时候执行。

这也说明了启动service有两种方法,一种是直接start,一种bind。

ok,,之前一直误解了线程和服务的关系。因为大家都说,他们是后台的。好吧,那就讲讲服务和线程的关系吧。

Service和Thread的关系

这里,只有一句总结的话,,,

那就是,tm的,这两兄弟,没有任何关系。

是不是有一种艹的感觉。没错,我了解到这里的时候,也有这个感觉。

service是执行在主线程的东西,所以说,service准确来说,是执行在主线程的一个无界面活动。service可以理解为一个没有界面的活动。活动,大家应该就知道了吧,如果你对activity说你不知道,ok,那你别学Android了。

所以,简单说一下,耗时操作统统放到线程执行,不需要界面的活动,就是service的事儿了。

前台service

是不是想吐槽,不是才说了service是在后台吗?怎么还有前台的?别激动,这里的前台service,和我们正常的service并没有多大不同,其实,只是一个标识吧。就是告诉Android系统老大,我是前台service,所以,我的优先级要高一点,不要随随便便就把我回收了。ok,就是这一点不同罢了。当然,这个标识,是以通知的形式存在的,别问我为什么,系统就是这么干的,我有什么办法。

这个真没什么好说的,直接上代码,敲一遍试试吧。

接着前面代码:

远程service(remote)

这一部分讲一个高端大气的新东西,不过用得,怎么说呢?个人用得并不多。

ok,正题。

先解释一波

嗯,我试着用最通俗的方式解释这个东西。这里的“远程”,的确很远,远到什么程度呢?爬到其他进程去了。本来我们一个应用(app),cpu会为他开一个进程,然后所有这个app的东西全在这一个进程中跑(先不要去研究进程之类的,先这么理解),而远程service,他的所有东西,就不在这个进程中跑了,而是到了其他进程了。

打个比方,就像我们看电影,或者电视中有些坏蛋,在国内犯了法,就逃到国外一样。我们就不能直接去管,去抓了。远程service就是这样的。

那么我们是不是对那些坏蛋就束手无策,任由他们逍遥法外呢?当然不行,这时候,我们就联系国际刑警,让国际刑警去处理,这样就实现了跨缉犯的事情了。(大致是这样)这里的跨国缉拿,就是我们常说的IPC(跨进程通信),这里的国际刑警就是我们说的AIDL(Android Interface Definition Language)。

好了,基本的解释,应该能看懂了,下面我们实际操作一下。

首先,我们验证一下,普通service和远程service所在线程的问题:(还是接着之前的代码,写)




分别启动普通的service和远程service,看日志


结果一目了然,就是不一样。

进程id不一样,而且,进程的包名都不一样,不过,相信,你观察一下就会发现,其实这个远程service的包名和普通service的包名看起来是有关系的(至少看起来),那这之间是不是有什么玄机呢?别急,往后读。

我们现在只是看到了这二者的区别,现在,要说说这个远程service得怎么用才行。

首先,我们需要一个国际刑警的组织(AIDL)

创建AIDL



使用AIDL


然后,就是配置清单文件了

远程service的属性配置

这里说一下,为什么要加action呢,因为跨进程通信时,别的进程又不知道这个Class,所以,只能通过类似与action的东西作为标识了。

最后一步,就是启动这个service干事情了,这里用bind方法:

绑定远程service


最后实验结果:


好了,关于service相关的,就算是讲完了。篇幅有点长,耐心看,收获会不少

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,376评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 1.什么是Activity?问的不太多,说点有深度的 四大组件之一,一般的,一个用户交互界面对应一个activit...
    JoonyLee阅读 5,724评论 2 51
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-androi...
    eddy_wiki阅读 3,243评论 0 20
  • 最近在看动画的使用。在看Android官方文档时,看到触摸反馈的文章,就自己动手实验下效果,顺便记录下使用方法。 ...
    space0o0阅读 2,131评论 0 1