Android創(chuàng)建服務(wù)之started service詳細(xì)介紹
創(chuàng)建started service
應(yīng)用組件(例如Activity)調(diào)用startService()來啟動(dòng)一個(gè)Service,將需要的參數(shù)通過Intent傳給Service,Service將會(huì)在onStartCommand函數(shù)中獲得Intent。
有兩種方式可以創(chuàng)建started service,一種是擴(kuò)展Service類,另外一種是擴(kuò)展IntentService類
擴(kuò)展Service
這是所有服務(wù)的基類。擴(kuò)展這個(gè)類的時(shí)候,特別重要的一點(diǎn)是,需要?jiǎng)?chuàng)建一個(gè)新的線程來做服務(wù)任務(wù),因?yàn)閟ervice默認(rèn)是運(yùn)行在你的主線程(UI線程)中的,它會(huì)使你的主線程運(yùn)行緩慢。
擴(kuò)展IntentService
這是一個(gè)service的子類,他可以在一個(gè)工作線程中處理所有的啟動(dòng)請(qǐng)求。如果你不需要同一時(shí)刻出來所有的服務(wù)請(qǐng)求,使用IntentService是一個(gè)很好的選擇。你需要做的僅僅是實(shí)現(xiàn)onHandlerIntent()方法,通過這個(gè)函數(shù)處理接受的每一個(gè)啟動(dòng)請(qǐng)求。
下面我們學(xué)習(xí)如何擴(kuò)展IntentService類和Service類
擴(kuò)展IntentService類
IntentService做了什么?
1.創(chuàng)建一個(gè)主線程的工作線程,用于執(zhí)行通過onStartCommand()傳來的所有的intent。
2.創(chuàng)建一個(gè)工作隊(duì)列,將接受的所有intent一個(gè)一個(gè)的傳給onHandlerIntent(),所以同一時(shí)間內(nèi)你只處理一個(gè)intent,不用擔(dān)心多線程的問題。
3.當(dāng)所有的請(qǐng)求都處理完后,停止服務(wù),所以你不需要手動(dòng)調(diào)用stopSelf()。
4.提供onBind()函數(shù)的默認(rèn)實(shí)現(xiàn),返回null
5.提供onStartCommand()函數(shù)的默認(rèn)實(shí)現(xiàn),它把intent發(fā)送到工作隊(duì)列中去,然后工作隊(duì)列再發(fā)送到你的onHandlerIntent()函數(shù)中。
有了上面的基礎(chǔ),你僅僅要做的就是實(shí)現(xiàn)onHandlerIntent()。并且實(shí)現(xiàn)一個(gè)小小的構(gòu)造函數(shù)。
參考下面的例子:
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) {
}
}
}
}
}
如果你要實(shí)現(xiàn)其他的回調(diào)函數(shù),例如 onCreate , onStartCommand ,onDestroy 一定記得調(diào)用父類相應(yīng)的函數(shù),這樣IntentService才能正確的處理工作線程。
例如,你需要在onStartCommand函數(shù)中彈出一個(gè)提示,那么你可以這樣寫:
@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);
}
有一點(diǎn)例外的是,如果你需要其他組件綁定服務(wù),那么你的onBind函數(shù)不需要調(diào)用父類的onBind。
在下一節(jié)中,你將會(huì)看到通過擴(kuò)展service類來實(shí)現(xiàn)與本節(jié)相同的服務(wù),所不同的是代碼更多,但是同樣意味著更靈活,特別是你需要同時(shí)處理多個(gè)請(qǐng)求時(shí),比較適合直接擴(kuò)展Service。
擴(kuò)展Service類
如同你在上一節(jié)看到的一樣,使用IntentService來實(shí)現(xiàn)一個(gè)started service非常簡(jiǎn)單。如果你需要你的service來執(zhí)行多個(gè)線程,那么你需要擴(kuò)展Service類去處理每個(gè)intent。
作為對(duì)比,下面的例子用Service類實(shí)現(xiàn)了和上一節(jié)中一樣功能的服務(wù)。
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的例子多了好多代碼。
因?yàn)槟阕约禾幚砻看蔚膐nStartcommand(),所以你可以在同一時(shí)間處理多個(gè)請(qǐng)求。這個(gè)例子沒有這樣做,但是如果你愿意,你完全可以每收到一個(gè)請(qǐng)求的時(shí)候,創(chuàng)建一個(gè)新線程并且立即運(yùn)行,而不是等到上一個(gè)請(qǐng)求處理完成后再運(yùn)行。
注意,onStartCommand()函數(shù)必須返回一個(gè)整數(shù)。這個(gè)整數(shù)描述了當(dāng)系統(tǒng)該服務(wù)時(shí)將要如何處理該服務(wù)。返回值必須是下面幾個(gè)值:
START_NOT_STICKY
在onStartCommand()返回后,系統(tǒng)服務(wù),服務(wù)將不會(huì)重啟,除非有pending intents(目前筆者還不懂什么是pending Intents)要投遞。這比較適合非常容易就可以重新啟動(dòng)未完成任務(wù)的情況。
START_STICKY
在系統(tǒng)服務(wù)后,重啟服務(wù)并且調(diào)用onStartCommand(),但是不重新投遞上一次的intent。系統(tǒng)會(huì)傳給onStartCommand()函數(shù)null,除非有pending intent來啟動(dòng)服務(wù),會(huì)傳給onStartCommand()相應(yīng)的intent。這種做法比較適合媒體播放服務(wù),不需要執(zhí)行命令,但是運(yùn)行,常處于等待任務(wù)的服務(wù)。
START_REDELIVER_INTENT
在系統(tǒng)服務(wù)后,重啟服務(wù)并且調(diào)用onStartCommand(),參數(shù)傳入上一次的intent,然后接下來是pending intent傳入onStartCommand()。這比較適合于正在執(zhí)行一項(xiàng)工作,它不能被立刻恢復(fù),例如下載文件。
啟動(dòng)Service
你可以從一個(gè)Activity或者其他組件調(diào)用startService(intent)來啟動(dòng)服務(wù)。Android系統(tǒng)會(huì)調(diào)用服務(wù)的onStartCommand()函數(shù)并且傳給它intent。
用上一節(jié)的HelloService來做個(gè)例子:
Intent intent = new Intent(this, HelloService.class);
startService(intent);
startService()會(huì)立刻返回,Android系統(tǒng)會(huì)調(diào)用服務(wù)的onStartCommand()函數(shù)。如果這是服務(wù)沒有在運(yùn)行,系統(tǒng)會(huì)首先調(diào)用onCreate()函數(shù),然后會(huì)緊接著調(diào)用onStartCommand()函數(shù)。
如果服務(wù)沒有提供綁定,那么通過startService()函數(shù)傳入的intent就是應(yīng)用組件和服務(wù)進(jìn)行交互的唯一途徑。如果你想服務(wù)發(fā)送結(jié)果給客戶組件,那么客戶組件需要在啟動(dòng)服務(wù)時(shí),創(chuàng)建一個(gè)用于廣播的PendingIntent,通過intent投遞給服務(wù)。這樣,服務(wù)就可以利用廣播把結(jié)果發(fā)送給客戶組件。
多個(gè)請(qǐng)求會(huì)導(dǎo)致服務(wù)的onStartCommand()被調(diào)用多次。但是,只需要一個(gè)停止請(qǐng)求(stopSelf() 或 stopService())就會(huì)使服務(wù)停止。
停止服務(wù)
一個(gè)啟動(dòng)的服務(wù)必須管理自己的生命周期。因?yàn)槌窍到y(tǒng)需要回收資源的時(shí)候,系統(tǒng)不會(huì)停止或者銷毀服務(wù)。所以,服務(wù)必須通過調(diào)用stopSelf()來停止自己,或者有其他組件調(diào)用stopService()。
一擔(dān)收到stopSelf()或stopService()的停止請(qǐng)求,系統(tǒng)會(huì)立刻銷毀服務(wù)。
如果你的服務(wù)同時(shí)處理多個(gè)服務(wù)請(qǐng)求,當(dāng)某個(gè)請(qǐng)求完成時(shí)就不能馬上停止服務(wù),因?yàn)槟憧赡芤呀?jīng)又接受了一個(gè)新的請(qǐng)求(在第一個(gè)請(qǐng)求完成時(shí)結(jié)束服務(wù)會(huì)終止第二個(gè)請(qǐng)求)。為了解決這個(gè)問題,你可以調(diào)用stopSelf(int)函數(shù)來保證你的停止請(qǐng)求總是基于最近的一次服務(wù)請(qǐng)求。這是因?yàn)椋{(diào)用stopSelf(int)函數(shù)
會(huì)把onStartCommand()函數(shù)傳入的startId傳給停止請(qǐng)求。當(dāng)你的startId和最近一次接受的服務(wù)請(qǐng)求不匹配時(shí),服務(wù)不會(huì)結(jié)束。
注意:stopSelf(int)函數(shù)只是簡(jiǎn)單的與最近一次收到的startId比較,如果你的服務(wù)處理過程是多線程的,可能后收到的服務(wù)請(qǐng)求先完成,那stopSelf(int)的方案不適合你,你應(yīng)該手動(dòng)管理接受到的服務(wù)請(qǐng)求和完成的服務(wù),比如在onStartCommand()函數(shù)中把startId記錄到一個(gè)表中,在完成服務(wù)任務(wù)時(shí)在表中記錄完成狀態(tài),在確保表中任務(wù)都完成的情況下直接調(diào)用stopSelf()來停止服務(wù)。
在前臺(tái)運(yùn)行服務(wù)
一個(gè)前臺(tái)服務(wù)是用戶能夠很明顯意識(shí)到的服務(wù),當(dāng)可用內(nèi)存比較低的時(shí)候,系統(tǒng)不會(huì)殺掉前臺(tái)服務(wù)。一個(gè)前臺(tái)服務(wù)必須提供一個(gè)狀態(tài)欄通知,放在正在運(yùn)行條目里,這意味著只要服務(wù)沒有停止或者一直是前臺(tái)服務(wù),這個(gè)通知就不會(huì)被消失。
舉個(gè)例子,一個(gè)音樂播放器服務(wù)應(yīng)該被設(shè)置為前臺(tái)運(yùn)行,因?yàn)橛脩舴浅C黠@知道他在運(yùn)行。這個(gè)狀態(tài)欄通知可以顯示正在播放的歌曲,并且可以允許用戶點(diǎn)擊進(jìn)入音樂播放界面。
要使服務(wù)運(yùn)行在前臺(tái)只要調(diào)用startForeground()。這個(gè)方法有兩個(gè)參數(shù):一個(gè)通知的整數(shù)ID和一個(gè)狀態(tài)欄通知。代碼片段:
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);
使用stopForeground()函數(shù)可以把服務(wù)從前臺(tái)移除。這個(gè)函數(shù)帶一個(gè)布爾值參數(shù),來表示是否從狀態(tài)欄移除通知。這個(gè)函數(shù)不會(huì)使服務(wù)停止。如果把前臺(tái)服務(wù)停止掉,那么通知也會(huì)同時(shí)被移除。
相關(guān)文章
Android自定義View實(shí)現(xiàn)數(shù)獨(dú)游戲
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)數(shù)獨(dú)游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android仿IOS UIAlertView對(duì)話框
這篇文章主要為大家詳細(xì)介紹了Android仿IOS UIAlertView對(duì)話框,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android布局中g(shù)ravity與layout_gravity屬性說明
這篇文章主要介紹了Android布局中g(shù)ravity與layout_gravity屬性說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
詳解Android App卸載后跳轉(zhuǎn)到指定的反饋頁面的方法
這篇文章主要介紹了Android App卸載后跳轉(zhuǎn)到指定的反饋頁面的方法,關(guān)鍵點(diǎn)是相關(guān)線程要判斷在目錄被消除以前作出響應(yīng),需要的朋友可以參考下2016-04-04
Android?Hilt?Retrofit?Paging3使用實(shí)例
這篇文章主要介紹了Android?Hilt依賴注入的使用,首先,某個(gè)類的成員變量稱為依賴,如若此變量想要實(shí)例化引用其類的方法,可以通過構(gòu)造函數(shù)傳參或者通過某個(gè)方法獲取對(duì)象,此等通過外部方法獲取對(duì)象實(shí)例的稱為依賴注入2023-01-01
Android Studio安裝配置方法圖文詳細(xì)教程
這篇文章主要為大家介紹了Android Studio下載和配置圖文詳細(xì)教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
Android通過HttpURLConnection和HttpClient接口實(shí)現(xiàn)網(wǎng)絡(luò)編程
這篇文章主要介紹了Android通過HttpURLConnection和HttpClient接口實(shí)現(xiàn)網(wǎng)絡(luò)編程的相關(guān)資料,需要的朋友可以參考下2015-02-02
Android中ProgressBar用法簡(jiǎn)單實(shí)例
這篇文章主要介紹了Android中ProgressBar用法,以簡(jiǎn)單實(shí)例形式分析了Android中ProgressBar進(jìn)度條控件的功能與布局相關(guān)技巧,需要的朋友可以參考下2016-01-01
Android Studio debug.keystore位置介紹
這篇文章主要介紹了Android Studio debug.keystore位置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03

