欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

温习Android基础知识——《第一行代码(第三版)》读书笔记 Chapter 10 Service

程序员文章站 2022-03-07 15:37:06
第十章:后台默默的劳动者,探究Service目录第十章:后台默默的劳动者,探究ServiceService是什么安卓异步消息处理机制子线程中无法更新UI异步消息处理机制组成1.Message2.Handler3.MessageQueue4.Looper流程AsyncTaskService的用法基本用法与Activity进行通信Service的生命周期前台ServiceIntentServiceService是什么Service是Android中实现程序后台运行的解决方案,它适合执行那些不需要和用户交互...

第十章:后台默默的劳动者,探究Service

Service是什么

Service是Android中实现程序后台运行的解决方案,它适合执行那些不需要和用户交互且还要求长期运行的任务。
Service不依赖用户界面,即使程序被切到后台它也能正常运行。
Service并不是运行在一个独立的进程当中的,而是依赖于创建自己的应用程序进程。当一APP被杀掉时,所以依赖于此的Service都将停止运行。
尽管Service运行在后台,但它自己并不会自动开启线程,所有的Service代码都是默认运行在主线程的。

安卓异步消息处理机制

子线程中无法更新UI

Android的UI也是线程不安全的,即更新UI的操作只能在主线程中进行,否则就会出现异常。

Why?
在多线程中并发访问,可能会导致UI控件处于不可预期的状态。而对UI上锁会导致让UI控件的使用变得复杂,还会阻塞某些进程的运行。

异步消息处理机制

针对有时我们必须在子线程中执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件这一需求,Android提供了一套异步消息处理机制。

组成

1.Message

Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间传递数据。作用类似于信封,将内部信息封装。

2.Handler

Handler,即处理者,负责发送和处理消息。

3.MessageQueue

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

4.Looper

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

流程

将Message从子线程传到主线程,从不可更新UI到可更新UI

首先在主线程中创建一个Handler对象,并重写handleMessage方法。然后当子线程中需要一个UI操作时,就创建一个Message对象,并通过Handler发生出去。之后这条消息会被添加到MessageQueue中,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后发回Handler的handMessage方法中(在主线程),根据处理结果执行UI操作。

AsyncTask

为了方便我们的使用,Android将异步消息处理机制做了很好的封装,也就是AsyncTask。
AsyncTask是一个抽象类,我们经常需要重写onPreExecute(), doInBackground(Params…), onProgressUpdate(Progress…), onPostExecute(Result)等方法。
onPreExecute()在后台任务执行前被调用,可以用于初始化界面等操作。
doInBackground(Params…)在子线程中运行,在这里处理耗时任务,也可以调用publishProgress方法反馈当前任务的执行进度。
onProgressUpdate(Progress…)在后台调用publishProgress方法后调用,可以更新UI界面中的进度。
onPostExecute(Result)在任务执行完毕并通过return语句返回后调用。

Service的用法

基本用法

新建一个Service后,常常需要重写以下方法:
在Service被创建时调用的onCreate方法,在每次启动Service时调用的onStartCommand方法,在Service被销毁时调用的onDestroy方法。
启动一个Service同样使用Intent。

注意,而为了防止恶意应用程序在后天占用手机资源而导致的手机变卡,从Android 8.0系统开始,应用的后台功能被大幅削减。现在只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时可能会被系统回收。

与Activity进行通信

Service可以与应用程序内任何一个Activity绑定,当Activity和Service进行绑定之后,就可以调用该Service里Binder提供的方法了。
onBind方法是Service里的唯一一个抽象方法,可以用来返回一个Binder对象,利用该Binder对象,Activity就能够了解Service。
绑定和取消绑定的方法分别是bindService和unbindService。

Service的生命周期

一旦在项目的任何位置调用了Context的startService方法,相应的Service就会启动,如果还未创建,会先执行onCreate,之后回调onStartCommand方法。Service启动之后会一直保持运行状态,直到stopSevice或stopself方法被调用,或者被系统回收。

注意,每个Service只会存在一个实例,重复调用startService方法不会创建多个,只会调用onStartCommand方法,而无论我们执行了多少次startService方法,只要调用一次stopSevice或stopself方法就可以停止Service。

还可以调用Context的bindService方法来获取一个Service的持久连接,这时就会回调Service中的onBind方法。同样,如果Service还未创建,会先执行onCreate。之后,调用方就可以获取到onBind方法返回的IBinder对象,并借其与Service进行通信。只要连接没有断开,Service就会一直保持运行状态,直到被系统回收。

当对一个Service既调用了startService方法,又调用了bindService方法,又当如何销毁?
一个Service只要被启动或被绑定之后 就会处于运行状态,只有以上两个条件同时不满足时才能被销毁。所以这时我们要同时调用stopService和unbindService方法,onDestroy方法才会执行。

前台Service

如果需要Service一直保持运行状态,就可以考虑使用前台Service。
它和普通Service最大的区别就是它是可见的,会有一个正在运行的图标在系统的状态栏显示,类似于通知的效果。
正因为其是可见的,不但Android系统不会回收,还能让用户清楚应用在后台运行的情况。
调用startForeground方法即可启动前台Service。
Android 9.0系统开始,使用前台Service必须在AndroidManifest注册文件中声明权限。

class MainActivity : AppCompatActivity() {

    lateinit var downloadBinder: MyService.DownloadBinder

    private val connection = object : ServiceConnection{
		//在成功绑定时调用
        override fun onServiceConnected(name: ComponentName?, service: IBinder) {
            downloadBinder = service as MyService.DownloadBinder
            downloadBinder.startDownload()
            downloadBinder.getProgress()
        }
		//在Service的创建进程奔溃或被杀掉时调用
        override fun onServiceDisconnected(name: ComponentName?) {

        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        startService.setOnClickListener {
            val startIntent = Intent(this, MyService::class.java)
            startService(startIntent)
        }

        stopService.setOnClickListener {
            val stopIntent = Intent(this, MyService::class.java)
            stopService(stopIntent)
        }

        bindService.setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }

        unbindService.setOnClickListener {
            unbindService(connection)
        }

        startIntentServiceBtn.setOnClickListener {
            Log.d("MainActivity", "Thread id is ${Thread.currentThread().name}")
            val intent = Intent(this, MyIntentService::class.java)
            startService(intent)
        }

    }
}
class MyService : Service() {

    private val TAG = "MyService"

    private val mBinder = DownloadBinder()

    class DownloadBinder : Binder(){
        fun startDownload(){
            Log.d("MyService","startDownload executed" )
        }

        fun getProgress():Int{
            Log.d("MyService", "getProgress executed")
            return 0
        }
    }

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

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG,"onCreate executed")
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            val channel = NotificationChannel("my_service","前台通知",NotificationManager.IMPORTANCE_DEFAULT)
            manager.createNotificationChannel(channel)
        }
        val intent = Intent(this, MainActivity::class.java)
        val pi = PendingIntent.getActivity(this,0,intent,0)
        val notification = NotificationCompat.Builder(this, "my_service")
            .setContentTitle("这是一个通知")
            .setContentText("一个短小且持久的通知")
            .setSmallIcon(R.drawable.small_icon)
            .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.large_icon))
            .setContentIntent(pi)
            .build()
        startForeground(1, notification)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand executed")
        return super.onStartCommand(intent, flags, startId)
    }


    override fun onDestroy() {
        Log.d(TAG, "onDestroy executed")
        super.onDestroy()
    }
}

IntentService

由于Service的代码都是默认运行在主线程当中的,如果直接在Service里处理一些耗时的逻辑,很容易出现ANR。
Android为我们提供了IntentService来创建一个异步的,会自动停止的Service。

class MyIntentService : IntentService("MyIntentService") {
	//该方法会运行在子线程中,可被用来处理耗时的逻辑
    override fun onHandleIntent(intent: Intent?) {
        Log.d("MyIntentService", "Thread id is ${Thread.currentThread().name}")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("MyIntentService", "onDestroy executed")
    }
}

本文地址:https://blog.csdn.net/qq_45254908/article/details/107525597