Android应用开发中核心的四大组件是整体应用的骨架,利用好四大组件才能搭建出一个合理高效的APP。了解了Activity之后,我们再认识一下Service,它是一个独立的组件,译为服务,不需要用户交互,可长时间运行,即便程序销毁也可继续工作。
本文已收录至☞Android学习路线_梳理
上一篇☞Android学习路线_入门篇(三)Acitvity生命周期与启动模式
下一篇☞Android学习路线_入门篇(五)Service的生命周期与如何保活
1. 创建一个Service
和Activity类似,要使用一个Service首先需要在清单文件中注册,然后创建一个Service类,但是它不需要布局文件。
1.1 注册Service
AndroidManifest中添加Service的注册代码,相对Activity的注册来说,Service的注册可就简单多了,只需要在application
中增加一行代码:
<service android:name=".MyService" />
当然Service也是可以加上<intent-filter></intent-filter>
标签的,用法和Activity一致:
<service android:name=".MyService" >
<intent-filter>
<action android:name="com.example.MyService.DO_SOMETHING" />
</intent-filter>
</service>
通过Intent设置对应的action可以进行隐式启动,不过在Android5.0以后有一个Service Intent must be explitict
的特性,也就是说从Lollipop开始,service服务必须采用显示方式启动。但是并不是不能避开的,隐式启动还是有些用处的,可不能说没了就没了。我们来看看关键的源码:
private void validateServiceIntent(Intent service) {
if (service.getComponent() == null && service.getPackage() == null) {
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
IllegalArgumentException ex = new IllegalArgumentException(
"Service Intent must be explicit: " + service);
throw ex;
} else {
Log.w(TAG, "Implicit intents with startService are not safe: " + service
+ " " + Debug.getCallers(2, 3));
}
}
}
可以看到源码中判断的条件是service.getComponent() == null && service.getPackage() == null
,那么就有两种办法避开这个判断啦:
- 设置Action和packageName:
此方式是google官方推荐使用的解决方法。参考代码如下:
val mIntent = Intent()
// 设置注册时配置好的action
mIntent.action = "com.example.MyService.DO_SOMETHING"
// 设置当前应用的包名
mIntent.setPackage(getPackageName())
context.startService(mIntent)
- 设置ComponentName:
此方式是其实也类似方法1。参考代码如下:
val mIntent = Intent()
val componentName = ComponentName(pkgName, serviceName)
intent.component = componentName
context.startService(intent)
1.2 创建类文件
注册完会有有报错提醒,和之前创建Activity是一样的。这里插一句话,之前创建Activity我们的顺序是注册->创建类->创建布局,每做一步都会有报错提醒,直到最后完成才恢复正常。
有小伙伴就有疑问了,如果反过来的话就完全没有报错,这里有什么玄机吗?
首先报错的原因是注册是用了类但是类没创建,类创建时用了布局但是布局没创建,反过来就没有这种依赖关系。其次有报错的情况下,就逼着我们要去解决,对于初学者来说有一个好处,那就是提醒你还有文件没创建,防止漏了步骤最后程序跑起来遇到崩溃或者没反应的问题。
那么下一步就是创建类文件了,我们还是在src/main/java/com/example/myapplication/
路径新建一个类MyService,因为我们在注册时用的是.MyService
:
package com.example.myapplication
import android.app.Service
import android.content.Intent
import android.os.IBinder
class MyService: Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
}
创建很简单,必须实现的方法只有onBind()
,而且可以直接返回null。这时候的MyService不能做任何事,想要起作用就需要我们重写父类的onStartCommand()
方法,最好再重写onDestroy()
方法:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Toast.makeText(this, "服务启动", Toast.LENGTH_SHORT).show()
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
Toast.makeText(this, "服务停止", Toast.LENGTH_SHORT).show()
}
这里实现了两个简单操作,Toast是一个简单的提示控件,通常是在屏幕底部弹出一个浮动文本框。
2. 启动Service
使用一个Service需要通过其他组件启动Service,随后才会触发onStartCommand()
方法执行Service的业务逻辑。而Service的启动方法有两种:startService()
和bindService()
,这两种模式下生命周期有所不同,运行的机制也有区别。
2.1 startService方式
startService方式启动需要写的代码特别少,只需要在你想要启动它的地方加上一小段代码。我们来重写MainActivity的onStart()
方法,加入这段代码:
override fun onStart() {
super.onStart()
val intent = Intent(this, MyService::class.java)
startService(intent)
}
这时候MyService就启动了,运行起来自动打开MainActivity,你可以看到屏幕底部弹出服务启动
的提示语。但是这时候不论你如何操作都无法看到服务停止
的提示语。
Android的应用程序组件,如活动(Activity),通过startService()启动了服务,则服务是Started状态。一旦启动,服务可以在后台无限期运行,即使启动它的组件已经被销毁。
那么我们如何停止一个服务呢?和startService启动服务类似,在你想要关闭它的地方加上stopService的逻辑,比如在MainActivity中重写onStop()
方法,加入这段代码:
override fun onStop() {
super.onStop()
val intent = Intent(this, MyService::class.java)
stopService(intent)
}
重新运行项目,自动打开MainActivity,你可以看到屏幕底部弹出服务启动
的提示语。这时候点击物理返回键或者桌面键,你可以看到屏幕底部弹出服务停止
的提示语。
2.2 bindService方式
bindService的方式需要提供一个ServiceConnection
对象,我们来改写之前的启动逻辑:
private var sc: MyServiceConnection? = null
override fun onStart() {
super.onStart()
sc = MyServiceConnection()
val intent = Intent(this, MyService::class.java)
bindService(intent, sc, Context.BIND_AUTO_CREATE)
}
override fun onStop() {
super.onStop()
unbindService(sc)
}
class MyServiceConnection: ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
// service: IBinder? 就是MyService里onBind()方法的返回值
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
当Android的应用程序组件通过bindService()绑定了服务,则服务是Bound状态。Bound状态的服务提供了一个客户服务器接口来允许组件与服务进行交互,如发送请求,获取结果,甚至通过IPC来进行跨进程通信。
完毕
今天的分享就到这里,文章多有不足,各位小伙伴有什么想法可以直接评论或是私信,要是对你有所帮助就给我一个赞吧,喜欢我的小伙伴可以关注我哦~
本文已收录至☞Android学习路线_梳理
上一篇☞Android学习路线_入门篇(三)Acitvity生命周期与启动模式
下一篇☞Android学习路线_入门篇(五)Service的生命周期与如何保活
支持我的小伙伴们可以微信搜索“Android思维库”,或者微信扫描下方二维码,关注我的公众号,每天都会推送新知识~