Service
什么是Serviece
Serciece 表示”服务”。
Service是android系统的核心组件之一。
Service的本质是一个继承了android.app.Service的JAVA类
每个Service都应该在AndroidManifest.xml文件中进行注册;
Service由Android系统进行维护
Service的定位
Service没有匹配的用户界面,通常用于后台处理的耗时操作。
不许在主线程中执行耗时操作
Service是运行在主线程中的;
尽管Service被定为”用于处理耗时操作”,但是各种耗时操作需要在Service中另外开辟线程来完成。
组件可以绑定到Service,实现进程间通信(IPC:Inter process Communication)。
Service比Activity生命力更强,不容易被销毁掉、
进程优先级
Android 系统力图维护尽可能多的进程,但由于设备性能有限,在动态管理内存的过程中,Android系统经常终止一些优先级低的进程,以释放资源,保证优先级高的进程正常运行
进程的优先级分类如下(从高到底):
1. 前台进程(Foreground Process)
存在正在与用户交互的Activity;
存在Service,且Service绑定在与用户交互的Activity之上
存在运行在前台的Service,即该Service的start Foreground()被调用
存在正在执行核心生命周期方法的Service,包括:onCreate(),on Start(),OnStartCommand(),onDestroy();
存在正在接受广播的广播接收者。
2. 可见进程(Visible Process)
并无前台组件,但包含用户可见的组件,例如:
处于暂停状态的Activity
存在绑定在可见Activity之上的Service
3. 服务进程(Service Process)
使用startService()方式启动的Service,单该Service并未上升到”前台”,”可见”的级别
4. 后台进程(Background Process)
大多表现为onStop()之后的Activity等
5. 空进程(Empty Process)
不包含任何活动的组件的进程,为了便于缓存组件,以提高下次的启动效率,系统可能仍暂时保留它们。
Service 所在的优先级都在前三种
Service 启动模式
开发人员可以使用Intent激活Service组件。
激活Service组件的方式有:
调用Context定义的startService()方法;(启动Service)
调用Context定义的bindService()方法;(绑定Service)
启动Service的开发流程如下:
1. 创建JAVA类,继承android.app.Service;
Service中定义了抽象方法onBind(),该方法必须被重写,但不一定需要被具体实现。
2. 在AndroidManifest.xml中的<application>下添加<Service>子节点,配置创建的Service;
3. 在Activity中调用startService(Intent intent)方法启动Service.
显示或隐式启动Service
无论是显示Intent或隐式Intent都可以激活service组件。
如果需要实现进程间的通信,则应该为Service组件配置隐式意图过滤器。
停止Service
停止Service的方法:
通过调用Context的stopService(Intnt intent)方法可以停止Service,并销毁该Service组件。
在Service中调用stopSelf()方法可以停止自身。(在自己的类里面)
Service的生命周期
如下:
如果Activity反复调用startService()方法,在Service中只会反复调用onStartCommand()方法
也就是说第一次执行的话会调用onCreate()方法,当它的组件已经存在了它就会跳过OnCreate()方法.
Service的粘性
Service的粘性表现为其所在进程被意外终止后,该Sercice是否可以自动重新被启动
默认情况下,使用startService()方式激活的Service组件是粘性的,则即使其所在进程被意外终止了,稍后该Service依然会被自动创建出来。
在Service生命周期中onStartCommand()方法的返回值决定了Service的粘性。
该方法的返回值可以被设为:
START_STICKY:粘性的,被意外终止后自动重启,但丢失原来用于激活的Intent;
START_NOT_STICKY:非粘性的,被意外终止后不会自动重启;
START_REDELIVER_INTENT:粘性的且重新发送Intent,即被意外终止后自动重启,且该Service组件将得到原来用于激活它的Intent 对象;
START_STICKY_COMPATILITY:START_STICKY的兼容版本,并不担保onStartCommand()会被重新调用。
使用Service播放音乐
使用系统的Media Player类可以播放音乐,开发步骤如下:
创建Media Player对象,可直接使用无参数构造方法;
调用Media Player的reset()方法重置(非必要,但推荐);
调用Media Player的setDataSource()方法设置需要播放的歌曲;
调用Media Player 的prepare()方法加载(缓冲)歌曲;
调用Media Player的start()方法播放歌曲;
当退出时应该调用release()方法释放资源。
逻辑:
通过Activity激活Service,且在Service中创建Media Player的实例,实现歌曲的播放;
被播放的歌曲保存在模拟器的sdcard/Music/中;
当Activity被停止时,停止播放歌曲的Service
当Service被停止时,释放Media Player的资源;
关于MediaPlayer
MediaPlayer支持主流的音频,视频文件的播放,亦支持播放非本机的媒体文件;
Media Player会开启子线程播放歌曲;
可调用pause()方法暂停播放,调用seekTo()方法快进到指定的位置开始播放;
可调用用prepareAsync()方法加载歌曲,并配置OnPreparedListener,在监听器用调用MediaPlayer的start()方法;
通常MediaPlayer配置OnCompletionListener,以实现在播放完成后的处理
Service 绑定模式
使用绑定的Service可以实现组件与Service的通信
组件与被绑定的Service可以不归属于同一个应用程序,因此通过绑定Service可以实现进程间通信。
(也就是说A页面的Activity可以绑定B页面的Service,也可以实现进程间的通信)
使用绑定模式激活Service
调用bindService(Intent service,ServiceConnection conn,int flags)方法即可实现当前组件与Service的绑定。
参数说明:
Intent service:配置被激活的Service组件,该Intent可以是显示的,也可以是隐式的
ServiceConnection conn:当前组件与被激活的Service的链接对象,当成功的以绑定模式激活Service后,该Service的onBind()方法的返回值(非null)对象被会回传到当前组件,即当前组件与被激活的Service存在相同的IBinder对象;
Int flags:标志。
关于Service的onBind()方法
在绑定Service时,定义的Service类中,onBind()方法应该返回一个非null的值。
onBind()方法的返回值类型时IBinder类型,IBider是接口,开发人员可以自己定义类实现该接口。
Google官方不推荐开发人员直接定义类实现IBinder接口,而是通过继承Binder类即可,Binder类是IBinder接口的实现类。
如果onBind()方法的返回值是null,则该绑定过程是失败的,尽管Service也会执行onCreate()方法开始工作,但其他组件无法与Service通信。
实现绑定Service
步骤:
创建JAVA类继承android.app.Service类,并在AndroidManifest.xml中注册该Service;
在自定义的Service类中创建IBinder的对象,作为onBind()方法返回值;
在Activity中创建ServiceConnection的对象;
在Activity中调用bindService()方法实现与Service的绑定;
重写Activity的onDestroy()方法,调用unbindService()方法取消与Service的绑定,以避免Activity被销毁时绑定仍然存在导致的异常
取消绑定
当与Service绑定的组件被销毁时,应该及时取消与Service的绑定,否则导致异常。
在组件中,调用unbindService(ServiceConntion conn)方法则可以取消与Service的绑定
Service绑定的生命周期
图在上面,绑定的默认不是粘性的,也就是说它被意外终止掉是不会重新启动的
当绑定Service之后,Activity被销毁之前应该取消与Service的绑定,否则将抛出异常。
关于ServiceConnection的onServiceDisconnected()
ServiceConnection的onServiceDisconnected()方法并不会随着组件取消与Service的绑定而回调,该方法仅在Service在意外情况下崩溃时被调用
关于Service的onRebind()
一般情况下,onRebind()方法并不会被回调,被回调的情景通常为:
与该Service的所有组件都已经取消与它的绑定,导致该Service的onUnbind()方法被回调,且重写了onUnbind()方法返回值为true;
该Service没有被销毁且再次被绑定时,则被回调onRebind()方法。
注意:以绑定模式激活的Service组件并不是粘性的,且与Service绑定的组件在推出之前必须取消绑定,即无法保证Service组件依然存在,所以,为了保证其他组件可以销毁,但Service依然存在,可以:
1. 先调用startService()激活Service组件;
2. 在调用bindService()实现绑定。
实现Activity与Service通信
组件间通信的方式
已知:在Service中,onBind()方法的返回值可以被Activity获得,即Activity与Service共有一个IBinder类型的对象。
结论:开发人员可以在Service中自定义IBinder的实现类,并在该类中定义若干方法,当Activity获得该实现类的对象时,即可调用这些方法。
矛盾:IBinder的实现类的相关业务可能与Activity发送的Intent,Service的生命周期等存在密切关系,使用Service的内部类定义更合适,但是用内部类则无法让Activity知晓该实现类的数据类型。
解决方法:使用接口定义Activity需要让Service完成的方法。
绑定Service时接口的作用
绑定Service时接口的作用为:
约定一种数据类型,让组件与Service均可使用这种类型的数据
约定组件与Service通信的标准,以使得组件可以调用相关的方法
AIDL 实现跨进程Service 的绑定
使用AIDL绑定远程Service
关于AIDL接口
在开发工具生成的AIDL接口的JAVA源文件中,定义了名为Stub的内部类,继承了android.os.Binder,并实现了AIDL的接口,因此,开发人员在创建Service中的IBinder的实现类时,仅需继承该Stub类即可。
由开发人员定义的AIDL接口中的抽象方法,都被定义为抛出远程异常(android.os.RemoteException),在实现这些抽象方法时需要处理。
在AIDL接口的Stup内部类中,存在asInterface(IBinder obj)方法,用于将IBinder对象转换为AIDL接口的实现类对象,因此,再绑定Service的组件中,可通过该方法得到AIDL接口的实现类对象。
使用AIDL与一般绑定服务的区别
使用AIDL与一般绑定服务的区别如下:
服务端 使用AIDL接口
服务端 创建AIDL接口的内部类Stub的子类对象,并将其作为Service中onBind()的返回值
服务端 必须配置Service是可以隐式激活的
客户端 将服务端的AIDL文件复制到客户端
客户端 在ServiceConnection的onServiceConnected()中,使用AIDL接口的Stub的asInterface()方法,将参数IBinder转换为AIDL接口的对象。
服务端开发步骤
服务端开发步骤:
1. 开发AIDL接口源文件,并确保扩展名是.aidl(如果先创建的*.java源文件后改名,则需要刷新);
2. 在Service中开发AIDL接口的实现类,并实现AIDL接口中定义的抽象方法;
3. 实现Service类中的onBind()方法,并将AIDL的实现类的对象作为该方法的返回值;
4. 注册Service类,并配置<intent-filter>;
5. 部署服务端应用程序到设备上。
6. 客户端开发步骤
客户端开发步骤如下:
1. 将服务端的AIDL的相关文件复制到客户端中,注意:相关文件的包名,类名不允许修改;
2. 开发ServiceConnection接口中的实现类;
3. 使用隐式意图绑定服务端的Service;
4. 在ServiceConnection接口中的实现类的onServiceConnected()方法中,通过AIDL的内部类Stub的asInterface()方法,将IBinder对象转换为ADIL接口的对象。
注意事项
绑定远程Service时,组件的相关方法执行顺序:
1. 客户端 绑定远程Service,即可调用bindService()方法;
2. 服务端 Service的生命周期开始,即onCreate()->onBind();
3. 客户端 ServiceConnection中onServiceConnected()方法被回调
在客户端执行bindService()时,由于服务端的Service尚未启动,更没有返回IBinder对象时,则客户端中onServiceConnected()方法还没有被回调,因此,直接在客户端bindService()后马上调用远程服务可能会导致NullPointerException.、
AIDL 中的数据类型
在AIDL接口中,如需使用以下数据类型,无需导包语句即可直接使用:
JAVA的基本数据类型,包括byte,short,int,long,float,double,char,boolean;
String与CharSequence;
List与Map
如需使用自定义数据类型,或List,Map中封装自定义数据类型的数据则要求:
1. 自定义的数据类型实现Parcel able接口;
2. 编写自定有数据类型的*.aidl文件;
3. 无论自定的数据类型与AIDL接口文件释放在同一个包中,在使用自定义数据类型时,都需要在AIDL接口文件中显示的使用import语句导入
4. 将自定义数据类型的*.java源文件与*.aidl源文件都复制到客户端。