导语
service是安卓应用开发组件之一,它用于执行较长时间操作而不是与用户进行交互和为其他应用提供功能。本文主要介绍什么是service、service的生命周期、service的分类和实现、Service和IntentService的区别、service的通信;
1.service的概述
service是安卓应用开发组件之一,它用于执行较长时间操作而不是与用户进行交互和为其他应用提供功能。每一个sercice必须在Androidmanifast.xml中声明,用 Context.startService()
和Context.bindService()
可以开启一个服务。
和其它组件一样,service是其托管进程的主线程中运行的。这意味着您的service要执行一些大量消耗CPU的操作,比如:MP3播放、网络下载、解压等就需要在handleMessage(Message msg)
中需要开启一个单独的线程,以免造成UI阻塞。IntentService作为标准的实现,该服务拥有自己的线程,可以自己处理。
注:
* service不是一个单独的进程。service对象本身不运行在单独的一个进程中,它和其他组件一样运行在应用主进程中。
* service不是一个线程。但它本身并不是用来处理主线程的工作,请避免主程序没有响应的错误。
因此,服务本身实际上非常简单,提供了两个主要特性:
* 应用程序的一个功能,它告诉系统它想在后台做的事情(即使用户没有直接与应用程序交互)。这相当于调用Context.startService()
,它要求系统为服务调度工作,直到服务或其他人显式地停止服务为止。
* 一种用于将某些功能暴露给其他应用程序的应用程序。这相当于调用Context.bindService()
,它允许对服务进行长期连接,以便与服务交互。
由于,service如此简(lan)单(duo),所以你可以用service对象实现本地服务或用更多的代码去实现你复杂的远程服务器(AIDL)。
2.service的生命周期
服务可以由系统运行有两个原因。如果有人调用Context.startService()
,那么系统将检索服务(创建它并调用它的onCreate()方法),然后调用它的onStartCommand(Intent, int, int)
方法,并使用客户机提供的参数。此时,服务将继续运行,直到调用Context.stopService()
或stopSelf()
。请注意,对Context.startService()
的多个调用不会嵌套(尽管它们会导致对onStartCommand()的多个相应调用),因此无论它启动了多少次,都将停止一次服务。但是,服务可以使用它们的stopSelf(int)
方法来确保服务在开始处理之前不会停止。
开始服务,主要有两个额外的操作模式,他们可以决定在运行,这取决于他们从onStartCommand()
返回值START_STICKY用于显式地根据需要启动和停止服务,而START_NOT_STICKY或START_REDELIVER_INTENT应该只用于服务仍然运行在处理任何命令发送给他们。
客户机还可以使用Context.bindService()
来获得对服务的持久连接。同样,如果它还没有运行(调用onCreate()
),但不调用onStartCommand()
,也会创建服务。客户端将接收服务从onBind(Intent)
方法返回的IBinder对象,允许客户端返回服务。只要连接已经建立(无论客户机是否保留了服务的IBinder的引用),服务就会继续运行。通常IBinder返回的是在aidl中编写的复杂接口。
2.1 服务的两种启动方式:
started启动:started形式的服务是指当一个应用组件(比如activity)通过startService()
方法开启服务。一旦开启,该服务就可以永久的在后台运行,哪怕开启它的组件被销毁掉。通常开启的服务执行一个单独的操作并且不向调用者返回一个结果。比如,从网络下载文件,当文件下载完成,服务就应该自己停止。
关闭服务则需要服务自己调用方法stopSelf()
或者由启动服务的地方调用stopService(Intent)
方法来关闭。
Bound绑定:bound形式的服务是指一个应用组件通过调用bindService()
方法与服务绑定。一个绑定的服务提供一个接口,允许组件与服务交互,发送请求、获得结果、甚至进行进程间通信。一个绑定的服务只和与其绑定的组件同时运行。多个组件可以同时绑定到一个服务,当全部解除绑定后,服务就会被销毁。
虽然分为两类,但是一个服务可以同时使用这两种方式-使用started永久运行,同时允许绑定。只要在服务中实现两个回调方法:onStartCommand()
允许组件开启服务,onBind()
允许绑定。
不论引用程序是怎么起服务的,任何应用程序都可以用这个服务。同样的,任何组件可以使用一个Activity通过传递Intent开启服务。你也可以在配置文件设置服务为私有来防止其他应用访问该服务。
2.2 生命周期与方法
- void onCreate(): 当Service被启动时被触发,无论使用Context.startServcie还是Context.bindService启动服务,在Service整个生命周期内只会被触发一次。
- int onStartCommand(Intent intent, int flags, int startId): 当通过Context.startService启动服务时将触发此方法,但当使用 Context.bindService 方法时不会触发此方法,其中参数 intent 是 startCommand 的输入对象,参数 flags 代表 service 的启动方式,参数 startId 当前启动 service 的唯一标式符。返回值决定服务结束后的处理方式,下文将再作详细说明。
- void onStart(Intent intent,int startId): 2.0旧版本的方法,已被Android抛弃,不推荐使用,默认在onStartCommand 执行中会调用此方法。
- IBinder onBind(Intent intent): 使用 Context.bindService 触发服务时将调用此方法,返回一个IBinder 对象,在远程服务时可用于对 Service 对象进行远程操控。
- void onRebind(Intent intent): 当使用startService启动Service,调用bindService启动Service,且 onUnbind 返回值为 true 时,下次再次调用 Context.bindService 将触发方法。
- boolean onUnbind(Intent intent): 调用 Context.unbindService 触发此方法,默认返回 false, 当返回值 true 后,再次调用 Context.bindService 时将触发 onRebind 方法
- void onDestory(): 分三种情况:1.以Context.startService启动service,调用Context.stopService结束时触发此方法;2.以Context.bindService启动service,以Context.unbindService结束时触发此方法;3.先以Context.startService 启动服务,再用Context.bindService绑定服务,结束时必须先调用Context.unbindService解绑再使用Context.stopService结束service才会触发此方法。
2.3 常见方法调用生命周期
3.service的分类和实现
3.1 本地服务实现下载
用户页面相关代码
package com.example.li.servicedemo
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.View
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//注册BroadcastReceiver
var intentFilter = IntentFilter()
intentFilter.addAction("com.demo.download")
registerReceiver(myreceiver, intentFilter)
button.setOnClickListener({
//发送开启服务的intent
var intent = Intent(MainActivity@this,DownloadService::class.java)
intent.putExtra("url","http://wap.apk.anzhi.com/data1/apk/201802/07/1208063166e96231b8e0e5e2614fbc80_78464200.apk")
startService(intent)
progressBar.visibility = View.VISIBLE
})
}
override fun onDestroy() {
super.onDestroy()
//取消注册BroadcastReceiver
unregisterReceiver(myreceiver)
}
/**
* 匿名内部类 BroadCastReceiver 接收下载进度和完成的处理
*/
var myreceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
//显示进度
var progress = intent!!.getIntExtra("progress",0)
Log.e("download","progress:"+progress)
progressBar.setProgress(progress)
var success = intent!!.getBooleanExtra("success",false)
if(success){
progressBar.visibility = View.INVISIBLE
Toast.makeText(baseContext, "下载完成",Toast.LENGTH_LONG).show()
}
}
}
}
service实现相关代码
package com.example.li.servicedemo
import android.app.Service
import android.content.Intent
import android.os.Environment
import android.os.IBinder
import android.util.Log
import org.xutils.common.Callback
import org.xutils.common.task.PriorityExecutor
import org.xutils.http.RequestParams
import org.xutils.x
import java.io.File
class DownloadService : Service() {
private val MAX_DOWNLOAD_THREAD = 6
private val executor = PriorityExecutor(MAX_DOWNLOAD_THREAD, true)
override fun onBind(intent: Intent): IBinder? {
// TODO: Return the communication channel to the service.
throw UnsupportedOperationException("Not yet implemented")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val url = intent!!.getStringExtra("url")
val filesavePath = Environment.getExternalStorageDirectory().path + "/serviceDemo/aaa.apk"
val downLoadManagerCallBack = DownLoadManagerCallBack()
val params = RequestParams(url)
params.isAutoResume = true
params.isAutoRename = false
params.saveFilePath = filesavePath
params.executor = executor
params.isCancelFast = true
x.http().get(params, downLoadManagerCallBack)
return super.onStartCommand(intent, flags, startId)
}
inner class DownLoadManagerCallBack : Callback.CommonCallback<File>, Callback.ProgressCallback<File>, Callback.Cancelable {
override fun isCancelled(): Boolean {
// Log.e("download", "isCancelled")
return false
}
override fun cancel() {
Log.e("download", "cancel")
}
override fun onWaiting() {
Log.e("download", "onWaiting")
}
override fun onStarted() {
Log.e("download", "onStarted")
}
override fun onLoading(total: Long, current: Long, isDownloading: Boolean) {
if (isDownloading) {
Log.e("download", "total:$total--current:$current")
var progress :Int = (current.toFloat()/total.toFloat() * 100.0f).toInt()
//发送广播通知下载进度
var intent = Intent()
intent.setAction("com.demo.download")
intent.putExtra("progress",progress)
sendBroadcast(intent)
}
}
override fun onSuccess(result: File) {
Log.e("download", "name:" + result.name + "--path:" + result.absoluteFile)
//发送广播通知完毕
var intent = Intent()
intent.setAction("com.demo.download")
intent.putExtra("success",true)
sendBroadcast(intent)
}
override fun onError(ex: Throwable, isOnCallback: Boolean) {
ex.printStackTrace()
Log.e("download", "onCancelled")
}
override fun onCancelled(cex: Callback.CancelledException) {
cex.printStackTrace()
Log.e("download", "onCancelled")
}
override fun onFinished() {
Log.e("download", "onFinishied")
}
}
}
3.2远程服务实现办理购房业务
服务端定义接口
android studio 新建 IMyAidlInterface.aidl 如图:
// IMyAidlInterface.aidl
package com.example.remotserviceapp;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* 处理购房业务
* dealMoney 业务费用
* yezhu 业主
* maijia 买家
*/
String deal(int dealMoney, String yezhu,String maijia);
}
新建服务端 DealService 如图:
package com.example.remotserviceapp
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
class DealService : Service() {
override fun onBind(intent: Intent): IBinder? {
//返回继承者Binder的Stub类型的Binder,业务逻辑的实现处理类
Log.e("deal", "onBind")
return mBinder
}
override fun onUnbind(intent: Intent?): Boolean {
Log.e("deal", "onUnbind")
return super.onUnbind(intent)
}
var mBinder = object : IMyAidlInterface.Stub() {
override fun deal(dealMoney: Int, yezhu: String?, maijia: String?): String {
Log.e("deal", "dealMoney:$dealMoney--yezhu:$yezhu--maijia:$maijia")
return "成交!"
}
}
}
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".DealService"
android:enabled="true"
android:process=":remote" //设置为远程服务
android:exported="true" //可被其他线程调用
>
<intent-filter>
<action android:name="com.example.remotserviceapp.IMyAidlInterface"/>//action名称
</intent-filter>
</service>
</application>
客户端调用实现
package com.example.remotserviceapp
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private var myDealService: IMyAidlInterface? = null
private var isbinded: Boolean = false
private var mConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
myDealService = null
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
myDealService = IMyAidlInterface.Stub.asInterface(service)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindBtn.setOnClickListener({
if (!isbinded) {
val intent = Intent();
intent.action = "com.example.remotserviceapp.IMyAidlInterface"
//Android5.0后无法只通过隐式Intent绑定远程Service
//需要通过setPackage()方法指定包名
intent.setPackage("com.example.remotserviceapp")
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
isbinded = true
}
})
unbindBtn.setOnClickListener({
if (isbinded) {
unbindService(mConnection)
isbinded = false
}
})
banliBtn.setOnClickListener({
try {
val result = myDealService!!.deal(50000, "房姐", "土豪")
Toast.makeText(baseContext, result, Toast.LENGTH_LONG).show()
} catch (e: RemoteException) {
e.printStackTrace()
}
})
}
}
kotlin实现本地服务和远程服务完整代码地址: https://github.com/baibai2013/serviceDemo
4.Service和IntentService的区别
5.service的通信
本文参考:
file:///[SDK路径]/docs/reference/android/app/Service.html
http://blog.csdn.net/a296777513/article/details/54984935
https://www.cnblogs.com/leslies2/p/5401813.html#p0
https://www.jianshu.com/p/e04c4239b07e