Android service介绍——startService和bindService基本使用(1)
问题背景
在安卓日常开发和学习中,经常会使用到Service,它是Android四大组件之一。使用Service可以在后台执行长时间的操作( perform long-running operations in the background ),Service并不与用户产生UI交互。其他的应用组件可以启动Service,即便用户切换了其他应用,启动的Service仍可在后台运行。一个组件可以与Service绑定并与之交互,甚至是跨进程通信(IPC)。例如,一个Service可以在后台执行网络请求、播放音乐、执行文件读写操作或者与 content provider交互等等。 本文将先对作为四大组件之一的Service的基本使用进行介绍。
问题分析
话不多说,直接上代码,一起先看看service的基本使用。
一、startService()方法启动一个Service
(1)创建一个service,HelloService
import android.app.Service
import android.content.Intent
import android.os.*
import android.util.Log
import android.widget.Toast
class HelloService : Service() {
private var mServiceLooper: Looper? = null
private var mServiceHandler: ServiceHandler? = null
companion object {
const val TAG = "HelloService"
}
private inner class ServiceHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
Log.d(TAG, "handleMessage")
try {
Thread.sleep(5000)
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
}
}
}
override fun onCreate() {
Log.d(TAG, "onCreate")
val thread = HandlerThread(
"ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND
)
thread.start()
mServiceLooper = thread.looper
mServiceHandler = ServiceHandler(mServiceLooper!!)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand")
val msg: Message? = mServiceHandler?.obtainMessage()
msg?.arg1 = startId
msg?.let {
mServiceHandler?.sendMessage(msg)
}
return START_STICKY
}
override fun onBind(intent: Intent): IBinder? {
Log.d(TAG, "onBind")
return null
}
override fun onDestroy() {
Log.d(TAG, "onDestroy")
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
}
}
(2)在manifest中记得要注册service,正常创建文件时候选择service会自动创建
<service
android:name="composer.service.HelloService"
android:exported="true"
android:enabled="true" />
(3)activity中startService()方法启动service
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import composer.databinding.ActivityTestStartServiceBinding
import composer.service.HelloService
import kotlinx.android.synthetic.main.activity_test_start_service.*
class TestStartServiceActivity : AppCompatActivity() {
lateinit var binding: ActivityTestStartServiceBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestStartServiceBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root)
startMyService.setOnClickListener {
val intent = Intent(baseContext, HelloService::class.java)
startService(intent)
}
stopMyService.setOnClickListener {
val intent = Intent(baseContext, HelloService::class.java)
stopService(intent)
}
}
}
(4)对应布局layout文件如下:
<?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=".view.TestStartServiceActivity">
<Button
android:id="@+id/startMyService"
android:text="start MyService"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/stopMyService"
android:text="stop MyService"
app:layout_constraintTop_toBottomOf="@id/startMyService"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
二、bindService()方法绑定一个Service
对上面的demo进行调整,使用bindService()的方法绑定启动一个service (1)HelloService代码如下:
import android.app.Service
import android.content.Intent
import android.os.*
import android.util.Log
import android.widget.Toast
class HelloService : Service() {
private var mServiceLooper: Looper? = null
private var mServiceHandler: ServiceHandler? = null
private val countNumBinder = CountNumBinder()
companion object {
const val TAG = "HelloService"
}
inner class CountNumBinder: Binder() {
fun getCount(): Int {
Log.i(TAG, ">>>>>>getCount()");
return 100;
}
}
private inner class ServiceHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
Log.d(TAG, "handleMessage")
try {
Thread.sleep(5000)
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
}
}
}
override fun onCreate() {
Log.d(TAG, "onCreate")
val thread = HandlerThread(
"ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND
)
thread.start()
mServiceLooper = thread.looper
mServiceHandler = ServiceHandler(mServiceLooper!!)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand")
val msg: Message? = mServiceHandler?.obtainMessage()
msg?.arg1 = startId
msg?.let {
mServiceHandler?.sendMessage(msg)
}
return START_STICKY
}
override fun onBind(intent: Intent): IBinder {
Log.d(TAG, "onBind")
return countNumBinder
}
override fun onDestroy() {
Log.d(TAG, "onDestroy")
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
}
}
(2)activity代码修改如下:
package composer.view
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import composer.databinding.ActivityTestStartServiceBinding
import composer.service.HelloService
import kotlinx.android.synthetic.main.activity_test_start_service.*
class TestStartServiceActivity : AppCompatActivity() {
lateinit var binding: ActivityTestStartServiceBinding
var countNumBinder: HelloService.CountNumBinder? = null
companion object {
const val TAG = "HelloService activity"
}
private val conn = object: ServiceConnection {
// Activity与Service断开连接时回调该方法
@Override
override fun onServiceDisconnected(name: ComponentName) {
Log.d(TAG, ">>>>>>Service DisConnected");
}
//Activity与Service连接成功时回调该方法
@Override
override fun onServiceConnected(name: ComponentName, service: IBinder) {
Log.i(TAG, ">>>>>>Service Connected");
countNumBinder = service as HelloService.CountNumBinder
Log.i(TAG, ">>>>>>Service Connected: " + countNumBinder?.getCount());
}}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestStartServiceBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root)
startMyService.setOnClickListener {
val intent = Intent(baseContext, HelloService::class.java)
startService(intent)
}
stopMyService.setOnClickListener {
val intent = Intent(baseContext, HelloService::class.java)
stopService(intent)
}
bindMyService.setOnClickListener {
val intent = Intent(baseContext, HelloService::class.java)
bindService(intent, conn, Service.BIND_AUTO_CREATE)
}
}
}
(3)activity对应layout布局文件修改如下:
<?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=".view.TestStartServiceActivity">
<Button
android:id="@+id/startMyService"
android:text="start MyService"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/stopMyService"
android:text="stop MyService"
app:layout_constraintTop_toBottomOf="@id/startMyService"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/bindMyService"
android:text="bind MyService"
app:layout_constraintTop_toBottomOf="@id/stopMyService"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
项目运行后,点击bindService按钮,运行结果如下:
问题总结
本文初步讲解了安卓中四大组件之一的service的基本使用,包括使用startService和bindService两种启动方式,以及通过运行结果可以看到对应的一个生命周期的关系。后面会继续介绍Bound Service的几种高阶用法,有兴趣的同学可以进一步深入研究。