Service 入门

Service

什么是Serviece

Serciece 表示”服务”。

Serviceandroid系统的核心组件之一。

Service的本质是一个继承了android.app.ServiceJAVA

  每个Service都应该在AndroidManifest.xml文件中进行注册;

  ServiceAndroid系统进行维护

Service的定位

  Service没有匹配的用户界面,通常用于后台处理的耗时操作。

不许在主线程中执行耗时操作

Service是运行在主线程中的;

尽管Service被定为”用于处理耗时操作”,但是各种耗时操作需要在Service中另外开辟线程来完成。

  组件可以绑定到Service,实现进程间通信(IPC:Inter process Communication)。

ServiceActivity生命力更强,不容易被销毁掉、

进程优先级

Android 系统力图维护尽可能多的进程,但由于设备性能有限,在动态管理内存的过程中,Android系统经常终止一些优先级低的进程,以释放资源,保证优先级高的进程正常运行

进程的优先级分类如下(从高到底):

1. 前台进程(Foreground Process

  存在正在与用户交互的Activity;

  存在Service,且Service绑定在与用户交互的Activity之上

  存在运行在前台的Service,即该Servicestart 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的方法:

  通过调用ContextstopService(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 Playerreset()方法重置(非必要,但推荐);

  调用Media PlayersetDataSource()方法设置需要播放的歌曲;

  调用Media Player prepare()方法加载(缓冲)歌曲;

  调用Media Playerstart()方法播放歌曲;

  当退出时应该调用release()方法释放资源。

逻辑:

通过Activity激活Service,且在Service中创建Media Player的实例,实现歌曲的播放;

被播放的歌曲保存在模拟器的sdcard/Music/;

Activity被停止时,停止播放歌曲的Service

Service被停止时,释放Media Player的资源;

关于MediaPlayer

MediaPlayer支持主流的音频,视频文件的播放,亦支持播放非本机的媒体文件;

Media Player会开启子线程播放歌曲;

可调用pause()方法暂停播放,调用seekTo()方法快进到指定的位置开始播放;

可调用用prepareAsync()方法加载歌曲,并配置OnPreparedListener,在监听器用调用MediaPlayerstart()方法;

通常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后,该ServiceonBind()方法的返回值(非null)对象被会回传到当前组件,即当前组件与被激活的Service存在相同的IBinder对象;

Int flags:标志。

关于ServiceonBind()方法

在绑定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的绑定;

  重写ActivityonDestroy()方法,调用unbindService()方法取消与Service的绑定,以避免Activity被销毁时绑定仍然存在导致的异常

取消绑定

当与Service绑定的组件被销毁时,应该及时取消与Service的绑定,否则导致异常。

在组件中,调用unbindService(ServiceConntion conn)方法则可以取消与Service的绑定

Service绑定的生命周期

图在上面,绑定的默认不是粘性的,也就是说它被意外终止掉是不会重新启动的

当绑定Service之后,Activity被销毁之前应该取消与Service的绑定,否则将抛出异常。

关于ServiceConnectiononServiceDisconnected()

ServiceConnectiononServiceDisconnected()方法并不会随着组件取消与Service的绑定而回调,该方法仅在Service在意外情况下崩溃时被调用

关于ServiceonRebind()

一般情况下,onRebind()方法并不会被回调,被回调的情景通常为:

  与该Service的所有组件都已经取消与它的绑定,导致该ServiceonUnbind()方法被回调,且重写了onUnbind()方法返回值为true;

Service没有被销毁且再次被绑定时,则被回调onRebind()方法。

注意:以绑定模式激活的Service组件并不是粘性的,且与Service绑定的组件在推出之前必须取消绑定,即无法保证Service组件依然存在,所以,为了保证其他组件可以销毁,但Service依然存在,可以:

1. 先调用startService()激活Service组件;

2. 在调用bindService()实现绑定。

 

实现ActivityService通信

组件间通信的方式

已知:在Service中,onBind()方法的返回值可以被Activity获得,即ActivityService共有一个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的子类对象,并将其作为ServiceonBind()的返回值

  服务端 必须配置Service是可以隐式激活的

  客户端 将服务端的AIDL文件复制到客户端

  客户端 ServiceConnectiononServiceConnected()中,使用AIDL接口的StubasInterface()方法,将参数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的内部类StubasInterface()方法,将IBinder对象转换为ADIL接口的对象。

注意事项

绑定远程Service时,组件的相关方法执行顺序:

1. 客户端 绑定远程Service,即可调用bindService()方法;

2. 服务端 Service的生命周期开始,即onCreate()->onBind();

3. 客户端 ServiceConnectiononServiceConnected()方法被回调

在客户端执行bindService()时,由于服务端的Service尚未启动,更没有返回IBinder对象时,则客户端中onServiceConnected()方法还没有被回调,因此,直接在客户端bindService()后马上调用远程服务可能会导致NullPointerException.

 

AIDL 中的数据类型

AIDL接口中,如需使用以下数据类型,无需导包语句即可直接使用:

JAVA的基本数据类型,包括byte,short,int,long,float,double,char,boolean;

StringCharSequence;

ListMap

如需使用自定义数据类型,或List,Map中封装自定义数据类型的数据则要求:

1. 自定义的数据类型实现Parcel able接口;

2. 编写自定有数据类型的*.aidl文件;

3. 无论自定的数据类型与AIDL接口文件释放在同一个包中,在使用自定义数据类型时,都需要在AIDL接口文件中显示的使用import语句导入

4. 将自定义数据类型的*.java源文件与*.aidl源文件都复制到客户端。

猜你喜欢

转载自blog.csdn.net/zeyu_rensheng/article/details/80323608