当前位置: 移动技术网 > 移动技术>移动开发>Android > 温习Android基础知识——《第一行代码(第三版)》读书笔记 Chapter 10 Service

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

2020年07月24日  | 移动技术网移动技术  | 我要评论

第十章:后台默默的劳动者,探究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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网