Android Service介绍

一、什么是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 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340