其他分享
首页 > 其他分享> > Android中的Service,潜在的服务者。

Android中的Service,潜在的服务者。

作者:互联网

前言

年前因为疫情以及个人原因辞职以后,很久没有工作了。终于在休息了大半年后,准备回到手敲代码的生活中去,开始着手准备自己的简历和面试等等。在面试过程中,被面试官问到了最基本的问题:“介绍一下Service作用和使用方式”,然而在回答的过程中,发现自己对于这部分东西有一些朦胧的感觉,竟然有一丝丝逐渐淡化的倾向。于是乎,有了这篇文章,Android中的Service,潜在的服务者。

正文

一,什么是Service?

Service是Android中实现程序后台运行的解决方案,适用于去执行那些不需要和用户交互而且还要求长期运行的任务。Service是android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行,只能后台运行,并且可以和其他组件进行交互。

Service并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。

虽然它是Android系统中四大组件之一,但是它并不能自己运行。那么问题来了,它到底是如何运行的呢?答案是———虽然无法自己运行,但是它可以和其他组件进行联系,通过别的组件来帮助它运行在进程中。其中运行方式又有不同,分为两种:

运行条件启动方式停止方式service与启动它的组件之间的通信方式service的生命周期
startService在其他组件中调用startService()方法后,服务即处于运行状态service中调用stopSelf()方法,或者其他组件调用stopService()方法后,service将停止运行没有提供默认的通信方式,启动service后,service就处于独立运行的状态一旦启动,service即可在后台无限期运行,即使启动service的组件已被销毁也不受其影响,直到其被停止
bindService在其他组件中调用bindService()方法后,服务即处于运行状态所有与service绑定的组件都被销毁,或者它们都调用了unbindService()方法后,service将停止运行可以通过 ServiceConnection进行通信,组件可以与service进行交互、发送请求、获取结果,甚至是利用IPC跨进程执行这些操作当所有与其绑定的组件都取消绑定(可能是组件被销毁也有可能是其调用了unbindService()方法)后,service将停止

PS:
1.上述中的“其他组件”不包括Broadcast receiver,其无法直接启动或绑定service。
2.startService()与bindService()并不冲突,同一个service可能既有组件调用了startService()启动它,又有组件与它进行了绑定。当同一个service与其他组件同时存在这两种联系时,其生命周期会发生变化,必须从两种方法的角度看service均停止才能真正停止。

ok,通过这个表格我们可以知道,这两种启动service的方式各有自己的特点,我们可以根据自己的项目需要选择合适的方式。

2,如何创建一个Service?

关于这个话题,其实在百度、google,又或者任何一本Android入门的书本上都有这个问题的答案:

其实和Activity非常相似,包括了其中的一些生命周期方法。

在Manifests文件里进行声明的时候,只有android:name属性是必须要有的,其他的属性都可以没有。但是有的时候适当的配置可以让我们的开发进行地更加顺利,所以了解一下注册一个service可以声明哪些属性也是很有必要的。

<service
    android:enabled=["true" | "false"]
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:isolatedProcess=["true" | "false"]
    android:label="string resource"
    android:name="string"
    android:permission="string"
    android:process="string" >
</service>

下边来举个栗子:

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log

class MyService : Service() {
    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate")
        //只在service创建的时候调用一次,可以在此进行一些一次性的初始化操作
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand")
        //当其他组件调用startService()方法时,此方法将会被调用
        //在这里进行这个service主要的操作
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onBind(intent: Intent): IBinder? {
        Log.d(TAG, "onBind")
        //当其他组件调用bindService()方法时,此方法将会被调用
        //如果不想让这个service被绑定,在此返回null即可
        return null
    }

    override fun onDestroy() {
        Log.d(TAG, "onDestroy")
        //service调用的最后一个方法
        //在此进行资源的回收
        super.onDestroy()
    }

    companion object {
        private const val TAG = "MyService"
    }
}
<!--in manifests -->
<service android:name=".ServiceDemo"/>

3,如何启动Service?

开始我们说了两种启动方式,但都是笼统的概括了一下,下面来具体的说说这两种方式。

3.1,startService()

另一个组件通过调用startService()方法,就可以启动一个特定的service,并且这将导致service中的onStartCommand()方法被调用。在调用startService()方法的时候,其他组件需要在方法中传递一个intent参数,然后service将会在onStartCommand()中接收这个intent,并获取一些数据。比如此时某个Activity要将一些数据存入数据库中,我就可以通过intent把数据传入service,然后让service去进行连接数据库,存储数据等操作,而此时用户可以执行其他的任何操作——甚至包括销毁那个Activity——这并不会影响service存储数据这件事。

当一个service通过这种方式启动之后,它的生命周期就已经不受启动它的组件影响了,它可以在后台无限期的运行下去,只要service自身没有调用stopSelf()并且其他的组件没有调用针对它的stopService()。

另外,如果确定了使用这种方式启动service并且不希望这个service被绑定的话,那么也许除了传统的创建一个类继承service之外我们有一个更好的选择——继承IntentService。

如果是扩建Service类的话,通常情况下我们需要新建一个用于执行工作的新线程,因为默认情况下service将工作于应用的主线程,而这将会降低所有正在运行的Activity的性能。而IntentService就不同了。它是Service的子类,它使用工作线程来注意的处理所有的startService请求。如果你不要求这个service要同时处理多个请求,那么继承这个类显然要比直接继承Service好到不知道哪里去了——IntentService已经做了这些事:

因此我们只需要实现onHandleIntent()方法来完成具体的功能逻辑就可以了。

import android.content.Intent
import android.os.Bundle
import android.os.PersistableBundle
import androidx.appcompat.app.AppCompatActivity

class TestActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        val intent = Intent(this, MyService::class.java)
        startService(intent)
    }
}
import android.app.IntentService
import android.content.Intent

class MyIntentService
/**
 * Creates an IntentService.  Invoked by your subclass's constructor.
 *
 * @param name Used to name the worker thread, important only for debugging.
 */
(name: String?) : IntentService(name) {

    override fun onHandleIntent(intent: Intent?) {
        //在这里根据intent进行操作
    }
}

相比上面的继承service实现,这个确实要简单许多。但是要注意的是,如果需要重写其他的方法,比如onDestroy()方法,一定不要删掉它的超类实现!因为它的超类实现里面也许包括了对工作线程还有工作队列的初始化以及销毁等操作,如果没有了的话很容易出问题。

但是,如果你有让service同时处理多个请求的需求,这个时候就只能去继承Service了。这个时候就要自己去处理工作线程那些事。下面是一个官方的栗子:

import android.app.Service
import android.content.Intent
import android.os.*
import android.widget.Toast

class HelloService : Service() {
    private var mServiceLooper: Looper? = null
    private var mServiceHandler: ServiceHandler? = null
    private val lock = java.lang.Object()

    private inner class ServiceHandler(looper: Looper?) : Handler(looper) {
        override fun handleMessage(msg: Message) {

            val endTime = System.currentTimeMillis() + 5 * 1000
            while (System.currentTimeMillis() < endTime) {
                synchronized(lock) {
                    try {
                        lock.wait(endTime - System.currentTimeMillis())
                    } catch (e: Exception) {
                    }
                }
            }
            stopSelf(msg.arg1)
        }
    }

    override fun 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 {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()
        val msg = mServiceHandler!!.obtainMessage()
        msg.arg1 = startId
        mServiceHandler!!.sendMessage(msg)
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
    }
}

 

比起IntentService来,显然做的工作要多的多的多。但是,由于是自己处理的对onStartCommand的调用,它可以同时执行多个请求——虽然官方的栗子里没有这样做。但是如果你想这样做,就可以为每一个请求创建一个线程,然后立即运行这些请求。

另外,我们注意到onStartCommand()的返回值是一个很奇怪的值START_STICKY,这是个什么呢?或者说这个方法的返回值是用来干嘛的呢?事实上,它的返回值是用来指定系统对当前线程的行为的。它的返回值必须是以下常量之一:
- START_NOT_STICKY : 如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。

除此之外,继承service来实现的service,一定要记得,停止这个service。因为除非系统必须回收内存资源,否则系统不会停止或销毁service。事实上,如果这个service是与可见组件绑定,其几乎永远不会被停止或销毁。在这种情况下,如果你忘记了在其工作完成之后将其停止,势必会造成系统资源的浪费以及电池电量的消耗,而这应当是我们要极力避免的。

3.2,bindService()

这是一种比startService更复杂的启动方式,同时使用这种方式启动的service也能完成更多的事情,比如其他组件可向其发送请求,接受来自它的响应,甚至通过它来进行IPC等等。我们通常将绑定它的组件成为客户端,而称它为服务器。

如果要创建一个支持绑定的service,我们必须要重写它的onBind()方法。这个方法会返回一个IBinder对象,它是客户端用来和服务器进行交互的接口。而要得到IBinder接口,我们通常有三种方式:继承Binder类,使用Messenger类,使用AIDL。

这三种方式也是各有各的好处以及缺点,更多的讲解将我会放在后边的文章中。

标签:调用,Service,service,服务者,组件,Intent,Android,android
来源: https://blog.csdn.net/elank0521/article/details/111320632