Android出来这么些年,发现国内程序员关于Android开发的文章写得越来越好了,有的真让人赞叹。不过再好也是别人的,自己只有亲自实践亲自总结,才能夯实基础,提升自我,尽力向大神的水平靠拢。
概述
真正做Android开发算来应该有两年多了,说实话自己写Service
的时候还是比较少的。最近使用到了,刚好对其做个总结,我这边主要侧重于应用层面的,其实是因为我比较菜,深入原理的话怕讲不好。
首先我们应该清楚Service
是Android四大组件之一,下面是官方网站对Service
的定义,非常清楚
Service
是一个可以在后台执行长时间运行而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
服务分为两种形式:
- 启动
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
- 绑定
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
如何使用
对于一项技术首先要清楚什么场景下应该使用,这个技术是不是解决了我们的问题,然后才是如何使用
使用之前要明白Service
有两种使用方式,启动模式与绑定模式,如概述所示,这两种方式适用于不同的使用场景。
启动模式使用方法
这种模式启动的Service
,一旦启动则与启动者毫无关系了,这个服务会一直运行在后台,直到服务完成任务后自己结束自己,或者其他组件主动结束它,或者操作系统强制结束它。
1.声明标签
在AndroidManifest.xml
文件中声明Service
标签,如下面代码所示
<service
android:name=".your.services.name"
android:enabled="true"
android:exported="true">
</service>
其实一个service
只有name
属性是必须的,其他都是可选的。
name: 这个Service
的唯一标识
enabled:此服务是否可以被系统实例化
exported:此服务是否可以被本应用以外的其他应用调起。
下面是其完整的代码清单,具体可参考 Service
<service android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</service>
2.创建服务
创建一个Service
子类(Kotlin代码)
package top.ss007.servicedemo.services
...
class Service1 : Service() {
companion object {
private val TAG = Service1::class.java.simpleName
}
//Service 的抽象方法,子类必须实现,虽然在启动模式下无需写具体实现代码
override fun onBind(intent: Intent): IBinder? {
Log.d(TAG, "onBind")
return null
}
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null) {
Log.d(TAG, "onStartCommand:" + intent.getStringExtra("param1") + " 开始id: " + startId)
}
Log.d(TAG,"Thread id:"+ Thread.currentThread().id+" Thread name:"+Thread.currentThread().name)
return super.onStartCommand(intent, flags, startId)
}
}
我们的服务必须继承Service
类,并重写onBind
方法,该方法是抽象方法必须重写,但是由于是启动模式可以不实现,返回null即可。此类还重写了onCreate、onStartCommand、onDestroy三个主要的生命周期方法,几个方法说明如下:
onBind()
在当前的启动模式下无需实现,直接返回null
即可。
onCreate()
首次创建服务时,系统将调用此方法来执行初始化(在调用 onStartCommand() 或onBind() 之前)该方法只调用一次。
onStartCommand()
在当前启动模式下,当另一个组件(如 Activity)调用 startService()
请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 我们需要在服务工作完成后,调用 stopSelf() 或 stopService() 来停止此服务。
onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。
3.启动服务
...
class ActMainActivity : AppCompatActivity() {
companion object {
private val TAG = ActMainActivity::class.java.simpleName
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.act_main)
btnStartService1.setOnClickListener(clickListener)
btnStopService1.setOnClickListener(clickListener)
...
}
private val clickListener = View.OnClickListener { v ->
val intent = Intent(this@ActMainActivity, Service1::class.java)
intent.putExtra("param1", "hello server")
when (v.id) {
R.id.btnStartService1 -> {
startService(intent)
}
R.id.btnStopService1 -> {
stopService(intent)
}
...
else -> {
}
}
}
...
}
如上代码是一个Activity,上面有两个按钮,一个启动服务,一个结束服务。点击启动服务按钮启动服务,Logcat
输出如下
03-28 01:01:06.282 4485-4485/top.ss007.servicedemo D/Service1: onCreate
03-28 01:01:06.283 4485-4485/top.ss007.servicedemo D/Service1: onStartCommand:hello server 开始id: 1
Thread id:2 Thread name:main
从上面的log可以获得如下几个信息
- 以启动模式开始服务不会执行
onBind
方法 - 会依次执行
onCreate
与onStartCommand
两个方法 service
是运行在自己的进程中的主线程中的,不会单开线程,所以不能做耗时操作。
多次点击启动服务按钮,会多次执行onStartCommand
,onCreate
方法只会执行一次,如下log所示
03-28 01:04:47.321 4485-4485/top.ss007.servicedemo D/Service1: onCreate
03-28 01:04:47.322 4485-4485/top.ss007.servicedemo D/Service1: onStartCommand:hello server 开始id: 1
Thread id:2 Thread name:main
03-28 01:10:37.857 4485-4485/top.ss007.servicedemo D/Service1: onStartCommand:hello server 开始id: 2
Thread id:2 Thread name:main
03-28 01:10:50.372 4485-4485/top.ss007.servicedemo D/Service1: onStartCommand:hello server 开始id: 3
Thread id:2 Thread name:main
从上面的log可以看出,startId
是不断增加的,而所有的线程id
都是一样的,都是主线程。
绑定模式使用方法
在此模式下,Service
可以看做是服务端,绑定的组件,例如Activity
,看做是客户端,他们之间是通过Binder
来实现通信的。我们此处集中在同一进程间的通信,不涉及跨进程通信(IPC)的讨论,所以采用扩展 Binder
类 的方法即可
实现步骤:
- 1.新建一个自定义的名为
LocalService
的类,此类继承至Service
类 - 2.新建一个内部类
LocalBinder
,此类继承自Binder
类,并在里面提供一个public方法给客户端调用,例如getService()
方法。
inner class LocalBinder : Binder() {
fun getService(): LocalService {
return this@LocalService
}
}
- 3.在
LocalService
里面的onBind
的方法里面将LocalBinder
的实例返回。 - 4.在客户端中,从
onServiceConnected()
回调方法接收Binder
实例对象,那样就可以调用绑定服务了。
下面具体实现上面的步骤:
1.声明标签
在AndroidManifest.xml
文件中声明Service
标签,与前述一致
2.创建服务
创建一个Service
子类(Kotlin代码)
...
class LocalService : Service() {
companion object {
private val TAG: String = LocalService::class.java.simpleName
}
private var quit: Boolean = false
private var count: Int = 0
override fun onBind(intent: Intent): IBinder? {
Log.i(TAG, "Service is invoke onBind");
return LocalBinder()
}
override fun onUnbind(intent: Intent?): Boolean {
Log.i(TAG, "Service is invoke onUnbind");
return super.onUnbind(intent)
}
override fun onCreate() {
super.onCreate()
Log.d(TAG, "Service is invoke Created");
val thread = Thread {
while (quit.not()) {
Thread.sleep(1000)
count++
if (count > 1000) {
quit = true
break
}
}
}
thread.start()
}
fun getCount(): Int {
return count
}
override fun onDestroy() {
super.onDestroy()
Log.i(TAG, "Service is invoke Destroyed");
quit = true;
}
inner class LocalBinder : Binder() {
fun getService(): LocalService {
return this@LocalService
}
}
}
我们的服务必须继承Service
类,并重写onBind
方法,该方法是抽象方法必须重写,在当前绑定状态的情况下需要实现该方法并返回一个IBinder的实现类。此类还重写了onCreate
、onUnbind
、onDestroy
三个主要的生命周期方法,几个方法说明如下:
onBind()
在当前绑定模式下,当其他组件通过调用bindService() 与服务绑定时系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。
onCreate()
首次创建服务时,系统将调用此方法来执行初始化(在调用 onStartCommand() 或onBind() 之前)该方法只调用一次。
onUnbind
客户端可以通过调用 unbindService()
关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。 (服务不必自行停止运行。)
onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。
这这个服务中,我们有一个公共方法 getCount()
,外界通过这个方法可以获取到 count
的值,而这个count
在服务创建之初会每秒自增1直到1000
3.绑定服务
package top.ss007.servicedemo
...
class ActMainActivity : AppCompatActivity() {
companion object {
private val TAG = ActMainActivity::class.java.simpleName
}
var localService: LocalService? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.act_main)
...
btnStartServiceB1.setOnClickListener(clickListener)
btnStopServiceB1.setOnClickListener(clickListener)
btnShowCounter.setOnClickListener(clickListener)
...
}
private val clickListener = View.OnClickListener { v ->
...
val bindIntent = Intent(this@ActMainActivity, LocalService::class.java)
when (v.id) {
...
R.id.btnStartServiceB1 -> {
Log.d(TAG, "绑定调用:bindService");
bindService(bindIntent, serviceCon, Service.BIND_AUTO_CREATE)
}
R.id.btnStopServiceB1 -> {
Log.d(TAG, "解除绑定调用:unbindService");
if (localService != null) {
localService = null;
unbindService(serviceCon);
}
}
R.id.btnShowCounter -> {
btnShowCounter.text = localService?.getCount().toString()
Log.d(TAG, "服务中的计数器:"+localService?.getCount().toString());
}
...
else -> {
}
}
}
private val serviceCon: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d(TAG, "绑定成功调用:onServiceConnected "+" "+name?.className)
// 获取Binder
val binder = service as LocalService.LocalBinder
localService = binder.getService()
}
override fun onServiceDisconnected(name: ComponentName?) {
localService = null
}
}
}
如上代码是一个Activity,上面有3个按钮,一个启动服务,一个结束服务,一个获取服务中count
的值。点击绑定服务按钮绑定服务,Logcat
输出如下
03-28 02:33:31.576 6631-6631/top.ss007.servicedemo D/ActMainActivity: 绑定调用:bindService
03-28 02:33:31.580 6631-6631/top.ss007.servicedemo D/LocalService: Service is invoke Created
03-28 02:33:31.581 6631-6631/top.ss007.servicedemo I/LocalService: Service is invoke onBind
03-28 02:33:31.597 6631-6631/top.ss007.servicedemo D/ActMainActivity: 绑定成功调用:onServiceConnected top.ss007.servicedemo.services.LocalService
可见,当绑定成功后,activity中的ServiceConnection
里面的onServiceConnected
就会收到回调,从回调里面就获取到了服务端传过来的Binder
实例,进而可以通过其调用服务里的方法与服务交互了。
点击获取服务中计数的按钮,输出log如下
03-28 02:57:44.871 6631-6631/top.ss007.servicedemo D/ActMainActivity: 服务中的计数器:3
03-28 02:57:54.127 6631-6631/top.ss007.servicedemo D/ActMainActivity: 服务中的计数器:12
可见从Activity
里面确实调用到了服务里面的函数。
总结
上面就是在同进程下,服务Service
的两种使用方法。相关的话题还涉及到运行在前台的服务,IntentService 等,大家可以搜索相关内容学习。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/14792.html