四大组件---Service
一、进程的概念
【1】 android的四大组件都运行在主线程中
【2】 进程是有生命周期的,最先杀死最不重要的进程
【3】 进程的优先级
[1] Foreground process(前台进程)
优先级最高,相当于activity执行了onResume
[2] Visible process(可视进程)
一直影响用户看到见,相当于activity执行了onPause
[3] Service process(服务进程)
通过startService()开启的服务
[4] Background process(后台进程)
用户不可见,相当于activity中调用了onStop方法,但是并没有销毁
[5] Empty process(空进程)
不会维持任何组件运行
二、start方式开启服务
【1】 定义一个类继承Service,在清单文件中注册
【2】 服务是在后台运行的,相当于没有界面的activity
【3】 服务启动后执行onCreate方法和onStart方法,之后如果再次启动,则只执行onStart方法,android 8.0以上的,建议不要用startServce,因为google为了省电,后台服务无操作后,一段时间后会自动停止服务,所以Android 8.0启动服务的解决方案是,调用startForegroundService(intent),调用此方法后,需要5s秒在service中调用startForeground(int id, Notification notification),否则系统会报出ANR异常,需要在清单文件中添加权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
java代码,在service类中
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// android 8.0以上通知栏必须创建NotificationChannel
val channelId = "myServiceId"
val channel =
NotificationChannel(channelId, "my", NotificationManager.IMPORTANCE_HIGH)
// 创建NotificationManager
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 使用NotificationManager创建channel
notificationManager.createNotificationChannel(channel)
// 创建通知栏,注意Builder的第二个参数是上面创建的channelId
val notification = Notification.Builder(this, channelId)
.build()
// 开启前台服务
startForeground(100, notification)
}
【4】 服务一旦开启,就会在后台长期运行,需要手动停止
三、bind方式启动服务
引入bindService,目的是为了在activity可以调用service中的方法
1.bindService的特点
【1】 使用bindService(Intent,ServiceConnection,flag)绑定服务,定义一个类实现ServiceConnection
【2】 注意在activity中绑定服务后,多次bindService没有效果,在界面销毁时,需要解绑服务,调用unbindService(ServiceConnection),但是不能多次解绑
【3】 使用绑定这种方式启动服务,在activity销毁的时候,服务也跟着销毁
【4】 启动服务时,service只会执行onCreate,不会执行onStartCommand
【5】 ServiceConnection中的onServiceConnected执行的时机,是在service中的onBind方法是否有返回值
【6】 这种启动方式,在设置中不能找到该服务,相当于一个隐形服务
2.调用service中的方法
① 使用Binder的方式
【1】 定义一个类继承Binder对象,在该类中定义外部调用的方法
inner class MyBind : Binder() {
fun f1() {
// 调用service中方法
this@MyService.f1()
}
}
【2】 在service中定义外部需要调用的函数,在Binder子类中调用
【3】 在service中的onBind方法中,返回刚创建的Binder的子类
override fun onBind(intent: Intent?): IBinder? {
println("绑定")
return MyBind()
}
【4】 在activity中bindService,在ServiceConnection的实现类中,的onServiceConnected方法中,获取IBinder对象,强转为自己刚定义的Binder子类,即可调用方法,间接的调用到service中的方法
inner class MyConn : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
println("onServiceDisconnected")
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
println("onServiceConnected")
if (service is MyService.MyBind){
service.f1()
}
}
}
② 使用接口方式调用服务里的方法(比较常用)
【1】 定义一个接口,把想暴露的方法定义在接口中
【2】 在定义的binder对象实现这个接口
【3】 在OnServiceConnected中获取IBinder对象,将IBinder对象转为接口的类型,即可调用接口中方法
四、混合方式开启服务
既让service在后台长期运行,又可以调用service中方法
【1】 调用startService使服务在后台长期运行
【2】 调用bindService可以调用service的方法
【3】 同时调用startService和bindService,解绑服务后,服务还没销毁,需要再调用stopService,服务才会被kill,同样的,如果先调用stopService,也要再调用unbindSerivce,服务才会停止
五、Aidl
1.本地服务:运行自己应用里的服务
【1】 不涉及进程间的通信
2.远程服务:运行在其他应用里的服务
【1】 实现进程间的通信IPC
【2】 aidl:Android Interface Definition Language,专门解决进程间的通信
【3】 应用场景:支付宝
【4】 实现的步骤:
比如两个应用之间通信
1.A应用中创建一个service,例如是RemoteService,与普通的service实现的一样
2.创建一个aidl,就是相当于一个接口,编译
3.在RemoteService中的onBind方法,返回一个IBinder对象,这时创建一个类,继承刚创建的aidl中的Stub
4.在B应用中开启service,例如使用bindService,开启服务时,需要用到隐式意图启动,
注意:在android 5.0以上,服务默认都要显示启动,如果一定要隐式启动的话,需要指定package属性
5.将A应用的中aidl,连同包名一起复制到B应用中,编译
在B应用中的ServiceConnection的实现类中的onServiceConnected方法中,调用Stub.asInterface(IBinder)方法,返回的是aidl接口类型,这时就可以调用接口中的方法了
java代码实现
A应用:
class RemoteService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return RemoteBinder()
}
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channel =
NotificationChannel("remote", "远程服务", NotificationManager.IMPORTANCE_HIGH)
notificationManager.createNotificationChannel(channel)
val notification = Notification.Builder(this, "remote").build()
startForeground(1, notification)
}
}
fun remoteMethod() {
println("远程服务中的方法")
}
inner class RemoteBinder : IRemoteServiceAidl.Stub() {
override fun basicTypes(
anInt: Int,
aLong: Long,
aBoolean: Boolean,
aFloat: Float,
aDouble: Double,
aString: String?
) {
}
override fun remoteMethod() {
this@RemoteService.remoteMethod()
}
}
}
B应用:
class MainActivity : AppCompatActivity() {
private val conn: RemoteConn
get() = RemoteConn()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 调用远程服务
val intent = Intent()
intent.action = ("com.wzh.a03_servicedemo.aidl.RemoteService")
intent.`package` = "com.wzh.a03_servicedemo"
bindService(intent, conn, Context.BIND_AUTO_CREATE)
}
override fun onDestroy() {
super.onDestroy()
unbindService(conn)
}
inner class RemoteConn : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val iRemoteServiceAidl = IRemoteServiceAidl.Stub.asInterface(service)
iRemoteServiceAidl.remoteMethod()
}
}
}
aidl文件:
interface IRemoteServiceAidl {
/**
* 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 remoteMethod();
}