后台默默的劳动者,探究Service

Service是什么

Service是Android中实现程序后台运行的解决方案,它非常适合执行那些不需要和用户交互而且还要求长期运行的任务。Service的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开另一个应用程序,Service仍然能够保持正常运行。
不过需要注意的是,Service并不是运行在一个独立的进程当中的,而是依赖于创建Service时所在的应用程序进程。当某个应用程序被杀掉时,所有依赖于该进程的Service也会停止运行。
另外,也不要被Service的后台概念所迷惑,实际上Service并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在Service的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况。

Android多线程编程

java中的线程创建和启动方式,这里不讲解。介绍一个kotlin提供给我们的一种更加简单的开启线程的方式,写法如下:

thread{
//编写具体的逻辑
}

这里的thread是一个kotlin内置的顶层函数,我们只需要在lambda表达式中编写具体的逻辑即可,连start方法都不用调用,thread函数在内部帮我们全部处理好了。
Android不允许在子线程中进行ui操作的。但是有些时候,我们必须在子线程里执行一些耗时任务,然后根据任务的执行结果来更新相应的ui控件。这时,Android提供了一套异步消息处理机制(handler)和AsyncTask(背后也是使用异步消息机制,对该机制做了一套封装)来处理。handler和AsyncTask的基本用法和原理可查看相关资料,这里不做讲解。

Service的基本用法

  • 启动和停止Service
package com.example.myapplication

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

class MyService : Service() {
    val TAG = "MyService"
    override fun onBind(intent: Intent): IBinder {
        return null
    }

    //service被创建的时候被调用
    override fun onCreate() {
        Log.e(TAG,"onCreate")
        super.onCreate()
    }

    //service被销毁的时候被调用
    override fun onDestroy() {
        Log.e(TAG,"onDestroy")
        super.onDestroy()
    }

    //每次service启动的时候调用,通常情况如果我们希望service一旦启动
    // 就立刻执行某个动作,就可以将逻辑写在该方法中
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e(TAG,"onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }
}
package com.example.myapplication

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_service.*

class ServiceActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_service)

        startServiceBtn.setOnClickListener {
            startService(Intent(this@ServiceActivity,MyService::class.java))
            //启动service,如果多点几次的话,会发现只有onStartCommand方法被调用了,onCreate方法只在第一次的时候被调用
        }

        stopServiceBtn.setOnClickListener {
            stopService(Intent(this@ServiceActivity,MyService::class.java))
            //停止service,也有另外停止service的方式就是在service内部调用stopSelf()方法即可
        }
    }
}

AndroidManifest.xml

<!-- enabled表示是否启用这个service,exported表示是否将这个service暴露给外部其他程序访问 -->
 <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true" />

以上就是启动和停止service的基本用法,但是从Android8.0系统开始,应用的后台功能被大幅削减。现在只有当应用保持在前台可见状态的情况下,service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。之所以这样改动,是为了防止许多恶意的应用程序长期在后台占用手机资源,从而导致手机变得越来越卡。当然,如果你真的非常需要长期在后台执行一些任务,可以使用前台Service或者workmanager。

  • Activity和Service进行通信
package com.example.myapplication

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

class MyService : Service() {
    val TAG = "MyService"
    private val mBinder = DownloadBinder()

    inner class DownloadBinder:Binder(){
       fun startDownload(){
           Log.e(TAG,"startDownload")
       }

        fun getProgress():Int{
            Log.e(TAG,"getProgress")
            return 0
        }
    }

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

    //service被创建的时候被调用
    override fun onCreate() {
        Log.e(TAG,"onCreate")
        super.onCreate()
    }

    //service被销毁的时候被调用
    override fun onDestroy() {
        Log.e(TAG,"onDestroy")
        super.onDestroy()
    }

    //每次service启动的时候调用,通常情况如果我们希望service一旦启动
    // 就立刻执行某个动作,就可以将逻辑写在该方法中
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e(TAG,"onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }
}
package com.example.myapplication

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import kotlinx.android.synthetic.main.activity_service.*

class ServiceActivity : AppCompatActivity() {
    lateinit var downloadBinder: MyService.DownloadBinder//通过binder来实现activity指挥service干什么service就去干什么的功能

    private val connection = object :ServiceConnection{
        //在service的创建进程崩溃或者被杀掉的时候才会调用,这个方法不太常用
        override fun onServiceDisconnected(name: ComponentName?) {

        }

        //在service与activity绑定的时候调用
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            downloadBinder = service as MyService.DownloadBinder
            downloadBinder.startDownload()
            downloadBinder.getProgress()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_service)

        startServiceBtn.setOnClickListener {
            startService(Intent(this@ServiceActivity,MyService::class.java))//启动service
        }

        stopServiceBtn.setOnClickListener {
            stopService(Intent(this@ServiceActivity,MyService::class.java))
            //停止service,也有另外停止service的方式就是在service内部调用stopSelf()方法即可
        }
        bindServiceBtn.setOnClickListener {
            //Context.BIND_AUTO_CREATE:表示在activity和service进行绑定后自动创建service,这会使得service中onCreate方法被执行,但是onStartCommand方法不会执行
            bindService(Intent(this@ServiceActivity,MyService::class.java),connection, Context.BIND_AUTO_CREATE)//绑定service
        }
        unbindServiceBtn.setOnClickListener {
            unbindService(connection)//解绑service,回调service的onDestory方法
        }
    }
}

另外需要注意,任何一个service在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity绑定,还可以和任何一个其他的Activity进行绑定,而且在绑定完成后,它们都可以获取相同的binder实例(意味着可以多个不同的service操作同一个binder)。

Service的生命周期

一旦在项目的任何位置调用了Context的startService方法,相应的Service就会启动,并回调onStartCommand()方法。如果这个Service之前还没有创建过,onCreate方法会先于onStartCommand方法执行。Service启动了之后会一直保持运行状态,直到stopService方法或stopSelf方法被调用,或者系统回收。注意,虽然没调用一次startService方法onStartCommand方法就会执行一次,但实际上每个Service只会存在一个实例。所以不管你调用了多少次startService方法,只需调用一次stopService或stopSelf方法,Service就会停止。
另外,还可以调用Context的bindService方法来获取一个Service的持久连接,这时就会回调Service的onBind方法。类似地,如果这个Service之前还没有创建过,onCreate方法会先于onBind方法执行。之后,调用方可以获取到onBind方法里返回的IBinder对象的实例,这样就能自由地和Service进行通信了。只要调用方和Service之前的连接没有断开,Service就会一直保持运行状态,直到被系统回收。
当调用了startService方法后,再去调用stopService方法。这时,Service中的onDestory方法就会执行,表示Service已经销毁。类似地,当调用了bindService方法后,在去调用unbindService方法,onDestory方法也会执行。但是如果我们对一个service即调用了startService方法又调用了bindservice方法,这种情况下想要销毁service,要同时调用stopservice方法和unbindservice方法,ondestory方法才会执行。

Service的更多技巧

  • 使用前台Service
    从Android8.0系统开始,只有当应用程序保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。而如果希望Service能够一直保持运行状态,就可以考虑使用前台Service。前台Service和普通Service最大的区别在于,它一直会有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似通知的效果。
    由于状态栏中一直有一个正在运行的图标,相当于我们的应用以另一种形式保持在前台可见状态,所以系统不会倾向于回收前台Service.。另外,用户也可以通过下拉状态栏清楚地知道什么应用正在运行,因此也不存在某些恶意应用长期在后台偷偷占用手机资源的情况。如果用户不希望我们的程序一直运行,也可以选择手动杀掉应用,这样Service就会跟着一起停止运行了。
    另外,从Android9.0系统开始,使用前台Service必须在androidManifest.xml文件中进行以下权限声明才行。
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

前台Service的基本用法:点击跳转

  • 使用IntentService
    如果我们想要在service中编写耗时的业务逻辑,则必须要开启一个线程才行。Android提供了IntentService类,提高我们的开发效率。该类可以健的地创建一个异步地、会自动停止的Service

示例如下:

 startIntentServiceBtn.setOnClickListener {
            Log.e("ServiceActivity","Thread id is ${Thread.currentThread().name}")
            val intent = Intent(this@ServiceActivity,MyIntentService::class.java)
           startService(intent)
package com.example.myapplication

import android.app.IntentService
import android.content.Intent
import android.content.Context
import android.util.Log

class MyIntentService : IntentService("MyIntentService") {

    override fun onHandleIntent(intent: Intent?) {
        Log.e("MyIntentService","Thread id is ${Thread.currentThread().name}")
    }

    override fun onDestroy() {//IntentService中onHandleIntent方法地逻辑执行完之后,会自动回调onDestory方法
        super.onDestroy()
        Log.e("MyIntentService","onDestory executed")
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_36828822/article/details/114108987