Android入门(Service的使用)
Service
service是android实现程序后台运行的解决方案。
当然它并不是独立运行,而是依赖于应用程序,应用程序停止时,他也会一起停止。Service并不会自动开启线程,所有的代码都是默认运行在主线程当中的。
Android多线程编程
在kotlin中开启线程的方式更加简单。它为你定义了顶层方法
thread{//方法体}
直接在其中写内容,然后他会被拉起为一个新线程。
- 更新UI:和许多其他的GUI库一样,Android的UI也是线程不安全的。也就是说,如果想要更新应用程序里的UI元素,必须在主线程中进行,否则就会出现异常。
- 所以android提供了一套异步消息处理机制:解决了在子线程进行UI操作的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class MainActivity : AppCompatActivity() {
val updateText = 1
val handle = object :Handler(Looper.getMainLooper()){
//在这里处理UI的异步操作
override fun handleMessage(msg: Message) {
when(msg.what){
updateText ->{
binding.textView.text = "Nice to meet you"
}
}
}
}
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.changeTextBtn.setOnClickListener {
thread {
val msg = Message()
msg.what = updateText
handle.sendMessage(msg)//将message对象发送过去
}
}
}
}
异步消息处理机制原理
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper
- Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间传递数据。上一小节中我们使用到了Message的what字段,除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。 - Handler
Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法、post()方法等,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。 - MessageQueue
MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。 - Looper
Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入一个无限循环当中,然后每当发现MessageQueue中存在一条消息时,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。
使用AsyncTask
借助AsyncTask,即使你对异步消息处理机制完全不了解,也可以十分简单地从子线程切换到主线程
基本用法:AsyncTask是一个抽象类,我们需要去继承他,同时它需要接收三个参数:
- Params,在执行时需要传入的参数,用于在后台任务中使用
- Progress,在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
- Result。当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
样例:常用重写的四个方法:1
2
3class DownloadTask : AsyncTask<Unit, Int, Boolean>() {
...
}
onPreExecute()
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。doInBackground(Params…)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成,就可以通过return语句将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Unit,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress (Progress…)方法来完成。onProgressUpdate(Progress…)
当在后台任务中调用了publishProgress(Progress…)方法后,onProgressUpdate (Progress…)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如说提醒任务执行的结果,以及关闭进度条对话框等。
简单来说,使用AsyncTask的诀窍就是,在doInBackground()方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。
如果需要启动某个任务,就执行AsyncTask的execute()方法并传入任意数量的参数,这些参数将会传递到DownloadTask
的doInBackground()方法当中。
Service的基本用法
创建一个service,只需要这样即可
service常用的重写方法:
onCreate()方法会在Service创建的时候调用,
onStartCommand()方法会在每次Service启动的时候调用
onDestroy()方法会在Service销毁的时候调用。
启动和停止service都是通过intent来实现的。
Activity与Service进行通信
需要使用到service中的onBind()方法
。
在service中定义一个bind类继承自Binder:
1 | //声明这个bind类并构建出来 |
在activity中提前预埋好用来存储的bind:
1 | //用来存储从service中获取的binder类。 |
与按钮绑定相关连接事件:
1 | binding.bindServiceBtn.setOnClickListener { |
bindService接受三个参数,第一个就是intent,第二个放入连接时对象,当连接成功时就会调用其中的方法。,第三个参数则是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service进行绑定后自动创建Service。这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。
这样子后,获取到activity就可以通过已经存储好的downloadBinder命令service完成任何需要完成的事。注意的是,不同的activity都可以获得这个binder对象。
Service的生命周期
service只有在第一次被启动时,才会执行onCreate方法,然后执行onStartCommand()。其他每次执行都只会调用onStartCommand()方法。所以service只会有一个实例,所以只要你调用了stopself()或者stopService()都会直接停止。
bindService()方法则会回调onBind()方法,如果service没有建立,就会调用一次onCreate()方法。从onBind()获取对象后就可以相互通信了。
当调用了startService()方法后,再去调用stopService()方法。这时Service中的onDestroy()方法就会执行,表示Service已经销毁了。类似地,当调用了bindService()方法后,再去调用unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意,我们是完全有可能对一个Service既调用了startService()方法,又调用了bindService()方法的,在这种情况下该如何让Service销毁呢?根据Android系统的机制,一个Service只要被启动或者被绑定了之后,就会处于运行状态,必须要让以上两种条件同时不满足,Service才能被销毁。所以,这种情况下要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行。
前台Service
从Android 8.0系统开始,只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。前台Service和普通Service最大的区别就在于,它一直会有一个正在运行的图标在系统的状态栏显示
构建前台service:
样例:
1 | override fun onCreate() { |
它与通知很像,但不同的是,它不是通过manager.notify()来启动。,而是调用了startForeground()方法
第一个参数是通知的id,类似于notify()方法的第一个参数;第二个参数则是构建的Notification对象。调用startForeground()方法后就会让MyService变成一个前台Service,并在系统状态栏显示出来。
而且前台service需要获取权限认可:
IntentService
它与service大差不差,属于service的一个子类。需要实现一个抽象方法onHandleIntent(),它会默认在子线程中运行。
这个抽象方法与startCommand一样,每次启动时都会调用