Android Service与IntentService详解与使用

一、介绍        

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

服务有三种不同的类型:

前台:

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

后台:

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

绑定:

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

服务和线程之间进行选择

        服务是一种即使用户未与应用交互也可在后台运行的组件,因此,只有在需要服务时才应创建服务。

        如果您必须在主线程之外执行操作,但只在用户与您的应用交互时执行此操作,则应创建新线程。如果您只是想在 Activity 运行的同时播放一些音乐,则可在 onCreate() 中创建线程,在 onStart() 中启动线程运行,然后在 onStop() 中停止线程。您还可考虑使用 AsyncTask 或 HandlerThread,而非传统的 Thread 类

注意:

        如果您确实要使用服务,则默认情况下,它仍会在应用的主线程中运行,因此,如果服务执行的是密集型或阻止性操作,则您仍应在服务内创建新线程。

关于进程的耗时:

        有人会说进程在后台的耗时是15S,这个时间是怎么来的?这个时间是在Framework中定义的,在包括UI线程和Service的liveLongtime已定义好了,在APP层是无法修改,除非你重新编译AOSP包,然后烧制一个新的rom,这个已涉及到包的定制。有兴趣可以自己查看aosp的源码

如何创建

        如要创建服务,您必须创建 Service 的子类(或使用它的一个现有子类)。在实现中,您必须重写一些回调方法,从而处理服务生命周期的某些关键方面,并提供一种机制将组件绑定到服务(如适用

onStartCommand()

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

onBind()

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

onCreate()

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

onDestroy()

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

注意:

        如果组件通过调用 startService() 启动服务(这会引起对 onStartCommand() 的调用),则服务会一直运行,直到其使用 stopSelf() 自行停止运行,或由其他组件通过调用 stopService() 将其停止为止。

        如果组件通过调用 bindService() 来创建服务,且未调用 onStartCommand(),则服务只会在该组件与其绑定时运行。当该服务与其所有组件取消绑定后,系统便会将其销毁。

如何接入:

如要声明服务,请添加 <service> 元素作为 <application> 元素的子元素。

服务的启动

服务启动有两种方式,startService() 和bindService()

startService:

​        服务启动后,其生命周期即独立于启动它的组件。即使系统已销毁启动服务的组件,该服务仍可在后台无限期地运行。因此,服务应在其工作完成时通过调用 stopSelf() 来自行停止运行,或者由另一个组件通过调用 stopService() 来将其停止

        ​startService后,Service先调用onCreate(),再调用onStartCommand,如果该服务已启动,再启动也只会调用onStartCommand()

        var service: Intent = Intent(context, HelloService::class.java)
        bind.btnStart.setOnClickListener {

            startService(service)


        }

当服务被销毁了,才会重新执行oncrete()

       var service: Intent = Intent(context, HelloService::class.java)
    

        bind.btnStop.setOnClickListener {
            stopService(service)
        }

如果你是继承了IntentService,那么将会调用到onHandleIntent。但是IntentService会自动调用ondestory()。也就是说,IntentService启动就是一套流程走完,然后调用stopSelf()

 
 

Fragment和Activity如何让Service去工作?

知道了Service与IntentService的工作流程,那么我们应该了解了如何去让Service为我们做一些耗时。

Service:

 inline fun Service.showToast(msg:String) {Toast.makeText(applicationContext,msg,Toast.LENGTH_SHORT).show()}  
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        MyLog.log(TAG, "onStartCommand")


        var bundle = intent?.extras;
        var title=bundle?.getString("title","")
        runOnUiThread {
            showToast(title!!)

        }

        return super.onStartCommand(intent, flags, startId)

    }
      var service: Intent = Intent(context, HelloService::class.java)
        var bundle=Bundle();
        bundle.putString("title","nihao")
        service.putExtras(bundle)
        bind.btnStart.setOnClickListener {

            startService(service)


        }

IntentService:

 inline fun IntentService.showToast(msg:String) {Toast.makeText(applicationContext,msg,Toast.LENGTH_SHORT).show()}



    override fun onHandleIntent(intent: Intent?) {
        MyLog.log(TAG, "onHandleIntent")

        var bundle = intent?.extras;
        var title=bundle?.getString("title","")
        runOnUiThread {
            showToast(title!!)

        }

    }

这里为类扩展了一个函数showToast();

但是IntentService,调用完,就会销毁。

创建绑定服务:bindService

        绑定服务允许应用组件通过调用 bindService() 与其绑定,从而创建长期连接。此服务通常不允许组件通过调用 startService() 来启动它。

        如需与 Activity 和其他应用组件中的服务进行交互,或需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定服务。

​        如要创建绑定服务,您需通过实现 onBind() 回调方法返回 IBinder,从而定义与服务进行通信的接口。然后,其他应用组件可通过调用 bindService() 来检索该接口,并开始调用与服务相关的方法。服务只用于与其绑定的应用组件,因此若没有组件与该服务绑定,则系统会销毁该服务。您不必像通过 onStartCommand() 启动的服务那样,以相同方式停止绑定服务。

实战:

        通过以上我们可以了解到,数据的获取,在onBind获取。如果想要和其他交互,可以通过IBinder。


    override fun onBind(p0: Intent?): IBinder? {
        bind = MyBinder().asBinder()
        bindOne=MyTestBind()
        MyLog.log(TAG, "onBind")

        return bindOne
    }

        有人很好奇,这个IBinder如何获取?甚至有人了解AIDL,这个和AIDL有什么?其实Service是一个服务,在Android体系中,所有的service同学都是通过Binder。又因为Activity归AMS管理,AMS想要和Service通讯,就需要Binder来接入,我们想要和谁通讯,就需要获取这个对象的Binder,这就是他的信使,把我们的信息交给信使。

这边将介绍两种创建IBinder对象

1.AIDL

直接通过通过快捷键new一个。

        创建完,删除里面生成的代码,写入自己的代码,记住:AIDL是接口,所有在使用要小心,准好以后,build->make一下项目,IDE会自动创建对象的JAVA文件。如果对AIDL还不懂,可以查下这篇文章:

Android Binder,AIDL跨进程通讯详解与实现,看一遍就懂_蜗牛、Z的博客-CSDN博客

    inner class MyBinder : HelloServiceBinder.Stub() {

        var info: String = ""
        override fun add(msg: String?) {

            showToast("add收到你的${msg}")
            info = msg!!
        }

        override fun getTitle(): String {

            return "我已收到你的${info}"
        }
    }

    private inner class MyConnect : ServiceConnection {

        override fun onServiceConnected(component: ComponentName?, bind: IBinder?) {

            if (bind is HelloService.MyBinder) {
                bind.add("onServiceConnected")

                showToast(bind.title)
            } else if (bind is HelloService.MyTestBind) {
                bind.add("发送消息")
                showToast(bind.getTitle())
            }
        }

        override fun onServiceDisconnected(p0: ComponentName?) {
            showToast("断开")
        }
    }

这样,就可以通过IBinder来和service交互。

第二种:继承Binder

这就很简单了,直接继承Binder。

    inner class MyTestBind : Binder() {

        var info: String = ""
        fun add(msg: String?) {

            showToast("add收到你的${msg}")
            info = msg!!
        }

        fun getTitle(): String {

            return "我已收到你的${info}"
        }
    }

总结        

1.startService与bindService的对比

2.bindService:可以多次bindService,但是onBind只会调用一次,但是解绑UNBindService只有一次,否则会报错。所以在绑定与解绑回调的时候,可以通过一个字段记录,方便操作绑定,

猜你喜欢

转载自blog.csdn.net/qq36246172/article/details/129951993
今日推荐