接下来我们开始学习如何在Manifest文件中定义一个Service
<span style="font-size:14px;"><manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest></span>
我们可以在Service这个元素中定义一些属性例如启动这个Service所需要的权限或者这个Service将要运行的进程。而android:name是仅仅必须的——它用来指定这个Service的类名称。一旦你发布了自己的应用,这个Service的名称就不能改变,因为这样可能影响到其他将要调用这个Service的应用。有的时候为了确保我们的app的安全,我们在启动或者绑定一个Service的时候定义一个精确的Service而不会定义这个Service的Intent Filter。另外,如果我们想要我们自己的程序运行我们的Serivce,我们可以通过定义andriod:exported = false。这样将有效的阻止其他应用程序启动这个Service,甚至于通过一个不精确的Intent的。
创建一个已经启动的Service?
一个已经启动的Service就是其他的组件调用startService方法或者bindService方法启动并且调用这个Service的onStartCommond方法的Service。
当一个Service被启动的时候,它就会拥有独立于启动这个Service的组件的生命周期,并且这个Service能够独立运行在后台,即使这个组件被销毁掉。这个Service将会一直运行下去直到这个Service自己调用stopSelf方法,或者另一个组件调用stopService方法停止这个Service。
一个应用程序的组件例如一个Activity能够通过通过startService方法来开启这个Service,并且向这个Service传递一个Intent并且包含一些这个Service要使用的数据,而这个Service将会在onStartCommond方法中接受到这个Intent。
在这里我们可以通过继承Service和IntentService类来定义一个自己的Service。
Service:这个是所有Service的父类。当我们使用这个Service的时候,我们需要自己开启开启一个新的线程去完成这个Service要做的工作。因为这个Service默认的使用这个应用程序的主线程,这样将要减慢应用程序的速度。
IntentService:这是一个Service的子类,它用一个工作线程去完成所有请求的任务,但是一次只能执行一个请求。这样,这个IntentService就成为了我们最好的选择,如果我们的Service不要求多线程工作的话。我们所需要做的是实现onHandleIntent方法,在这个方法中我们接受到每个请求的Intent,这样就能够在后台工作。
继承IntentService类
因为大多数被启动的Service不需要同时进行多个请求(这也是一种非常危险的多线程方案),如此一来,继承IntentService类来定义自己的Service可能成为一种最好的方式。
IntentService做了以下操作:
1、创建一个默认的工作线程用来执行所有的发送给onStartCommond方法的Intent,这个线程是独立于这个应用的主线程的。
2、创建一个工作队列,这个队列一次只能传递一个Intent给onHandleIntent方法的实现,因此我们不需要担心多线程的问题。
3、当所有的请求的Intent进行完成以后,这个Service自动停止,我们不需要调用stopSelf方法来停止这个Service。
4、提供onBind的默认实现,返回一个NULL。
5、提供onStartCommond方法的默认实现,发送Intent到工作队列,然后传递给onHandleIntent方法的默认实现。
<span style="font-size:14px;">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) {
}
}
}
}
}</span>
有以上代码我们可以看出我们需要做的仅是:提供一个构造方法并且实现onHandleIntent方法。
但是如果我们也想要实现其他方法,我们必须记得首先调用他们父类的实现,以至于IntentService能够正确的操作这个工作线程的生命周期。
<span style="font-size:14px;">@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);
}</span>
除了onHandleIntent方法以外,我们不需要调用父类实现的唯一的方法是onBind()方法。
继承Service类
正如上面的例子一样,继承IntentService将会事我们自定义的Service更加简单。然而,如果我们希望我们的Service能够表现多线程操作,而不是通过工作队列,那么我们需要继承Service来操作每一个Intent。
作为比较,下面的代码实例是一个继承了Service类并且进行和IntentService相同操作,对于每一个请求都用一个工作线程去完成,一次仅仅进行一个请求。
<span style="font-size:14px;">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();
}
}</span>
真如我们看到的,它做了比IntentService更多的工作才能完成相同的 任务。
然而,正是因为我们操作了每一个请求,这样我们也就可以同时进行多个请求的操作。如果我们想到达到这个目的,我们可以为每一个Intent启动一个新的线程来完成不同的工作。
注意onStartCommond方法必须返回一个int类型的数值,而这个数值描述了当系统杀死这个Service时的时候,系统将要怎么进行这个Service。返回的常量必须是以下几种数值:
START_NOT_STICKY:这个数值代表如果系统杀死了这个Service,这个Service将要不会重新创建,除非有一个PendingIntent被发送。这是最安全的方式,避免了当我们的Service在不需要或者仅仅需要重新启动未完成的任务的时候运行。
START_STICKY:这个数值代表,当系统杀死这个Service的时候,系统会重新创建这个Service,并且调用onStartCommond方法,但是不会重新发送最后一个Intent。相反,系统用一个null的Intent调用onStartCommond方法,除非有一些pendingIntent来开启这个Service,在这种情况下这些Service被发送。这种方式适合于一个音乐播放器,不执行命令只是在后台运行并且等待着一个工作。
START_REDELIVER_INTENT:这个数值代表如果系统杀死了这个Service,系统会重新创建这个Serivice,并且用发送给这个Service的最后一个Intent来调用onStartCommond方法。任何pendingIntent被依次发送。这适合于需要立即恢复的工作,例如文件下载。