Services-Android 6.0开发者文档
原文地址:Service
一、前言
service没有界面,通常用于在后台进行耗时操作。其他应用组件可以启动一个service,此service在启动后可以长时间运行在后台,即使用户切换到了其他应用。另外,应用组件可以绑定service并与它通信,甚至进行进程间通信。service的使用场景有:网络操作、播放音乐、文件IO、content provider操作等。
service有两种形式:
- started。
如果应用组件通过startService()启动一个service,那么这个service就是started形式。service通过started启动后,会一直存在于后台,即使启动它的应用组件被销毁。通常,started形式的service会执行单一操作,并不会向调用方返回结果。例如,某个service从网络下载或者上传文件,当此操作完成,它会自己stop自己 - bound。
如果应用组件通过bindService()启动一个service,那么这个service就是bound形式。bound形式的service提供client-service接口,允许调用方与service通信,发送请求,获取结果,甚至做跨进程通信。bound形式的service与绑定它的组件生命周期一样长(绑定它的组件销毁,那么此service也会销毁)。service可以被多个组件绑定,这时,当所有的组件都解绑时,service才会被销毁
虽然本文档是将两种形式的service分开描述的,但是你的service可以同时支持两种形式。你的service支持的形式仅仅取决于你实现的回调方法:实现onStartCommand()允许其他组件start,实现onBind()允许其他组件绑定。
不管你的service是否已经被started或者bound,其他应用组件仍然可以使用你的service,即使是其他应用。但是,你可以将service声明为私有(在manifest中设置),从而阻止其他应用的访问。
注意:service运行在宿主进程的主线程——它不会创建自己的线程,也不是在分离出来的进程中运行(除非你指定)。这意味着,你的service需要在子线程进行CPU高消耗的操作或者阻塞操作,从而减少ANR并优化用户体验。
二、基本内容
通过继承Servie类或者它的子类可以创建一个service。在你的service实现中,你需要重写几个回调方法,以处理service的生命周期并提供启动你的service的机制。重要的几个回调方法如下:
- onStartCommand()。
当其他组件通过startService()启动service时,会调用此方法。此方法执行后,service就进入start状态且无限期的运行在后台。如果你实现了此方法,那么当功能完成后停止service就成了你的责任(通过stopSelf()或stopService())。如果你仅仅提供绑定的方式,那么你不需要实现此方法。 - onBind()。
当其他组件通过bindService()启动service时,会调用此方法。在此方法中,你必须返回一个IBinder,其表示绑定方与你的service通信的接口。你必须实现此方法,但是如果你不希望别的组件绑定此service,可以返回null。 - onCreate()。
当service初次创建时调用此方法。你可以在这个方法中进行一些初始化工作(它先于onStartCommand和onBind被调用)。如果service已经运行了,那么此方法不会被调用。 - onDestroy()。
当service被销毁时调用此方法。你可以在这个方法中清理占用的资源,比如线程、已注册的listener、receiver等。这个方法是service能收到的最后的回调。
如果其他组件通过startService()启动你的service,那么这个service会在后台一直运行,直到它停止自己-stopSelf(),或者其他组件停止它-stopService()。
如果其他组件通过bindService()启动你的service,那么这个service只有在被绑定期间运行,只要所有的绑定方都解绑,那么service就会被销毁。
Android系统可能会在内存过低时为了回收内存而停止service。如果此service是被有用户焦点的activity绑定的,那么它被销毁的可能性会小一点。如果此service被声明为“前台服务”(run in the foreground),那么它将几乎不会被销毁。其他情况的service,如果在后台停留了很长时间,那么系统会降低它在后台task的优先级,从而很可能被系统销毁。如果你的service已经启动了,那么你需要用某种方式处理由系统发起的重启。如果系统销毁了你的service,那么它会在资源可用的时候尽快重启你的service。
下面的部分将会叙述怎样创建各种类型的service和怎样使用它。
2.1 在manifest中声明service
像其他组件一样,你需要在manifest文件中声明service。
声明方法是在<application>
下添加<service>
标签,如下所示:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
在<service>
标签下你可以添加一些属性,以控制service的一些行为,如要求启动service的权限、指定service运行的进程等。android:name是必须的,它指定了service的类名。在发布你的应用后,你不应该再改变此属性,以防依赖于此属性的显式intent被影响。
不为service声明intent filter可以保证你的service的安全,你可以使用显式intent启动或绑定你的service。如果你决定允许通过非显式intent启动你的service,可以为service添加intent filter,这样就可以不用在intent中添加组件名称了,但是你必须在intent中通过setPackage()指定包名,以提供足够详细的信息来找到目标service。
另外,如果你希望你的service只能在本应用使用,可以将android:exported属性设置为false。
三、创建started形式的service
通过startService()启动的service即started形式的service,其对应service的onStartCommand()方法。
当service通过start启动后,它的生命周期就独立于启动它的组件了,它将在后台持续运行,即使启动它的组件被销毁。因此,这种service应该在工作完成后停止自己(通过stopSelf()方法),或者其他应用通过stopService()停止它。
应用组件,如activity,可以通过startService()启动service,并可以在intent中添加service需要的数据。service将会在onStartCommand()方法中收到此intent。
举个例子,某个activity需要将数据保存到云端数据库。那么此activity可以start一个service,并且将数据传给它(在startService()时通过intent添加数据)。service通过onStartCommand()收到intent并取出数据,然后连接网络、操作数据库。当操作结束后,service就stopSelf,被系统销毁。
注意:默认情况下,service运行在声明它的应用的进程的主线程中。所以,如果在service进行复杂或者阻塞操作会影响用户与activity的交互,在这种情况下,你应用新建一个线程进行这些操作。
通常,你可以继承以下两个类来创建service:
- Service
它是所有service的基类。如果你继承了这个类,那么你应该在新的线程中执行各种操作,以防阻塞主线程。 - IntentService
它是Service的子类,使用worker线程处理传入的请求。如果你不需要同时处理多个请求,可以选择继承它。你需要做的,只是实现它的onHandleIntent()方法,其会收到每个start请求传入的intent,这样你就可以在后台执行工作了。
下面的部分描述了通过以上两个类创建service的方法。
3.1 继承IntentService类
大多数started形式的service不需要同时处理多个请求,那么你可以使用IntentService来实现你的service。
IntentService做了以下事情:
- 创建默认的worker线程处理应用主线程通过onStartCommand()分发的intent
- 创建一个work队列,每次向onHandleIntent()传递一个intent,这样你就不需要担心多线程处理了
- 在所有的请求都被处理后停止service,所以你不需要调用stopSelf()
- 提供默认的onBind()方法实现,其返回null
- 提供默认的onStartCommand()实现,其会将传入的intent传递给work队列,后者会将intent依次传递给onHandleIntent()
你需要做的,只是在onHandleIntent()方法中处理调用方传来的intent。当然,你也需要提供一个简单的service构造方法。
下面是一个IntentService的例子:
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
如果你想要重写其他的回调方法,比如onCreate()、onStartCommand()、onDestroy(),请保证调用对应超类的实现。
例如,onStartCommand()必须返回默认的实现(默认实现是将intent传递给onHandleIntent()):
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
在onHandleIntent()中,你不需要调用超类实现。另外在onBind()中,你也不需要调用超类实现,但是如果你不允许绑定你的service,最好不实现此方法。
3.2 继承Service类
在前面我们可以看到,通过IntentService可以很简单的实现一个started形式的service。但是,如果你想要你的service可以处理多线程,而不是通过work队列依次处理请求,可以继承Service类处理每个intent。
作为比较,下面使用Service类实现了与使用IntentService具有相同功能的service。下面的代码逻辑为:将每个start的请求加入到工作线程中,每次处理一个请求。
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
正如你看到的,代码比IntentService多很多。
但是,这种方式可以自己处理onStartCommand()的每个请求,你可以同时处理多个请求。当然,这个功能在上面的代码中没有实现,如果你想,可以为每个请求开启一个新线程去处理,而不是像上面一样等待上一个完成才处理。
注意,onStartCommand()方法必须返回一个整数。这个整数表示,如果系统kill了你的service将会怎样重启你的service(之前也提到,IntentService已经为你做了这些,你不需要做这些)。onStartCommand()的返回值必须为以下值:
- START_NOT_STICKY
如果系统在onStartCommand()方法返回后kill了此service,那么系统不会再重启这个service,除非有pending intent尚未分发。这个值可以避免service的不必要启动,如果你的应用能够重启未完成工作那么可以使用这个值 - START_STICKY
如果系统在onStartCommand()方法返回后kill了service,那么系统会重新创建service并调用其onStartCommand()方法,但是不会再传递最后的intent,相反,系统会传入一个空的intent(除非是使用pending intent启动service,那么系统还是会传入原intent)。这个值适合多媒体播放器或类似的service,它们不执行某种命令,而是在后台持续运行等待工作 - START_REDELIVER_INTENT
如果系统在onStartCommand()方法返回后kill了service,那么系统会重建service并调用其onStartCommand()方法,并传入最后的intent(pending intent也会依次被传入)
3.3 启动service
你可以在某个activity或其他应用组件中启动service,调用startService()并传入指定了目标service的intent即可。系统会调用目标service的onStartCommand()方法并传入你的intent(你不应该直接调用onStartCommand()方法)。
下面的例子说明了怎样使用显式intent启动前面提到的service:
Intent intent = new Intent(this, HelloService.class);
startService(intent);
startService()方法会立刻返回,之后系统会调用目标service的onStartCommand()方法。如果目标service并没有在运行,那么系统会先调用onCreate(),然后调用onStartCommand()。
如果service不支持绑定方式,那么通过startService()传递intent就是应用组件和service通信的唯一方式了。但是,如果你需要service向应用组件返回结果,那么应用组件可以先创建一个广播的PendingIntent并传递给service,然后service可以用这个广播发送结果。
多次start一个service会调用多次onStartCommand()。stop此service只需调用一次stopSelf()或stopService()即可。
3.4 停止service
started形式的service必须自己管理自己的生命周期。这是因为在onStartCommand()方法返回后,service会持续运行,除非系统需要回收内容,否则系统不会停止或销毁service。所以service必须通过stopSelf()停止自己,或者其他组件通过stopService()停止它。
调用stopSelf()或stopService()后,系统会尽快销毁此service。
如果service在onStartCommand()中同时处理多个请求,那么你不应该在某个请求被处理完成后停止这个服务,因为可能会有新的请求进来。要避免这种情况,你可以使用stopSelf(int)方法,它会保证你的stop请求总是基于最近的start请求。当你调用stopSelf(int)时,你需要传入start请求的id(通过onStartCommand()的startId获得),以指定你的stop请求对应了哪个start请求。这样的话,当service收到新的start请求,之前的stopSelf(int)中的ID就会与新的start请求不匹配,service就不会被停止了。
注意:你的应用应该在service完成工作后停止它,以防浪费系统资源、消耗电量。如果必要的话,其他应用组件可以通过stopService()停止此service。即使你使用的是bound形式的service,你也得在收到onStartCommand()调用后停止你的service
四、创建bound形式的service
bound形式的service允许应用组件通过bindService()绑定它,以创建一个持续的连接。通常bound形式的service不允许用于组件通过startService()启动它。
如果你希望service能与其他应用组件互动或者通过进程间通信分享功能,可以使用bound形式的service。
要创建bound形式的service,你必须实现onBind()方法,并返回一个定义了通信接口的IBinder。其他应用组件通过bindService()获取接口,并通过其中的方法与service通信。bound形式的service只在有应用组件绑定它时运行,如果没有组件绑定到此service,系统会销毁它(你不必像started形式的service那样自己停止service)。
如果有多个应用组件绑定了service,那么应用组件在结束通信后,需要通过unbindService()解绑,当所有的应用组件都解绑了,系统会销毁此service。
bound形式的service比started形式的service难的多,所以这部分内容另写一篇文档。
五、向用户发送通知
service在运行时,可以通过Toast Notification或者Status Bar Notification通知用户一些信息。
Toast Notification是在桌面上显示一段时间的信息,Status Bar Notification会在通知栏显示图标和信息,用户可以点击此通知来进行某个动作。
通常,Status Bar Notification是后台工作完成后进行通知的最好方式,用户可以看到并与它互动。当用户点击了通知,通知可以进行某些动作。
六、在前台运行service
前台service指用户可以“看到”的service,系统不会在内存紧张的时候kill前台service。前台service必须在状态栏显示一个通知,此通知位于顶栏下面。除非service停止或者从前台移除,否则此通知不会被关闭,。
例如,音乐应用会在service中播放音乐,此service一般为前台service,用户可以明确的看到此service。状态栏中的通知表示当前播放歌曲,并且允许用户点击以打开音乐应用。
你可以通过startForeground()让你的service在前台运行。这个方法有两个参数:一个是整数,表示通知的id,另一个是要显示的notification对象。示例如下:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
注意:startForeground()的整数ID不能为0
你可以通过stopForeground()方法将service从前台移除。这个方法需要一个boolean参数,表示是否将状态栏通知一起移除。这个方法不会停止service。当然,如果你直接停止了service,那么它也会从前台移除(包括状态栏通知)。
七、管理service生命周期
service的生命周期类似于activity。但是你需要特别注意service创建和销毁过程,因为service可以在用户不知情的情况下运行在后台。
service的生命周期从创建到销毁有两条路线:
- started service
- bound service
这两条路线不是完全分离的,你可以绑定已经通过startService()启动的service。例如,后台音乐service可能被startService()启动(传入要播放的音乐的信息),之后,用户可能想要获取当前音乐的信息,那么就会有一个activity通过bindService()绑定此service。在这个例子中,stopService()或者stopSelf()在解绑以前不能真正的停止此service。
7.1实现生命周期方法
类似于activity,你可以实现service的一些生命周期回调方法以监控service状态,并在适当时候进行一些工作。下面是生命周期方法的示例:
public class ExampleService extends Service {
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
@Override
public void onCreate() {
// The service is being created
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy() {
// The service is no longer used and is being destroyed
}
}
注意:与activity生命周期回调方法不同的是,你不需要调用超类的实现
通过实现上面的方法,你可以监控service生命周期的两个环:
- entire lifetime
entire lifetime从service的onCreate()调用开始到onDestroy()返回结束。类似于activity,service在onCreate()中初始化设置,并在onDestroy()中释放资源。例如,音乐service可以在onCreate()中新建线程播放音乐,在onDestroy()中停止线程。
不管service是通过startService()创建的还是通过bindService()创建的,onCreate()与onDestroy()方法都会被调用。 - active lifetime
active lifetime从onStartCommand()或onBind()开始,这两个方法会处理传入的intent。started形式的service的active lifetime与entire lifetime一起结束。bound形式service的active lifetime从onUnbind()返回后结束
注意:虽然started形式的service是通过stopSelf()或stopService()停止的,但是没有对应的回调方法,即没有onStop()方法。所以,除非service被某个组件绑定,否则当此service进入stopped状态,系统便会销毁它——onDestroy()是唯一会被调用的方法
下图1表示了service的典型回调方法。虽然图中将started形式的service与bound形式的service分开了,但是注意,不管service是怎样被启动的,都可以进行bind或start。所以,通过onStartCommand()初始化的service仍然可以收到onBind()的调用。
上一篇: java计算1000以内所有不能被7整除的整数之和
下一篇: 89.格雷编码
推荐阅读
-
Services-Android 6.0开发者文档
-
fragment-Android 6.0开发者文档
-
注册开发者数量超400万!华为HMS Core 6.0全球上线
-
七牛开发者文档关于上传凭证,url安全base64得出的数据不同
-
Oracle 10.2.0.1 For CentOS 6.0 安装文档
-
Oracle 10.2.0.1 For CentOS 6.0 安装文档
-
微信支付开发7 收货地址共享接口V2 微信支付接口开发文档 微信支付 开发文档 微信开发者平
-
WinForm框架开发教程 - 多文档界面中的子窗口弹出、拖拽处理 WinFormdevexpressc#.net 6.0
-
fragment-Android 6.0开发者文档
-
微信支付开发7 收货地址共享接口V2 微信支付接口开发文档 微信支付 开发文档 微信开发者平