1. 什么是service
Service是一个可以在后台执行长时间操作而不使用用户界面的应用组件。那么问题来了,既然它不使用用户界面,那么它怎么知道应该什么时候开始执行什么操作呢?答案是——它可以与其他的引用组件形成一些联系,从而可以根据其传来的信息在合适的时候执行合适的操作。
一般来讲,这种联系分为两种:startService()以及bindService()。这两种联系都可以使得一个service开始运行,但是在其他方面有着诸多不同。
service | 启动service的方式 | 停止service的方式 | service与启动它的组件之间的通信方式 | service的生命周期 |
---|---|---|---|---|
startService | 在其他组件中调用startService()方法后,服务即处于启动状态 | service中调用stopSelf()方法,或者其他组件调用stopService()方法后,service将停止运行 | 没有提供默认的通信方式,启动service后该service就处于独立运行状态 | 一旦启动,service即可在后台无限期运行,即使启动service的组件已被销毁也不受其影响,直到其被停止 |
bindService | 在其他组件中调用bindService()方法后,服务即处于启动状态 | 所有与service绑定的组件都被销毁,或者它们都调用了unbindService()方法后,service将停止运行 | 可以通过 ServiceConnection进行通信,组件可以与service进行交互、发送请求、获取结果,甚至是利用IPC跨进程执行这些操作 | 当所有与其绑定的组件都取消绑定(可能是组件被销毁也有可能是其调用了unbindService()方法)后,service将停止 |
2. 如何创建一个service?
关于这个,每本Android入门的书籍基本上都会有所提及,基本上也正如它们所述:
创建一个类继承自Service(或它的子类,如IntentService),重写里面的一些关键的回调方法,如onStartCommand(),onBind()等
在Manifests文件里面为其声明,并根据需要配置一些其他属性。
onCreate()
在每个service的生命周期中这个方法会且仅会调用一次,并且它的调用在onStartCommand()以及onBond()之前,我们可以在这个方法中进行一些一次性的初始化工作。
onStartCommand()
当其他组件通过startService()方法启动service时,此方法将会被调用。
onBind()
当其他组件通过bindService()方法与service相绑定之后,此方法将会被调用。这个方法有一个IBinder的返回值,这意味着在重写它的时候必须返回一个IBinder对象,它是用来支撑其他组件与service之间的通信的——另外,如果你不想让这个service被其他组件所绑定,可以通过在这个方法返回一个null值来实现。
onDestory()
这是service一生中调用的最后一个方法,当这个方法被调用之后,service就会被销毁。所以我们应当在这个方法里面进行一些资源的清理,比如注册的一些监听器什么的。
在Manifests文件里进行声明的时候,只有android:name属性是必须要有的,其他的属性都可以没有。但是有的时候适当的配置可以让我们的开发进行地更加顺利,所以了解一下注册一个service可以声明哪些属性也是很有必要的。
<service
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
</service>
android:enabled
如果为true,则这个service可以被系统实例化,如果为false,则不行。默认为true
android:exported
如果为true,则其他应用的组件也可以调用这个service并且可以与它进行互动,如果为false,则只有与service同一个应用或者相同user ID的应用可以开启或绑定此service。它的默认值取决于service是否有intent filters。如果一个filter都没有,就意味着只有指定了service的准确的类名才能调用,也就是说这个service只能应用内部使用——其他的应用不知道它的类名。这种情况下exported的默认值就为false。反之,只要有了一个filter,就意味着service是考虑到外界使用的情况的,这时exported的默认值就为true
android:icon
一个象征着这个service的icon
android:isolatedProcess
如果设置为true,这个service将运行在一个从系统中其他部分分离出来的特殊进程中,我们只能通过Service API来与它进行交流。默认为false。
android:label
显示给用户的这个service的名字。如果不设置,将会默认使用<application>的label属性。
android:name
这个service的路径名,例如“com.lypeer.demo.MyService”。这个属性是唯一一个必须填的属性。
android:permission
其他组件必须具有所填的权限才能启动这个service。
android:process
service运行的进程的name。默认启动的service是运行在主进程中的。
下面是一个例子
public class ServiceDemo extends Service {
private static final String TAG = "ServiceDome";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
//只在service创建的时候调用一次,可以在此进行一些一次性的初始化操作
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
//当其他组件调用startService()方法时,此方法将会被调用
//在这里进行这个service主要的操作
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
//当其他组件调用bindService()方法时,此方法将会被调用
//如果不想让这个service被绑定,在此返回null即可
return null;
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
//service调用的最后一个方法
//在此进行资源的回收
super.onDestroy();
}
}
<!--in manifests -->
<service android:name=".ServiceDemo"/>
3. 如何启动service?
startService()
另一个组件通过调用startService()方法,就可以启动一个特定的service,并且这将导致service中的onStartCommand()方法被调用。在调用startService()方法的时候,其他组件需要在方法中传递一个intent参数,然后service将会在onStartCommand()中接收这个intent,并获取一些数据。比如此时某个Activity要将一些数据存入数据库中,我就可以通过intent把数据传入service,然后让service去进行连接数据库,存储数据等操作,而此时用户可以执行其他的任何操作——甚至包括销毁那个Activity——这并不会影响service存储数据这件事。
当一个service通过这种方式启动之后,它的生命周期就已经不受启动它的组件影响了,它可以在后台无限期的运行下去,只要service自身没有调用stopSelf()并且其他的组件没有调用针对它的stopService()。
另外,如果确定了使用这种方式启动service并且不希望这个service被绑定的话,那么也许除了传统的创建一个类继承service之外我们有一个更好的选择——继承IntentService。
如果是扩建Service类的话,通常情况下我们需要新建一个用于执行工作的新线程,因为默认情况下service将工作于应用的主线程,而这将会降低所有正在运行的Activity的性能。而IntentService就不同了。它是Service的子类,它使用工作线程来注意的处理所有的startService请求。如果你不要求这个service要同时处理多个请求,那么继承这个类显然要比直接继承Service好到不知道哪里去了——IntentService已经做了这些事:
创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent
创建工作队列,用于将一个 Intent 逐一传递给 onHandleIntent() 实现,这样的话就永远不必担心多线程问题了
在处理完所有启动请求后停止服务,从此妈妈再也不用担心我忘记调用 stopSelf() 了
提供 onBind() 的默认实现(返回 null)
提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现
例子:
//现在在一个Activity里面 Intent intent = new Intent(MainActivity.this , IntentServiceDemo.class); startService(intent)
public class IntentServiceDemo extends IntentService { public IntentServiceDemo(String name) { super(name); //构造方法 } @Override protected void onHandleIntent(Intent intent) { //在这里根据intent进行操作 } }
bindService()
这是一种比startService更复杂的启动方式,同时使用这种方式启动的service也能完成更多的事情,比如其他组件可向其发送请求,接受来自它的响应,甚至通过它来进行IPC等等。我们通常将绑定它的组件成为客户端,而称它为服务器。 如果要创建一个支持绑定的service,我们必须要重写它的onBind()方法。这个方法会返回一个IBinder对象,它是客户端用来和服务器进行交互的接口。而要得到IBinder接口,我们通常有三种方式:继承Binder类,使用Messenger类,使用AIDL