Android service介绍——startService和bindService基本使用(1)

时间:2023-02-28 20:02:54

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>

Android service介绍——startService和bindService基本使用(1)

二、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按钮,运行结果如下: Android service介绍——startService和bindService基本使用(1)

问题总结

本文初步讲解了安卓中四大组件之一的service的基本使用,包括使用startService和bindService两种启动方式,以及通过运行结果可以看到对应的一个生命周期的关系。后面会继续介绍Bound Service的几种高阶用法,有兴趣的同学可以进一步深入研究。