美文网首页
Android体系复习--Service

Android体系复习--Service

作者: 突然思考的杨小六 | 来源:发表于2020-11-17 11:06 被阅读0次

一、Service简介

四大组件之一,是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
一般分为:前台服务、后台服务、绑定服务

  • 前台服务:前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知即使用户停止与应用的交互,前台服务仍会继续运行

  • 后台服务:后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务

  • 绑定服务:当应用组件通过调用 bindService()绑定到服务时,服务即处于绑定状态。绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

二、生命周期

服务启动方式有两种:启动服务和绑定服务,下图是两种启动方式的生命周期


service_lifecycle.png

onStartCommand:

当另一个组件(如 Activity)请求启动服务时,系统会通过调用 startService() 来调用此方法。执行此方法时,服务即会启动并可在后台无限期运行。如果您实现此方法,则在服务工作完成后,您需负责通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)

onBind:

当另一个组件想要与服务绑定(例如执行 RPC)时,系统会通过调用 bindService() 来调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,以供客户端用来与服务进行通信。请务必实现此方法;但是,如果您并不希望允许绑定,则应返回 null。

onCreate:

首次创建服务时,系统会(在调用 onStartCommand() 或 onBind() 之前)调用此方法来执行一次性设置程序。如果服务已在运行,则不会调用此方法。

onDestroy:

当不再使用服务且准备将其销毁时,系统会调用此方法。服务应通过实现此方法来清理任何资源,如线程、注册的侦听器、接收器等。这是服务接收的最后一个调用。

三、onStartCommand的返回值介绍

START_STICKY:

在Service启动后,如果该Service被系统杀死了,系统会重新创建该Service,onStartCommand方法会被调用,但是传给Service的intent为null

START_NOT_STICKY:

在Service启动后,如果该Service被系统杀死了,系统不会重新创建该Service。除非开发者明确地使用startService的方式

START_REDELIVER_INTENT:

在Service启动后,如果该Service被系统杀死了,系统会重新创建该Service,onStartCommand方法会调用,并且将上次的Intent通过onStartCommand方法传递进来。除非开发者明确使用stopSelf方法停止该Service,否则其不会停止。

START_STICKY_COMPATIBILITY

其功能和START_STICKY一样,是START_STICKY的兼容版本,但是不保证onStartCommand方法会被调用。

四、开启服务的两种方式

1. startService

startService 第一次启动时会触发OnCreate--> onStartCommond方法, 如果Service已经存在,再次启动时则只处罚OnStartCommond方法,且第三个参数startId + 1,停止任务时,我们可以直接stopSelf,也可以通过stopSelf(startId)来停止服务
stopSelf(startId) 这种方式停止时,如果startService了多次,需要将所有startId关闭后才会触发OnDestroy回调


class LearnService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        "onBind".log()
        return null
    }

    override fun onCreate() {
        super.onCreate()
        "onCreate".log()
    }

    override fun onDestroy() {
        super.onDestroy()
        "onDestroy".log()
    }


    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        "onStartCommand".log()
        val mThread = object : Thread() {
            override fun run() {
                super.run()
                var count: Int = 0
                while (!isInterrupted) {
                    "任务执行中:$startId   执行次数:${count++}".log()
                    if (count > 5) {
                        interrupt()
                        stopSelf(startId)
                    }
                    SystemClock.sleep(1000)
                }
            }
        }
        mThread.start()
        return super.onStartCommand(intent, flags, startId)
    }
}

间隔两秒后开启两次 打印结果如下:

2020-11-12 15:48:50.839 3337-3337/com.czy.systemlearn D/YYYYYY: onCreate
2020-11-12 15:48:50.841 3337-3337/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-12 15:48:50.842 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1   执行次数:0
2020-11-12 15:48:51.848 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1   执行次数:1
2020-11-12 15:48:52.856 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1   执行次数:2
2020-11-12 15:48:53.005 3337-3337/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-12 15:48:53.008 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2   执行次数:0
2020-11-12 15:48:53.864 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1   执行次数:3
2020-11-12 15:48:54.016 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2   执行次数:1
2020-11-12 15:48:54.870 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1   执行次数:4
2020-11-12 15:48:55.024 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2   执行次数:2
2020-11-12 15:48:55.876 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1   执行次数:5
2020-11-12 15:48:56.033 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2   执行次数:3
2020-11-12 15:48:57.040 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2   执行次数:4
2020-11-12 15:48:58.048 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2   执行次数:5
2020-11-12 15:48:58.059 3337-3337/com.czy.systemlearn D/YYYYYY: onDestroy

2. bindService

同一个 Service 可以被多个组件绑定,只有所有绑定它的组件都进行了 unBind 操作,这个 Service 才会被销毁。
绑定服务 bindService, 其生命周期是 onCreate -> onBind ,绑定成功后再次调用bindService不会触发生命周期。启动方法参数如下:

 bindService(Intent service, ServiceConnection conn,int flags)
  • Intent : 要开启的服务
  • ServiceConnection:一个接口,包含两个回调onServiceConnected(绑定成功后触发)和onServiceDisconnected(服务端意外挂掉触发)
  • flags:一般选用BIND_AUTO_CREATE 当bindService时,该服务如果不存在则自动创建该服务

ServiceConnection的onServiceConnected会返回一个IBinder对象, Service 也有一个onBind方法,返回一个IBinder对象,他们是同一个对象,我们可以通过该对象来实现 Activity 调用Service方法通信
代码示例


class LearnService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        "onBind".log()
        return MyBind()
    }

    override fun onUnbind(intent: Intent?): Boolean {
        "onUnbind".log()
        return super.onUnbind(intent)
    }
    
    override fun onCreate() {
        super.onCreate()
        "onCreate".log()
    }

    override fun onDestroy() {
        super.onDestroy()
        "onDestroy".log()
    }


    // 暴露给 Activity调用的方法
    fun serviceInnerMethods(msg: String) {
        msg.log()
    }

    open inner class MyBind : Binder() {
        val service: LearnService
            get() = this@LearnService
    }
}

调用方法

class MainActivity : AppCompatActivity() {
    lateinit var connection: ServerConnect
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        connection = ServerConnect()
  
        btnBindService.setOnClickListener {
            //绑定服务
            bindService(Intent(MainActivity@ this, LearnService::class.java), connection, BIND_AUTO_CREATE)
        }
        btnUnBindService.setOnClickListener {
            //解绑服务
            unbindService(connection)
        }
    }

    class ServerConnect : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
          // 拿到Service对象 调用其内部方法
            val myBind = service as LearnService.MyBind
            myBind.service.serviceInnerMethods("Activity 已经和你建立了连接")

        }

        override fun onServiceDisconnected(name: ComponentName?) {
            "连接断开".log()
        }
    }
}

最终结果

2020-11-12 17:31:36.607 13974-13974/com.czy.systemlearn D/YYYYYY: 点击绑定服务 ==== 
2020-11-12 17:31:36.623 13974-13974/com.czy.systemlearn D/YYYYYY: onCreate
2020-11-12 17:31:36.624 13974-13974/com.czy.systemlearn D/YYYYYY: onBind
2020-11-12 17:31:36.628 13974-13974/com.czy.systemlearn D/YYYYYY: Activity 已经和你建立了连接
2020-11-12 17:31:39.467 13974-13974/com.czy.systemlearn D/YYYYYY: 点击解绑服务 ==== 
2020-11-12 17:31:39.483 13974-13974/com.czy.systemlearn D/YYYYYY: onUnbind
2020-11-12 17:31:39.486 13974-13974/com.czy.systemlearn D/YYYYYY: onDestroy

上面只是Activity 向Service调用,如果Service获取到最终结果后如何将结果传递回Activity,除了广播外,我们可以在Service设置一个回调监听即可,


    lateinit var dataCallback: DataCallback

    //设置数据回调监听
    fun setDataCallbackListener(dataCallback: DataCallback) {
        this.dataCallback = dataCallback
    }
    
    interface DataCallback {
        fun callBack(result: String)
    }

 // 在合适的时机触发即可
if(this::dataCallback.isInitialized){
            dataCallback.callBack("Service 将结果传递给Activity")
        }

3. 先StartService 在BindService的生命周期

  1. 先startService : onCreate --> onStartCommand
    在BindService: --> onBinde
    然后unBindService: -->onUnbind
    最后stopService: -->onDestroy

  2. 先绑定bindService: onCreate --> onBinde
    在startService: --> onStartCommand
    然后stopService: 不触发生命周期
    最后UNBindService: --> unbind-->onDestroy

其他场景类似,只有stopService 跟unbindservice都执行了,才会走onDestroy生命周期

注意:混合使用时,如果我们绑定服务时flags 用的BIND_AUTO_CREATE,binderService后,在unbindService,如果之前此时Service还在运行,我们再次bindService是不会触发onBinde生命周期回调的

五、IntentService

IntentService是继承于Service的子类,它可以处理异步处理请求,Client使用startService(Intent)来启动Service。在它内部有一个工作线程来处理耗时操作,当它处理完所有请求的时候会停止它自身,不需要我们手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService 的 onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
而且,所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。

总结下相比Service的优势和特点:

  • 内部有一个工作线程,来处理耗时操作
  • 工作线程已队列形式运行,一个执行完在执行另一个(串行执行。)
  • 不需要开发者手动停止服务

使用场景和示例
IntentService 可多次启动,工作线程内部会顺序执行。另外 IntentService 因为是服务的原因,相比普通线程具有更高的优先级,适用更高优先级的的后台任务,不容易被系统杀死。可以用作后台下载任务。


class MyIntentService() : IntentService("name") {
    override fun onCreate() {
        super.onCreate()
        "onCreate".log()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        "onStartCommand".log()
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        "onDestroy".log()
        super.onDestroy()
    }

    override fun onHandleIntent(intent: Intent?) {
        val name = intent?.getStringExtra("name")
        "当前线程: ${Thread.currentThread().name}".log()
        SystemClock.sleep(2000)
        "$name 任务执行完毕".log()
    }
}

使用

 var index = 0
 btnStart.setOnClickListener {
     val intent = Intent(MainActivity@ this, MyIntentService::class.java)
     intent.putExtra("name", index++.toString())
     startService(intent)
}

结果

2020-11-16 17:31:52.588 8466-8466/com.czy.systemlearn D/YYYYYY: onCreate
2020-11-16 17:31:52.589 8466-8466/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-16 17:31:52.590 8466-8626/com.czy.systemlearn D/YYYYYY: 当前线程: IntentService[name]
2020-11-16 17:31:53.077 8466-8466/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-16 17:31:53.560 8466-8466/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-16 17:31:54.594 8466-8626/com.czy.systemlearn D/YYYYYY: 0 任务执行完毕
2020-11-16 17:31:54.597 8466-8626/com.czy.systemlearn D/YYYYYY: 当前线程: IntentService[name]
2020-11-16 17:31:56.600 8466-8626/com.czy.systemlearn D/YYYYYY: 1 任务执行完毕
2020-11-16 17:31:56.603 8466-8626/com.czy.systemlearn D/YYYYYY: 当前线程: IntentService[name]
2020-11-16 17:31:58.606 8466-8626/com.czy.systemlearn D/YYYYYY: 2 任务执行完毕
2020-11-16 17:31:58.613 8466-8466/com.czy.systemlearn D/YYYYYY: onDestroy

源码分析

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;//子线程中创建的Looper对象
    private volatile ServiceHandler mServiceHandler;// 跟子线程中的Looper关联的Hander
    private String mName;//线程名
    private boolean mRedelivery;//决定onStartCommand的返回值(服务是否重启)

    // 处理hander的消息,并通过startId来停止服务
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            //如果服务中所有的startid都被停止了,才会触发onDestroy
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        //name 是线程名称,主要是在调试时方便测试
        mName = name;
    }

  
  //enabled: 如果为true: onStartCommand返回START_REDELIVER_INTENT,否则:START_NOT_STICKY
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //创建一个子线程 里边运行着Looper
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        // 作用:创建Looper 并启动looper循环
        thread.start();
        
        mServiceLooper = thread.getLooper();
        //创建子线程Hander;将hander和HanderThread线程关联起来
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    //在服务的onStartCommand方法被调用
    //作用:通过子线程hander进行消息分发
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }


    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
    //真正处理需要在子线程运行的业务
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

intentService源码比较简单,流程如下:

  1. 在onCreate的时候创建两个对象:一个 handerThread(一个拥有Looper的线程),另一个:ServiceHandler 对象
    (与子线程looper绑定的Hander),
  2. 在onSTartCommand的时候进行mServiceHandler.sendMessag消息分发
  3. 分发的消息在onHandleIntent((Intent)msg.obj); 处理,处理完后通过StopSelf(startId)停止服务
  4. 因为hander机制 决定了任务是需要队列运行的。

六、android 8.0后台限制

限制
Android 8.0对系统进行了优化,为节省资源不允许后台应用创建后台服务,
处于前台时,应用可以自由创建和运行前台与后台 Service。 进入后台时,在一个持续数分钟的时间窗内,应用仍可以创建和使用 Service。 在该时间窗结束后,应用将被视为处于空闲状态。 此时,系统将停止应用的后台 Service,就像应用已经调用 Service 的 Service.stopSelf() 方法一样。

我们看下在8.0之上的手机在后台开启服务的运行情况;

Handler().postDelayed({
    startService(Intent(MainActivity@ this, LearnService::class.java))
},10000)

在后台十秒后自动开启服务,生命周期打印日志如下:

2020-11-17 09:55:29.841 32453-32453/com.czy.systemlearn D/YYYYYY: onCreate
2020-11-17 09:55:29.845 32453-32453/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-17 09:56:21.499 32453-32453/com.czy.systemlearn D/YYYYYY: onDestroy

可以看出:在55分时候创建服务后,在56分时就自动执行了onDestroy

解决
在 Android 8.0 之前,创建前台 Service 的方式通常是先创建一个后台 Service,然后将该 Service 推到前台。 Android 8.0 有一项复杂功能:系统不允许后台应用创建后台 Service。 因此,Android 8.0 引入了一种全新的方法,即 startForegroundService(),以在前台启动新 Service。 在系统创建 Service 后,应用有五秒的时间来调用该 Service 的 startForeground() 方法以显示新 Service 的用户可见通知。 如果应用在此时间限制内调用 startForeground(),则系统将停止此 Service 并声明此应用为 ANR

调用:

 startForegroundService(Intent(MainActivity@ this, LearnService::class.java))

因为需要在五秒内调用startForeground,我们更改下服务的onCreate代码,添加该方法
服务代码:


class LearnService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        "onBind".log()
        return MyBind()
    }

    override fun onUnbind(intent: Intent?): Boolean {
        "onUnbind".log()
        return super.onUnbind(intent)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate() {
        super.onCreate()
        "onCreate".log()
        val channel = NotificationChannel(
            100.toString(),
            "background",
            NotificationManager.IMPORTANCE_MIN
        )
        channel.enableVibration(false)
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        manager.createNotificationChannel(channel)

        val builder: Notification.Builder = Notification.Builder(
            applicationContext, 100.toString()
        ).setContentTitle("正在后台运行")
            .setSmallIcon(R.mipmap.sym_def_app_icon)
        startForeground(1, builder.build())

    }

    override fun onDestroy() {
        super.onDestroy()
        "onDestroy".log()
    }

    // 暴露给 Activity调用的方法
    fun serviceInnerMethods(msg: String) {
        msg.log()
    }

    open inner class MyBind : Binder() {
        val service: LearnService
            get() = this@LearnService
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        "onStartCommand".log()
        return super.onStartCommand(intent, flags, startId)
    }
}

相关文章

  • Android体系复习--Service

    一、Service简介 四大组件之一,是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件...

  • Android Service复习

    Service概念 Service是Android中的四大组件之一,和Activity一样继承自Context,但...

  • android--Service复习

    startService方法启动Service,启动它的组件(如Activity)和Service并没有关联,只有...

  • Android复习之旅--Service

    服务(Service)可以理解为一个没有界面的能够长期在后台运行的Activity。即使启动服务的应用程序被切换掉...

  • android Service复习总结

    1. Service 的 start 和 bind 状态有什么区别? startServiceService 生命...

  • Android Service、IntentService,Se

    导读 移动开发知识体系总章(Java基础、Android、Flutter) Service是什么 LocalSer...

  • Android 基础复习篇service

    * 1.service简介 Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需...

  • Android体系复习---广播

    一、广播简介 Android 应用与 Android 系统和其他 Android 应用之间可以相互收发广播消息,这...

  • android体系复习---Activity

    一、什么是Activity Activity是与用户交互的入口点。它表示拥有界面的单个屏幕。例如,电子邮件应用可能...

  • Android Service用法知识点的讲解

    Android Service 学习Service相关知识点: android service 的基础知识,生命周...

网友评论

      本文标题:Android体系复习--Service

      本文链接:https://www.haomeiwen.com/subject/rstybktx.html