一、什么是Service
1、Service与Activity
Service是后台运行,Activity是前台展示。
2、Service定义
3、Service应用
可以应用到后台下载、地图定位、后台播放音乐等。
4、Service分类
startService时Service才启动,与Activity的启停无关;bindService是与Activity绑定,一同启动和停止。
二、Service的基本用法
1、创建与配置Service
- 新建Android工程,在功能目录右键新建Service(其中Exported代表应用程序组件能否调用Service或与其进行交互,默认为true;Enabled代表Service能否被实例化,默认为true)
- 重写onBind、onCreate、onStartCommand和onDestory方法
package com.study.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
/**
* 自定义Service.
*/
class MyService : Service() {
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
//Service创建时调用
override fun onCreate() {
super.onCreate()
}
//启动Service时调用
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
//Service销毁时调用
override fun onDestroy() {
super.onDestroy()
}
}
- 在AndroidManifest.xml中配置Service
<service
android:name=".MyService"
android:enabled="true"
android:exported="true" />
2、启动和停止Service
3、startService生命周期
4、代码示例
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动服务"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.336" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止服务"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
package com.study.service
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.AppCompatButton
class MainActivity : AppCompatActivity() {
private lateinit var btnStart : AppCompatButton
private lateinit var btnStop : AppCompatButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnStart = findViewById(R.id.btn_start)
btnStop = findViewById(R.id.btn_stop)
btnStart.setOnClickListener {
//启动Service
val intent = Intent(this, MyService::class.java)
startService(intent)
}
btnStop.setOnClickListener {
//停止Service
val intent = Intent(this, MyService::class.java)
stopService(intent)
}
}
}
MyService.kt
package com.study.service
import android.app.ActivityManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.util.Log
/**
* 自定义Service.
*/
class MyService : Service() {
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
//Service创建时调用
override fun onCreate() {
Log.e("MyService", "onCreate: Service已创建")
super.onCreate()
}
//启动Service时调用
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e("MyService", "onCreate: Service已启动")
Thread {
var i = 0
while (isRunning()) {
Log.e("MyService", (++i).toString())
Thread.sleep(1000)
}
}.start()
return super.onStartCommand(intent, flags, startId)
}
//Service销毁时调用
override fun onDestroy() {
Log.e("MyService", "onCreate: Service已停止")
super.onDestroy()
}
/**
* 判断Service是否正在运行.
*/
private fun isRunning(): Boolean {
//获取Activity管理器
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
//获取所有正在运行的Service
val runningService =
activityManager.getRunningServices(60) as ArrayList<ActivityManager.RunningServiceInfo>
runningService.forEach {
if (it.service.className == "com.study.service.MyService")
return true
}
return false
}
}
三、Service的基本用法实例(实现后台音乐播放和停止)
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_stop"
android:layout_width="115dp"
android:layout_height="52dp"
android:text="暂停"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.461" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_start"
android:layout_width="118dp"
android:layout_height="59dp"
android:text="播放"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.322" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
package com.study.service
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
/**
* 控制页面.
*/
class MainActivity : AppCompatActivity() {
private lateinit var btnStart: AppCompatButton
private lateinit var btnStop: AppCompatButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnStart = findViewById(R.id.btn_start)
btnStop = findViewById(R.id.btn_stop)
btnStart.setOnClickListener {
//启动音乐播放服务
startService(Intent(this, MusicService::class.java))
}
btnStop.setOnClickListener {
//关闭音乐播放服务
stopService(Intent(this, MusicService::class.java))
}
}
override fun onStart() {
//启动页面播放音乐
startService(Intent(this, MusicService::class.java))
super.onStart()
}
}
MusicService.kt
package com.study.service
import android.app.Service
import android.content.Intent
import android.media.MediaPlayer
import android.os.IBinder
/**
* 音乐播放服务.
*/
class MusicService : Service() {
companion object {
//静态对象:记录当前播放状态
var isPlay: Boolean = false
}
//音乐播放对象
private lateinit var mediaPlayer: MediaPlayer
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
override fun onCreate() {
//创建MediaPlayer对象,并加载音频文件
mediaPlayer = MediaPlayer.create(this, R.raw.music)
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//判断音乐是否播放中
if (!mediaPlayer.isPlaying) {
//播放音乐
mediaPlayer.start()
//记录播放状态
isPlay = mediaPlayer.isPlaying
}
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
//停止播放
mediaPlayer.stop()
//记录播放状态
isPlay = mediaPlayer.isPlaying
//释放资源
mediaPlayer.release()
super.onDestroy()
}
}
四、Bound Service(绑定Service)
之前的startService,Activity与Service之前没有太大的联系,无法进行通信和交换数据;而boundService,Activity与Service之间可以进行数据交互和方法调用。
1、boundService生命周期
2、boundService基本步骤
3、boundService实例(模拟双色球)
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.85"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.323" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.726"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.323" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.61"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.323" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.323" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.374"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.323" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.255"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.323" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.128"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.323" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="生成随机数"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
BinderService.kt
package com.study.bindservice
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import kotlin.random.Random
/**
* 随机数生成服务.
*/
class BinderService : Service() {
//1. 创建MyBinder内部类
class MyBinder : Binder() {
//1.1. 获取Service方法
fun getService(): BinderService {
//1.2. 返回当前Service
return BinderService()
}
}
override fun onBind(intent: Intent?): IBinder {
//2. 返回MyBinder Service对象
return MyBinder()
}
override fun onDestroy() {
//3. 销毁Service
super.onDestroy()
}
//返回随机数方法.
fun getRandomNumber(): List<String> {
val resArr = mutableListOf<String>()
var strNumber = ""
for (i in 0 until 7) {
val number = Random.nextInt(33) + 1
strNumber = if (number < 10) {
"0$number"
} else {
number.toString()
}
resArr.add(strNumber)
}
return resArr
}
}
MainActivity.kt
package com.study.bindservice
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.AppCompatTextView
class MainActivity : AppCompatActivity() {
private lateinit var btnStart: AppCompatButton
private lateinit var binderService: BinderService
private val tvId =
mutableListOf(R.id.tv1, R.id.tv2, R.id.tv3, R.id.tv4, R.id.tv5, R.id.tv6, R.id.tv7)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnStart = findViewById(R.id.btn_start)
btnStart.setOnClickListener {
val number = binderService.getRandomNumber()
number.forEachIndexed { index, it ->
val textViewCompat = findViewById<AppCompatTextView>(tvId[index])
textViewCompat.text = it
}
}
}
//4. 创建ServiceConnection对象
private var conn = object : ServiceConnection {
//Service与绑定它的组件连接成功时调用
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
//4.1 获取后台service
binderService = (service as BinderService.MyBinder).getService()
}
//Service与绑定它的组件断开连接时调用
override fun onServiceDisconnected(name: ComponentName?) {
}
}
//5. 绑定服务
override fun onStart() {
super.onStart()
val intent = Intent(this, BinderService::class.java)
bindService(intent, conn, BIND_AUTO_CREATE)
}
//6. 解除绑定
override fun onStop() {
super.onStop()
unbindService(conn)
}
}
五、IntentService
IntentService在Android8.0以后已被弃用,目前官方推荐使用Jetpack组件。
1、为什么要使用IntentService
它可以自动开启线程来执行耗时任务,并且在耗时任务执行完毕之后可以自动停止服务。
2、IntentService实例
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_intent_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动IntentService"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.528" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动Service"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.288" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
package com.study.intent.service
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnService = findViewById<AppCompatButton>(R.id.btn_service)
val btnIntentService = findViewById<AppCompatButton>(R.id.btn_intent_service)
//启动普通Service
btnService.setOnClickListener {
startService(Intent(this, MyService::class.java))
}
//启动IntentService
btnIntentService.setOnClickListener {
startService(Intent(this, MyIntentService::class.java))
}
}
}
MyService.kt
package com.study.intent.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
/**
* 普通Service.
*/
class MyService: Service() {
companion object{
const val TAG = "MyService"
}
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
override fun onCreate() {
Log.e(TAG, "onCreate: Service已创建")
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e(TAG, "onStartCommand: Service已启动")
//进行耗时操作(需要在子线程中进行)
Thread {
val endTime = System.currentTimeMillis() + 20 * 1000
while (System.currentTimeMillis() < endTime) {
synchronized(this) {
endTime - System.currentTimeMillis()
}
}
//不能自动停止服务,需要代码停止
stopSelf()
}.start()
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
Log.e(TAG, "onDestroy: Service已销毁")
super.onDestroy()
}
}
MyIntentService.kt
package com.study.intent.service
import android.app.IntentService
import android.content.Intent
import android.util.Log
/**
* IntentService.
*/
class MyIntentService(name: String?) : IntentService(name) {
companion object {
const val TAG = "MyIntentService"
}
//默认的构造方法.
constructor() : this("")
override fun onHandleIntent(intent: Intent?) {
//自动开启线程执行耗时任务
Log.e(TAG, "onHandleIntent: Service已启动")
val endTime = System.currentTimeMillis() + 20 * 1000
while (System.currentTimeMillis() < endTime) {
synchronized(this) {
endTime - System.currentTimeMillis()
}
}
}
override fun onDestroy() {
//自动销毁
Log.e(TAG, "onDestroy: Service已销毁")
super.onDestroy()
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。