概念
就是默默运行在后台的组件,可以理解为是没有前台的activity,适合用来运行不需要前台界面的代码
服务可以被手动关闭,不会重启,但是如果被系统关闭,内存充足就会重启
进程优先级
- 前台进程:拥有一个正在与用户交互的activity(onResume方法被调用)的进程
- 可见进程:拥有一个非前台,但是对用户可见的activity(onPause方法被调用)的进程
- 服务进程:拥有一个通过startService方法启动的服务的进程
- 后台进程:拥有一个后台activity(onStop方法被调用)的进程
- 空进程:没有拥有任何活动的应用组件的进程,也就是没有任何服务和activity在运行
五种前台进程
- activity执行了onresume方法,获得焦点
- 拥有一个跟正在与用户交互的activity绑定的服务
- 拥有一个服务执行了startForeground()方法
- 拥有一个正在执行onCreate()、onStart()或者onDestroy()方法中的任意一个的服务
- 拥有一个正在执行onReceive方法的广播接收者
两种可见进程
- activity执行了onPause方法,失去焦点,但是可见
- 拥有一个跟可见或前台activity绑定的服务
Service
创建方式
- 定义一个类继承Service,并在清单文件中注册该service
服务的两种启动方式
startService
- 使用Context中的startService方法将服务启动,会使进程变成为服务进程
- 启动服务的activity和服务不再有关系
fun start(view: View){
val intent = Intent(this, MyService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//8.0以上,需要启动前台服务
startForegroundService(intent)
} else {
startService(intent)
}
}
- 8.0以上使用startForegroundService后,需要在Service中启动前台通知,否则会报错
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel("my", "MyService", NotificationManager.IMPORTANCE_HIGH)
channel.lockscreenVisibility = Notification.VISIBILITY_SECRET
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nm.createNotificationChannel(channel)
val notification = Notification.Builder(this, "my")
.setContentTitle("系统关键服务")
.setContentText("关闭会造成死机,请谨慎")
.setSmallIcon(R.mipmap.ic_launcher)//这个一定要设置,不然就是普通的通知
.build()
startForeground(1, notification)
}
}
- 启动前台服务,需要在清单文件中声明权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
通知要设置setSmallIcon方法,否则则会显示默认的通知
已设置
未设置
- 使用Context中的stopService方法将服务停止
fun stop(view:View){
val intent= Intent(this,MyService::class.java)
stopService(intent)
}
bindService
activity与service建立连接,进程优先级不变,绑定的服务与它的activity同生共死,activity销毁了,服务解绑并销毁了,但是如果是服务先销毁了,并不会影响activity。所以bindService不适合做后台任务。
- 绑定服务
fun bind(view: View) {
val intent = Intent(this, MyService::class.java)
bindService(intent, conn, Context.BIND_AUTO_CREATE)
}
class MyConn : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
//连接因为异常而终止才会调用,正常unBind是不会调用此方法的
println("服务中断")
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
//Service中的onBind返回有返回值才有调用
println("服务连接")
}
}
- 解绑服务
fun unbind(view: View) {
unbindService(conn)
}
服务的生命周期
- 如果当前服务未启动,调用startService时,首先会调用Service中的onCreate方法,其次调用onStartCommand方法。如果一开始调用的是bindService,则首先调用Service中的onCreate方法,再调用onBind方法。
- 多次启动服务,如果是使用startService启动的则会调用onStartCommand,如果是使用bindService启动的,只有第一次会调用onBind方法,后面多次调用bindService,则不会调用onBind方法
- 停止服务时,stopService会调用onDestroy方法,unbindService会先调用unBind方法,再调用onDestroy方法,多次解绑会抛出异常,使用bindService方式启动时,activity销毁,服务也会调用解绑方法,并且销毁。
class MyService : Service() {
override fun onBind(intent: Intent?): IBinder? {
println("绑定")
return null
}
override fun onUnbind(intent: Intent?): Boolean {
println("解绑")
return super.onUnbind(intent)
}
override fun onCreate() {
super.onCreate()
println("service onCreate")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
println("service onStartCommand")
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
println("service onDestroy")
super.onDestroy()
}
}
访问Service中的方法
访问调用Service中的方法,需要使用bindService方法进行启动服务,这样创建的ServiceConnection对象就是与Service连接的对象,这时需要Service中返回IBinder对象。
实现
- 在Service中定义一个内部类实现IBinder或者继承Binder,一般都是继承Binder的
inner class MyBinder : Binder() {
}
- 然后定义外部要访问的方法接口,让该类实现此接口
inner class MyBinder : Binder(), IFun {
override fun sayHello() {
}
override fun sayHaHa() {
}
}
- 实现这些方法时,根据内部类可以访问外部类的属性和方法,调用Service中的方法即可
inner class MyBinder : Binder(), IFun {
override fun sayHello() {
say("hello")
}
override fun sayHaHa() {
say("haha")
}
}
fun say(text: String) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
}
- 创建此对象,让Service中的onBind方法返回
class MyService : Service() {
override fun onBind(intent: Intent?): IBinder? {
println("绑定")
return MyBinder()
}
override fun onUnbind(intent: Intent?): Boolean {
println("解绑")
return super.onUnbind(intent)
}
}
- 外部调用时,首先使用bindService方法,绑定服务,在ServiceConnection对象中的onServiceConnected的IBinder对象,就是服务返回的IBinder对象
private val conn by lazy { MyConn() }
private var iFun: IFun? = null
fun bind(view: View) {
val intent = Intent(this, MyService::class.java)
bindService(intent, conn, Context.BIND_AUTO_CREATE)
}
fun unbind(view: View) {
unbindService(conn)
}
fun callService(view: View) {
iFun?.sayHello()
}
inner class MyConn : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
iFun = service as? IFun
}
}
服务的混合启动
即可以将Service运行在服务进程,又可以在Activity中调用service中的方法。
先start,再bind,销毁时先unbind,在stop
服务的分类
本地服务
服务和启动它的组件在同一个进程
远程服务
服务和启动它的组件不在同一个进程
实现
- 首先定义一个提供服务的应用,在清单文件中,注册该服务,远程服务只能是隐式启动,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点
<service android:name=".AliPayService">
<intent-filter>
<action android:name="com.wzh.alipay"/>
</intent-filter>
</service>
- 服务
class AliPayService : Service() {
override fun onBind(intent: Intent?): IBinder? {
println("AliPayService onBind")
return null
}
override fun onUnbind(intent: Intent?): Boolean {
println("AliPayService onUnbind")
return super.onUnbind(intent)
}
override fun onCreate() {
super.onCreate()
println("AliPayService onCreate")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel("alipay", "支付服务", NotificationManager.IMPORTANCE_HIGH)
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nm.createNotificationChannel(channel)
val notification = Notification.Builder(this, "alipay")
.setContentTitle("正在支付10000000元")
.setContentText("剩余金额19088900088945元")
.setSmallIcon(R.drawable.icon_alipay)
.setWhen(System.currentTimeMillis())
.setAutoCancel(false)
.build()
startForeground(1, notification)
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
println("AliPayService onStartCommand")
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
println("AliPayService onDestroy")
}
}
注意:需要在清单文件中配置前台服务的权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- 在需要启动服务的应用中,写启动服务的代码,配置所需的intent参数,5.0以上如果按以前的启动方式,则会报异常Service Intent must be explicit,服务意图必须明确,因为5.0以前启动服务必须显示声明,解决的方法是,将隐式意图转为显式意图
fun createExplicitFromImplicitIntent(context: Context, implicitIntent: Intent): Intent? {
val pm = context.packageManager
val resolveInfo = pm.queryIntentServices(implicitIntent, 0)
if (resolveInfo.size != 1) {
return null
}
val serviceInfo = resolveInfo[0]
val packageName = serviceInfo.serviceInfo.packageName
val className = serviceInfo.serviceInfo.name
val component = ComponentName(packageName, className)
val explicitIntent = Intent(implicitIntent)
explicitIntent.component = component
return explicitIntent
}
- 启动远程服务
fun startRemoteService(v: View) {
val intent = Intent()
intent.action = "com.wzh.alipay"
val it = createExplicitFromImplicitIntent(this, intent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(it)
} else {
startService(it)
}
}
fun stopRemoteService(v: View) {
val intent = Intent()
intent.action = "com.wzh.alipay"
val it = createExplicitFromImplicitIntent(this, intent)
stopService(it)
}
fun bindRemoteService(v: View) {
val intent = Intent()
intent.action = "com.wzh.alipay"
val it = createExplicitFromImplicitIntent(this, intent)
bindService(it, conn, Context.BIND_AUTO_CREATE)
}
fun unbindRemoteService(v: View) {
unbindService(conn)
}
inner class AlipayServiceConnection : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
println("获取连接对象")
}
}
- 启动服务后,执行的结果是
startService
System.out: AliPayService onCreate
System.out: AliPayService onStartCommand
bindService
System.out: AliPayService onCreate
System.out: AliPayService onBind
stopService
System.out: AliPayService onDestroy
unBind
System.out: AliPayService onUnbind
ServiceConnection中的onServiceConnected执行需要服务中的onBind方法有返回值,并且是bindService的方式才会执行
inner class AliPayBinder :Binder(){
}
override fun onBind(intent: Intent?): IBinder? {
println("AliPayService onBind")
return AliPayBinder()
}
AIDL
概念
Android interface definition language安卓接口定义语言
作用:跨进程通信
应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,使用aidl,就可以在其他应用中拿到中间人类所实现的接口
如果需要调用远程服务的方法就需要使用aidl
实现
- 在提供服务的应用中,创建一个供外部调用的功能接口,把这个接口的后缀名改为aidl,aidl文件中不能有任何修饰符。在Android studio中,使用右键创建aidl文件
// IAliPay.aidl
package com.wzh.alipay;
// Declare any non-default types here with import statements
interface IAliPay {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void pay();
}
- 立刻编译,生成java文件
这个类中有个重要的内部类
public static abstract class Stub extends android.os.Binder implements com.wzh.alipay.IAliPay
- 获取到Stub类
这个类既继承了Binder类,同时又实现了接口方法,从这里可以联想到上面定义一个类继承Binder,实现功能接口,然后让onBind方法返回出去。所以这时的这个内部类需要继承Stub类(必须)。
inner class AliPayBinder :IAliPay.Stub(){
override fun basicTypes(
anInt: Int,
aLong: Long,
aBoolean: Boolean,
aFloat: Float,
aDouble: Double,
aString: String?
) {
}
override fun pay() {
println("支付成功")
}
}
- 调用方,创建一个和服务方包名和名称相同aidl文件,编译
- 调用方绑定服务时,创建的ServiceConnection对象中的中间人对象,需要将中间人进行强转,这里只能使用Stub.asInterface
inner class AlipayServiceConnection : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
println("获取连接对象")
alipay = IAliPay.Stub.asInterface(service)
}
}
- 这样,使用这个aidl对象,就可以调用到服务方的方法了