这篇旨在复习service的基础知识,就是把之前在知乎上写的笔记差不多复习了一下,把文章搬运了过来
要点提炼
- 一个类 extends Serivce 并在AM中注册<service>标签
- 生命周期三个回调
第一次创建onCreate()
每次启动onStartCommand
每次终止onDestroy
- 在活动中可以
构造intent
+Context下的startService
+stopService
来启动和终止服务,在Service中可以stopSelf
自行终止服务- 活动和服务通信通过Binder对象,核心在于service中的
onBind
方法,需要在服务中建一个类extends Binder,里面加一些能够控制服务的方法,onBind方法返回这个类的对象- 在活动中通过实现
ServiceConnection
这个类中方法提供的参数获取上述的Binder对象,通过Binder对象来完成服务和活动之间的通信。intent
+bindService()
+unbindService
是活动和对应的服务绑定- 最后是
前台服务
和IntentService
服务,让程序在后台运行,适合去执行不需要和用户交互但是要求长期运行的任务。服务的运行不依赖于任何用户界面,也不运行在独立的进程中,而是依赖于创建服务时所在的应用程序进程。使用服务需要用到我们Android多线程编程的一些知识。
定义服务
新建一个ServiceTest项目,New个service出来:
![](https://img.haomeiwen.com/i13852523/23bd37e0272cfa25.jpg)
Exproted表示是否允许其他程序访问这个服务,Enabled表示是否启用这个服务。生成的代码是这样的:
![](https://img.haomeiwen.com/i13852523/9cecc33cffc51388.jpg)
里面自动生成了构造器,然后还有个onBind
方法,这是Service中唯一一个抽象方法,我们后面会介绍。定义一个服务要处理一些事情,就需要重写一些方法:
![](https://img.haomeiwen.com/i13852523/a6b31ec9eb8b3ca9.jpg)
这是常见的重写的几个方法。服务属于四大组件之一,我们还需要在AM中去注册一下,当然在new Service那一步里面,AS已经智能的完成了这一步:
![](https://img.haomeiwen.com/i13852523/570759ba5dda90a5.jpg)
启动和停止服务
启动和停止主要借助Intent实现。我们实践一下,修改ServiceTest项目,布局如下:
![](https://img.haomeiwen.com/i13852523/09405fde8ac5f183.jpg)
然后点击事件:
![](https://img.haomeiwen.com/i13852523/da4c7dbdab2689ae.jpg)
两个Intent,然后分别调用startService
和stopService
,这两个方法都是Context
类的,这里我们可以直接调用,由活动来决定服务的开始与终止,而另外,也可以在MyService的任何地方使用stopSelf()
方法,来让服务自行终止。为了能看到效果,我们在MyService里面重写方法,用Log.d打印一下方法执行了的结果:
![](https://img.haomeiwen.com/i13852523/41954cc34b14e1ec.jpg)
运行如下:
![](https://img.haomeiwen.com/i13852523/0aecbf1d230e9e34.jpg)
Logcat界面:
![](https://img.haomeiwen.com/i13852523/20003f80735c0bb8.jpg)
依次点了start,start,start,stop,start。结果符合预期。
活动和服务进行通信
上面这个例子中,就是在活动里面startService
了一下,启动服务的活动和服务之间没有多大的关系,如果想在活动中指挥服务去干什么,要借助Service
类的onBind
方法。
这里我们希望在MyService里面提供一个下载功能,并且在活动中决定何时开始下载,以及随时查看进度。修改MyService代码如下:
![](https://img.haomeiwen.com/i13852523/5008bc62f49f02ec.jpg)
新建了一个内部类,继承自Binder
,里面简单写了两个方法,模拟开始下载和获取进度。并且新建了这个类的实例,并且在onBind
方法中返回这个实例。下面在布局里面新增两个按钮:
![](https://img.haomeiwen.com/i13852523/0052ab869016d716.jpg)
用于把活动绑定和解绑服务。绑定之后,活动就可以调用Binder提供的方法了。修改活动:
![](https://img.haomeiwen.com/i13852523/376ec5ab5cc7472e.jpg)
![](https://img.haomeiwen.com/i13852523/6ad42e80bd9bed54.jpg)
创建了ServiceConnection
匿名类,重写了两个方法,从名字上也能看出来是绑定建立和绑定取消的时候会被调用的。onServiceConnected
方法里面,向下转型得到了DownloadBinder实例,有这个实例之后,就可以通过它在活动中调用一些DownloadBinder的public方法,实现所谓的在活动中指挥服务,让活动和服务的联系紧密了一些。我们这里调用了两个方法。当然还没有完成绑定,在按钮点击事件里面,还是用Intent,调用了bindService
方法,参数依次是intent,connection还有BIND_AUTO_CREATE,第三个参数是一个标志位,这个BIND_AUTO_CREATE表示活动和服务绑定之后自动创建服务,会使得服务中的onCreate
方法执行,但是onStartCommand
不会执行,解绑按钮中调用unbindService
即可。
运行之后,先bind再unbind,并且查看logcat:
![](https://img.haomeiwen.com/i13852523/5ebb7008dd2bc475.jpg)
服务的生命周期
服务和活动一样,也有自己的生命周期,各个生命周期内涉及的回调方法有onCreate(),onStartCommand(),onBind()
和onDestroy()
startService
方法调用之后,服务就会启动起来,回调onStartCommand
方法,没有创建过的话,onCreate
会被先行执行。当调用stopService
或者stopSelf
方法之后,onDestroy
方法会被执行。每startService
一次,都会执行一次onStartCommand
,但是每个服务只会存在一个实例,最终只需要一次stop服务就会停止了。
另外的onBind
方法,是在bindService
方法执行之后调用,把onBind
的返回结果交给匿名类的IBinder
参数,就是上一节演示的那样。而如果第一次创建,onCreate
方法会先执行,只要没有unbindService
方法,服务就会一直运行。
而有一种情况,可能既startService
了,又bindService
了,又有当stopService
(或stopSelf
)和unbindService
同时调用了,才会onDestroy
。
这就是服务的生命周期。
使用前台服务
服务几乎在后台运行,当系统内存不足时候,就可能回收掉正在后台运行的服务。但是如果想要服务一直保持运行状态,不因为内存不足被回收,就要使用前台服务。前台服务会有一个正在运行的图标一直显示在系统状态栏类似于通知的效果。现在用的这个华为运动大概就属于前台服务吧。
修改MyService如下:
![](https://img.haomeiwen.com/i13852523/6e3c788d80c4b558.jpg)
几乎和通知那块没什么两样,但是最后不是使用NotificationManager将通知显示出来,而是调用startForeground
方法,两个参数,第一个是id,第二个是构建出来的Notification对象。
![](https://img.haomeiwen.com/i13852523/ede74a7e0f6f2d10.jpg)
这个通知划也划不掉。
使用IntentService
上面我们所有服务相关代码全部写在主线程中,如果处理耗时逻辑的话,就容易出现ANR(Application Not Responding)的情况。应该在子线程中处理这些事情。一般像这样写,比较标准的服务:
![](https://img.haomeiwen.com/i13852523/961ed68605e89c66.jpg)
里面调用
stopSelf
是为了让在执行完任务之后让服务停止下来。
而Android同时还提供了一个IntentService
简化这些操作。新建一个MyIntentService继承自IntentService:
![](https://img.haomeiwen.com/i13852523/6fccc20f8f4e16e8.jpg)
我们重写了一下onHandleIntent
方法,这个方法就是运行在子线程中的,就是在这个方法里面处理耗时逻辑的。我们打印了一下当前线程的id来验证这个确实是子线程。构造器里面调用了一下超类的有参数的构造器。最后onDestroy
里面打印一下销毁执行了。然后按钮里面加一个启动MyIntentService的按钮:
![](https://img.haomeiwen.com/i13852523/d5c94229bee52e4a.jpg)
修改活动代码:
![](https://img.haomeiwen.com/i13852523/0a3db5cfef76e5f2.jpg)
![](https://img.haomeiwen.com/i13852523/4db75cf5609e0961.jpg)
启动还是用Intent
,然后也不要忘了AM里面申请一下服务(其实如果用AS的New里面的intentService,会帮助我们自动申请,但是自动的会生成一些现在用不到的代码,所以就手动创建手动申请了):
![](https://img.haomeiwen.com/i13852523/31acecc3bdee7bd7.jpg)
可见并不是一个线程,同时onHandleIntent
执行完毕之后还自动stop服务了。
网友评论