Android Service学习笔记

1、Service介绍

Android Service(服务)是 Android 四大组件之一,主要作用是执行后台操作。它是一个后台运行的组件,执行长时间运行且不需要用户交互的任务。即使应用被销毁也依然可以工作。

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

服务基本上包含两种状态:

  • Started :当 Android 的应用程序组件,如活动,通过 startService() 启动了服务,则服务是 Started 状态。一旦启动,服务可以在后台无限期运行,即使启动它的组件已经被销毁。
  • Bound :当 Android 的应用程序组件通过 bindService() 绑定了服务,则服务是 Bound 状态。Bound 状态的服务提供了一个客户服务器接口来允许组件与服务进行交互,如发送请求,获取结果,甚至通过 IPC 来进行跨进程通信。

服务拥有生命周期方法,可以实现监控服务状态的变化,可以在合适的阶段执行工作。例如,onStartCommand() 方法会在其他组件(如活动)通过调用 startService() 来请求启动服务时被系统调用。

要创建服务,你需要创建一个继承自 Service 基类或者它的已知子类的 Java 类。例如,下面是一个简单的 Service 类的示例:

java 复制代码
public class MyService extends Service {
    @Override
    public void onCreate() {
        // 服务被创建时调用
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 其他组件通过调用 startService() 来请求启动服务时,系统调用该方法
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        // 当其他组件想要通过 bindService() 来绑定服务时,系统调用该方法
        return null;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // 当客户中断所有服务发布的特殊接口时,系统调用该方法
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        // 当服务不再有用或者被销毁时,系统调用该方法
    }
}

与之对应的Kotlin代码如下:

java 复制代码
class MyService : Service() {

    // 当其他组件通过bindService来绑定服务时被调用
    override fun onBind(intent: Intent): IBinder {
        // 返回一个IBinder对象
        TODO()
    }

    // 在Service被创建时调用
    override fun onCreate() {
        super.onCreate()
    }

    // 当其他组件通过startService来请求启动Service时,该方法被调用
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }
    // 当服务不再有用或者被销毁时,系统调用该方法
    override fun onDestroy() {
        super.onDestroy()
    }
    // 当客户中断所有服务发布的特殊接口时,系统调用该方法
    override fun onUnbind(intent: Intent?): Boolean {
        return super.onUnbind(intent)
    }
}

onCreate()方法是在Service第一次创建的时候调用的,而onStartCommand()方法则在每次启动Service的时候都会调用。

2、异步消息处理机制

Android的异步消息处理主要由以下四个部分组成:Message、Handler、MessageQueue和Looper。

  1. Message :Message是在线程之间传递的消息,它可以携带少量的信息,用于在不同线程之间传递数据。我们可以使用Message的whatarg1arg2字段来携带整型数据,或者使用obj字段来携带一个Object对象。

  2. Handler :Handler主要用于发送和处理消息。我们可以使用Handler的sendMessage()post()等方法来发送消息,发送出的消息会最终传递到Handler的handleMessage()方法中进行处理。

  3. MessageQueue:MessageQueue是消息队列,主要用于存放所有通过Handler发送的消息。这些消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。

  4. Looper :Looper是每个线程中的MessageQueue的管家,当调用Looper的loop()方法后,就会进入一个无限循环中。每当发现MessageQueue中存在一条消息时,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。

异步消息处理的整个流程:首先,在主线程中创建一个Handler对象,并重写handleMessage()方法。然后,当子线程需要进行UI操作时,创建一个Message对象,并通过Handler将这条消息发送出去。这条消息会被添加到MessageQueue的队列中等待被处理。而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。由于在Handler的构造函数中我们传入了Looper.getMainLooper(),所以此时handleMessage()方法中的代码会在主线程中运行,这样我们就可以在这里进行UI操作了。

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    private val updateText = 1 // 用于标识哪个动作
    private lateinit var textView: TextView
    // Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般
    //是使用Handler的sendMessage()方法、post()方法等,而发出的消息经过一系列地辗
    //转处理后,最终会传递到Handler的handleMessage()方法中
    private val handler = object : Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message) {
            // 在主线程中进行UI操作
            when(msg.what){
                updateText -> textView.text = msg.obj.toString()
            }
        }
    }

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

        textView = findViewById<TextView>(R.id.textView)
        val changeTextBtn = findViewById<Button>(R.id.changeTextBtn)
        changeTextBtn.setOnClickListener{
            // 开启一个新的线程
            thread {
                // Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间传递数据
                val message = Message()
                message.what = updateText
                message.arg1 = 1 // 携带整型数据
                message.arg2 = 2
                message.obj = "你好啊" // 携带对象类型数据
                // 发送Message对象
                handler.sendMessage(message)
                // 通过post方法发送一个 Runnable 对象,这个 Runnable 对象会被添加到消息队列的尾部
                handler.post(Runnable { Log.d("Handler", "post") })
            }
        }
    }
}
3、Service的使用

例如一个后台下载服务的简单实现

kotlin 复制代码
class MyService : Service() {
    private val mBinder = DownloadBinder()
    // 当其他组件通过bindService来绑定服务时被调用
    override fun onBind(intent: Intent): IBinder {
        // 返回一个IBinder对象
        return mBinder
    }

    // 在Service被创建时调用
    override fun onCreate() {
        super.onCreate()
    }

    // 当其他组件通过startService来请求启动Service时,该方法被调用
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }
    // 当服务不再有用或者被销毁时,系统调用该方法
    override fun onDestroy() {
        super.onDestroy()
    }
    // 当客户中断所有服务发布的特殊接口时,系统调用该方法
    override fun onUnbind(intent: Intent?): Boolean {
        return super.onUnbind(intent)
    }

    class DownloadBinder : Binder() {
        fun startDownload() {
            Log.d("MyService", "startDownload executed")
        }
        fun getProgress(): Int {
            Log.d("MyService", "getProgress executed")
            return 0
        }
    }
}
kotlin 复制代码
class MainActivity2: AppCompatActivity(){
    private lateinit var mBinder: MyService.DownloadBinder
    // ServiceConnection用于监听服务的状态, 以便于在服务绑定成功时执行相应的逻辑
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mBinder = service as MyService.DownloadBinder
            mBinder.startDownload()
            mBinder.getProgress()
        }

        override fun onServiceDisconnected(name: ComponentName?) {
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 启动服务
        findViewById<Button>(R.id.startServiceBtn).setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            startService(intent)
        }
        // 停止服务
        findViewById<Button>(R.id.stopServiceBtn).setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            stopService(intent)
        }
        // 绑定服务,这样Activity和Service就建立了关联
        findViewById<Button>(R.id.bindServiceBtn).setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            // 传入参数:intent, ServiceConnection, flags,
            // flags一般传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service
            bindService(intent, connection, BIND_AUTO_CREATE)
        }
        // 解绑服务
        findViewById<Button>(R.id.unbindServiceBtn).setOnClickListener {
            unbindService(connection)
        }
    }
}
4、前台Service

从Android 8.0系统开始,只有当应用保持在前台可见状态的情况下,Service

才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。而如果你希望Service能够一直保持运行状态,就可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它一直会有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。

kotlin 复制代码
class FrontService : Service(){
    override fun onCreate() {
        super.onCreate()
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channel = NotificationChannel(
            "my_service",
            "前台Service通知",
            NotificationManager.IMPORTANCE_DEFAULT
        )
        manager.createNotificationChannel(channel)
        val intent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
        val notification = NotificationCompat.Builder(this, "my_service")
            .setContentTitle("Content title")
            .setContentText("content text")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher_round))
            .setContentIntent(pendingIntent)
            .build()
        // 第一个参数是通知的id,类似于notify()方法的第一个参数;第二个参数则是
        //构建的Notification对象。调用startForeground()方法后就会让MyService变成一个前
        //台Service,并在系统状态栏显示出来。
        startForeground(1, notification)
    }
    override fun onBind(intent: Intent?): IBinder? {
        TODO("Not yet implemented")
    }

}

这里需要声明权限,同时指定Service的foregroundServiceType(根据实际选择)

kotlin 复制代码
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<service android:name=".FrontService"
            android:enabled="true"
            android:exported="true"
            android:foregroundServiceType="mediaPlayback"/>
4、IntentService

Service中的代码都是默认运行在主线程当中的,如果直接在Service里处理一些耗时的逻辑,就很容易出现ANR(Application Not Responding)的情况。

应该在其他线程处理耗时操作,如下所示。

kotlin 复制代码
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        thread {
            // 开启一个线程执行耗时操作
            stopSelf() // 操作执行完毕关闭Service
        }
        return super.onStartCommand(intent, flags, startId)
    }

Android提供了IntentService,这种Service在执行完后自动销毁。

kotlin 复制代码
class MyIntentService: IntentService("MyIntentService"){
    // 新增了一个方法,该方法已经在子线程中执行,所以将耗时操作放在这里
    override fun onHandleIntent(intent: Intent?) {
        TODO("Not yet implemented")
    }
    // 其他方法和普通Service一样
    override fun onCreate() {
        super.onCreate()
    }
}
相关推荐
guoruijun_2012_412 分钟前
fastadmin多个表crud连表操作步骤
android·java·开发语言
Winston Wood12 分钟前
一文了解Android中的AudioFlinger
android·音频
金星娃儿17 分钟前
MATLAB基础知识笔记——(矩阵的运算)
笔记·matlab·矩阵
一只特立独行的程序猿19 分钟前
关于GCC内联汇编(也可以叫内嵌汇编)的简单学习
汇编·学习·gcc
虾球xz25 分钟前
游戏引擎学习第10天
学习·游戏引擎
Chef_Chen28 分钟前
从0开始学习机器学习--Day25--SVM作业
学习·机器学习·支持向量机
L_cl35 分钟前
Python学习从0到1 day28 Python 高阶技巧 ⑧ 递归
学习
vortex51 小时前
Vim 编辑器学习笔记
学习·编辑器·vim
源于花海1 小时前
论文学习(四) | 基于数据驱动的锂离子电池健康状态估计和剩余使用寿命预测
论文阅读·人工智能·学习·论文笔记
心怀梦想的咸鱼1 小时前
Ue5 umg学习(一)
学习·ue5