zoukankan      html  css  js  c++  java
  • Android-认识Service

    Android-认识Service

    学习自

    郭霖的博客
    https://developer.android.google.cn/reference/android/app/Service#WhatIsAService
    https://developer.android.google.cn/guide/components/services

    What is Service

    A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding declaration in its package's AndroidManifest.xml. Services can be started withContext.startService() and Context.bindService().
    Service 是一个应用程序组件,其代表着一个应用程序希望在不与用户交互的情况下执行一个长时间运行的操作或者为其他的应用程序提供功能。每一个Service都必须在Manifest.xml文件中有一个与之相对应的 <serivce> 标签。Service能有以 Context.startServiceContext.bindService 的方式启动。

    Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work.
    请注意服务像其他的应用程序对象一样,运行在其托管进程的主线程中。这意味着,如果你的服务打算做任何CPU密集型任务(比如播放MP3)或者堵塞线程的操作(比如链接网络).那么它应该生成自己的线程来做这些工作。

    理解官方文档

    从官方文档我们了解到,服务是一个运行与后台的并且是运行在主线程中的应用程序组件,服务经常会被用来做一些长时间运行的操作,但是如果是CPU密集型的操作或者网络请求操作的话,需要再开启线程来完成这些操作。

    我们可以通过 startServicebindService 两种方式来开启服务。

    服务的基本用法

    首先呢,我们先来新建一个服务并启动它,与Activity相同,同样是通过Intent来启动服务。

    Step 1 新建一个类来继承Service

    class TestService : Service() {
    
        override fun onBind(intent: Intent?): IBinder {
            TODO("not implemented")
        }
    
        override fun onCreate() {
            super.onCreate()
            "onCreate".logE()
        }
    
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            "onStartCommand".logE()
            return super.onStartCommand(intent, flags, startId)
        }
    
        override fun onDestroy() {
            super.onDestroy()
            "onDestroy".logE()
        }
    }
    

    Step2 在Manifest文件中声明

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!--声明服务-->
        <service android:name=".TestService" />
    </application>
    

    Step3: 开启服务
    界面布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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"
        android:gravity="center_vertical|center_horizontal"
        android:orientation="vertical"
        tools:context="top.littledavid.studyservice.MainActivity">
    
        <Button
            android:id="@+id/startBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="startService"
            android:text="Start Service"
            android:textAllCaps="false" />
    
        <Button
            android:id="@+id/stopBtn"
            android:onClick="stopService"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="Stop Service"
            android:textAllCaps="false" />
    
    </LinearLayout>
    
    

    Activity

    //开启服务
    fun startService(view: View) {
        val testServiceIntent = Intent(this, TestService::class.java)
        this.startService(testServiceIntent)
    }
    
    //关闭服务
    fun stopService(view: View) {
        val stopTestServiceIntent = Intent(this, TestService::class.java)
        this.stopService(stopTestServiceIntent)
    }
    

    Step 4 运行程序查看Log

    当点击Start Service按钮的时候Log如下:

    onCreate
    onStartCommand
    

    但是当我们在点击一次StartService按钮的时候,Log如下

    onStartCommand
    

    这是因为当我们在点击第一次Start Service的时候因为Service还有创建,这时候需要先创建服务,所以执行了 onCreate 声明周期方法,然后紧接着这时候Service已经创建完成开始运行,执行了 onStartCommand 声明周期方法,当我们第二次点击Start Service按钮的时候,因为此时刚刚创建的Service并没有被销毁,所以仅仅执行了 onStartCommand 方法,onStartCommand 可以多次执行,但是onCreate方法只会在创建Service的时候执行。

    当我们点击 Stop Service的时候然后再点击Start Service的时候Log如下:

    onDestroy
    onCreate
    onStartCommand
    

    当点击 Stop Service 服务会被销毁,然后如果我们再次点击 Start Service 的时候,因为服务已经被销毁所以,服务重新被创建,onCreate方法再次执行,这正好印证了我们上面的说法。

    Start方式开启Service的生命周期

    开启Service有两种方式,一种是startService,一种是bindService,上面例子中开启服务是通过startService的方式,start的方式开启服务与bind方式开启的Service的声明周期不太相同,我们先来看一下start方式的生命周期。

    image.png | left | 424x580

    上图就是startService的方式开启的服务的声明周期,当服务创建以后如果不是内存不足服务被系统杀死,或者客户端停止服务,那么服务将会一直存在,尽管没有执行任何的业务。

    bindService-Service与Activity通信

    在上面我们学习了,通过start的方式启动Activity,但是start方式开启服务的Activity与Service之间的联系实在是太过于松散了,Activity不能够对服务进行有效的控制,可不能获取服务的实时状态,start方式仅仅能够控制服务的开启和技术,然而更具体地控制服务的话就没有办法了,那么有没有其他的办法呢?有的!那就是通过bind的方式开启Service。

    我们继续基于我们的TestService来修改,在TestService中一直有一个 onBind 方法我们没有用到,这个方法就是用来与Activity建立联系的方法。

    Step 1 修改原来的TestService

    class TestService : Service() {
        private val mTestBinder = TestBinder()
    
        /**
         * 返回Binder
         * */
        override fun onBind(intent: Intent?): IBinder {
            return mTestBinder
        }
    
        override fun onCreate() {
            super.onCreate()
            "onCreate".logE()
        }
    
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            "onStartCommand".logE()
            return super.onStartCommand(intent, flags, startId)
        }
    
        override fun onDestroy() {
            super.onDestroy()
            "onDestroy".logE()
        }
        //建立我们的Binder类
        inner class TestBinder : Binder() {
            fun startDownload() = "Downloading".logE()
        }
    }
    

    在上面的代码中,建立了一个继承自Binder的内部类,并在在 onBind 方法执行的时候,将TestBinder的实例返回了出去,在Activity中将会通过这个类来与Service交互。这里仅仅是模拟一下,所以只打了个Log。

    Step 2 向布局文件中在添加几个Button,用来与服务交互

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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"
        android:gravity="center_vertical|center_horizontal"
        android:orientation="vertical"
        tools:context="top.littledavid.studyservice.MainActivity">
        <!--OLD CODE-->
        <Button
            android:id="@+id/bindingServiceBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="bindingService"
            android:text="Binding Service"
            android:textAllCaps="false" />
    
        <Button
            android:id="@+id/unbindingBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:onClick="unbindingService"
            android:text="Unbinding Service"
            android:textAllCaps="false" />
    
        <Button
            android:id="@+id/downloadBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:onClick="downloadFile"
            android:text="Download"
            android:textAllCaps="false" />
    
    </LinearLayout>
    
    

    Step 3 调用Service

    class MainActivity : AppCompatActivity() {
        private var mBinder: TestService.TestBinder? = null
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
        }
        //....OLD CODE
        
        //绑定服务
        fun bindingService(view: View) {
            var bindingIntent = Intent(this, TestService::class.java)
            this.bindService(bindingIntent, serviceConnection, android.app.Service.BIND_AUTO_CREATE)
        }
        //解绑服务
        fun unbindingService(view: View) {
            this.mBinder = null
            this.unbindService(serviceConnection)
        }
        //通过Binder与Service交互
        fun downloadFile(view: View) {
            mBinder?.startDownload() ?: "mBinder has been released".logE()
        }
        /**
         * ServiceConnection 是一个用来监测Service状态的接口
         * 当与Service建立联系后,会调用相应的方法来通知Activity
         * */
        private val serviceConnection = object : ServiceConnection {
    
            override fun onServiceDisconnected(name: ComponentName?) {
            }
            //在与Service建立链接后会执行此方法,在此方法中我们可以获取到对应服务的Binder
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                this@MainActivity.mBinder = service as TestService.TestBinder
                "链接建立".logE()
            }
        }
    }
    

    Step4 运行程序查看Log bindService -> Download-> unbindService->Download

    onCreate
    链接建立
    Downloading
    onDestroy
    mBinder has been released
    

    在Activity中的代码中,我们建立了一个 ServiceConnection 接口的匿名实现类, 在此接口我们用到了 onServiceConnected 方法。在这个方法中,有个 service IBinder类型的参数,该参数就是我们该TestService服务中声明的TestBinder的实例,我们将它向下转型后,就可以获取到TestBinder的实例。但是 onServiceConnected 方法时在与Service建立链接后采用获取到的。

    当我们点击了BindService按钮后,会执行bindService方法,该方法有三个参数 第一个开启服务的Intent,第二个是 ServiceConnection 的匿名实现类(在与服务完成连接后,将会调用onServiceConnected方法),第三个是一个Flag位,BIND_AUTO_CREATE 表示,当链接到服务后服务会自动创建并执行 onCreate 方法。

    当执行完bindService我们就可以拿到Binder对象了,这时候我们就可以与Service进行交互了。

    如果想要解除绑定调用unbindService方法即可。

    Bind方式开启服务的声明周期

    image.png | left | 422x619

    上图是通过Bind的方式开启的服务的声明周期,首先不相同的,Bind方式的声明周期并没有调用 onStartCommand 声明周期方法,仅仅是调用了onCreate方法,然就紧接着调用了onBind 方法获取Binder。这里我们需要注意一下。

    并且Bind方式开启的服务的存活时间相对于Start方式开启的Service比较特殊,众所周知,Service并不是为某一个特定了Activity而提供的,而是在任意的地方都可以开启和结束。对同一个Service可以Bind多次,所以当有任何一个bind操作没有被unbind的话Service是不会被销毁的(即只存在任何与Service的链接,那么服务都不会被销毁),所以我们要注意Unbind Service,通常的做法是: 在Activity的onCreate方法中Bind Service 然后在Activity的onDestroy方法中Unbind Service。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var bindingIntent = Intent(this, TestService::class.java)
        this.bindService(bindingIntent, serviceConnection, android.app.Service.BIND_AUTO_CREATE)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        this.unbindService(this.serviceConnection)
    }
    

    结语

    本文主要介绍了如何通过start方式和bind的方式开启服务以及各自的特点。正好篇幅也比较长了,正好就到此为止,接下来的内容将会在新的一章中继续学习,在下一张,将会学习start和bind结合的方式来开启服务。

  • 相关阅读:
    50.EasyGank妹纸App
    项目开发规范,数据库设计规范
    用外部物理路由器时与外部dhcp服务时怎样使用metadata服务(by quqi99)
    [报错处理]Python Requests
    [译]为什么在__new __()后总是调用__init __()?
    '>/dev/null 2>&1' 是什么意思?
    “努力就会成功”
    [译]如何在红帽系统(RHEL)上源码安装python3?
    [译]在你的GitHub主页固定仓库
    [译]拉取仓库远程分支
  • 原文地址:https://www.cnblogs.com/slyfox/p/9366480.html
Copyright © 2011-2022 走看看