接下來我們開始學習如何在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_STICKY:這個數值代表,當系統殺死這個Service的時候,系統會重新創建這個Service,並且調用onStartCommond方法,但是不會重新發送最後一個Intent。相反,系統用一個null的Intent調用onStartCommond方法,除非有一些pendingIntent來開啓這個Service,在這種情況下這些Service被髮送。這種方式適合於一個音樂播放器,不執行命令只是在後臺運行並且等待着一個工作。
START_REDELIVER_INTENT:這個數值代表如果系統殺死了這個Service,系統會重新創建這個Serivice,並且用發送給這個Service的最後一個Intent來調用onStartCommond方法。任何pendingIntent被依次發送。這適合於需要立即恢復的工作,例如文件下載。