Service是Android中实现程序后台运行的解决方案,它适合执行那些不需要和用户交互且还要求长期运行的任务。
Service不依赖用户界面,即使程序被切到后台它也能正常运行。
Service并不是运行在一个独立的进程当中的,而是依赖于创建自己的应用程序进程。当一APP被杀掉时,所以依赖于此的Service都将停止运行。
尽管Service运行在后台,但它自己并不会自动开启线程,所有的Service代码都是默认运行在主线程的。
Android的UI也是线程不安全的,即更新UI的操作只能在主线程中进行,否则就会出现异常。
Why?
在多线程中并发访问,可能会导致UI控件处于不可预期的状态。而对UI上锁会导致让UI控件的使用变得复杂,还会阻塞某些进程的运行。
针对有时我们必须在子线程中执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件这一需求,Android提供了一套异步消息处理机制。
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间传递数据。作用类似于信封,将内部信息封装。
Handler,即处理者,负责发送和处理消息。
MessageQueue 消息队列,用于存放所有通过Handler发送的消息。这些消息会一直存放在MessageQueue中,等待被处理。每个线程中只能有一个MessageQueue对象。
Looper是每个线程中消息队列的管家,调用Looper的loop方法后,就会进入到一个无限循环中,每当发现MessageQueue中存在一条消息时,就会将它取出,并传递到Handler的handMessage方法中。每个线程中只能有一个Looper对象。
将Message从子线程传到主线程,从不可更新UI到可更新UI
首先在主线程中创建一个Handler对象,并重写handleMessage方法。然后当子线程中需要一个UI操作时,就创建一个Message对象,并通过Handler发生出去。之后这条消息会被添加到MessageQueue中,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后发回Handler的handMessage方法中(在主线程),根据处理结果执行UI操作。
为了方便我们的使用,Android将异步消息处理机制做了很好的封装,也就是AsyncTask。
AsyncTask是一个抽象类,我们经常需要重写onPreExecute(), doInBackground(Params…), onProgressUpdate(Progress…), onPostExecute(Result)等方法。
onPreExecute()在后台任务执行前被调用,可以用于初始化界面等操作。
doInBackground(Params…)在子线程中运行,在这里处理耗时任务,也可以调用publishProgress方法反馈当前任务的执行进度。
onProgressUpdate(Progress…)在后台调用publishProgress方法后调用,可以更新UI界面中的进度。
onPostExecute(Result)在任务执行完毕并通过return语句返回后调用。
新建一个Service后,常常需要重写以下方法:
在Service被创建时调用的onCreate方法,在每次启动Service时调用的onStartCommand方法,在Service被销毁时调用的onDestroy方法。
启动一个Service同样使用Intent。
注意,而为了防止恶意应用程序在后天占用手机资源而导致的手机变卡,从Android 8.0系统开始,应用的后台功能被大幅削减。现在只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时可能会被系统回收。
Service可以与应用程序内任何一个Activity绑定,当Activity和Service进行绑定之后,就可以调用该Service里Binder提供的方法了。
onBind方法是Service里的唯一一个抽象方法,可以用来返回一个Binder对象,利用该Binder对象,Activity就能够了解Service。
绑定和取消绑定的方法分别是bindService和unbindService。
一旦在项目的任何位置调用了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最大的区别就是它是可见的,会有一个正在运行的图标在系统的状态栏显示,类似于通知的效果。
正因为其是可见的,不但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()
}
}
由于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")
}
}