上篇我們講解了Android中的5中等級(jí)的進(jìn)程,分別是:前臺(tái)進(jìn)程、可見(jiàn)進(jìn)程、服務(wù)進(jìn)程、后臺(tái)進(jìn)程、空進(jìn)程。系統(tǒng)會(huì)按照內(nèi)存的需求先殺死等級(jí)較低的進(jìn)程。其中,后臺(tái)進(jìn)程、空進(jìn)程極容易被殺死,且殺死后不會(huì)重啟。但是今天將的服務(wù)進(jìn)程卻不同, 服務(wù)進(jìn)程被系統(tǒng)殺死后,內(nèi)存充足時(shí)會(huì)自動(dòng)重新啟動(dòng),適合做一些后臺(tái)的操作。今天我們就來(lái)講講Service。
這篇文章,我打算先直接從Google提供的Service的生命周期講起,根據(jù)生命周期的各種回調(diào)方法一步步深入。
Service的生命周期
Service可以看成一種沒(méi)有前臺(tái)界面的Activity,生命周期與Activity有點(diǎn)類似。

看看Service很NB啊,((o)/),既然可以做到被系統(tǒng)殺死后在內(nèi)存充足的情況下重啟,那么必有其特殊之處。不同的使用方式,有著不同的生命周期?,F(xiàn)在看不懂沒(méi)關(guān)系,后面會(huì)一個(gè)個(gè)講的。這只是個(gè)藥引子~
服務(wù)有兩種使用方法:
- 方式一:調(diào)用startService創(chuàng)建啟動(dòng)服務(wù)
- 方式二:調(diào)用bindService創(chuàng)建綁定服務(wù)
無(wú)論使用哪一個(gè)方法,講解這個(gè)之前都要先看看怎么創(chuàng)建一個(gè)Service。
聲明一個(gè)Service
- 清單文件配置:
<service android:name=".RemoteService"/>
為了不讓其他應(yīng)用調(diào)用到我們自己的Service,為了安全,我們不要在Service中添加Intentfilter過(guò)濾器。也就是說(shuō)盡量使用顯示啟動(dòng)方法或者綁定Service。
更安全的防護(hù)罩是在Service的清單文件中添加android:exported="false"這樣就將該Service與外界的APP隔絕起來(lái),其他應(yīng)用無(wú)權(quán)訪問(wèn)我們的服務(wù)。
- 繼承自Service或者其子類
public class RemoteService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
經(jīng)過(guò)上面兩個(gè)操作,我們便創(chuàng)建好了一個(gè)Service。
最顯眼的莫過(guò)于需要實(shí)現(xiàn)一個(gè)IBinder onBind(Intent intent),這個(gè)方法,現(xiàn)在先不講。先來(lái)看看如果啟動(dòng)一個(gè)服務(wù)。
聲明過(guò)Service之后,下面就分創(chuàng)建啟動(dòng)的服務(wù)和創(chuàng)建綁定的服務(wù)兩個(gè)來(lái)講解了,最后再總結(jié)講解總的生命周期。
創(chuàng)建啟動(dòng)的服務(wù)
分兩部分講,啟動(dòng)服務(wù)和停止服務(wù)。
啟動(dòng)服務(wù)
還是先看看這部分生命周期方法。
通過(guò)startService(intent)創(chuàng)建啟動(dòng)的服務(wù)
public void startService(View view){
Intent intent = new Intent();
intent.setClass(this, RemoteService.class);
intent.putExtra("sendData", "通過(guò)intent保存MainActivity組件中要傳遞的數(shù)據(jù)");
startService(intent);//啟動(dòng)RemoteService服務(wù)
}
前面講過(guò),可以將Service看出一個(gè)沒(méi)有前臺(tái)界面的Activity,看看現(xiàn)在在組件中(這里是Activity)啟動(dòng)一個(gè)Service也是如啟動(dòng)一個(gè)Activity一般如此簡(jiǎn)單。
講解創(chuàng)建啟動(dòng)的Service的生命周期方法
先看下演示,看下整體概況:
public class RemoteService extends Service {
@Override
public void onCreate() {//Service被創(chuàng)建時(shí)回調(diào)
super.onCreate();
Log.i("hui", "------onCreate-----");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {//Service被啟動(dòng)時(shí)回調(diào)
Log.i("hui", "--intent:"+ intent.getStringExtra("sendData")+ "flags="+flags+"---startId="+startId);
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {//Service被銷毀時(shí)回調(diào)
super.onDestroy();
Log.i("hui", "------onDestroy-----");
}
@Override
public IBinder onBind(Intent intent) { return null; }}
下面看看這種情況下的生命周期方法的具體流程。
onCreate()
當(dāng)Service被創(chuàng)建的時(shí)候會(huì)回調(diào)這個(gè)方法。第一次啟動(dòng)Service的時(shí)候,由于Service沒(méi)有被創(chuàng)建過(guò),故:先創(chuàng)建Service,然后再啟動(dòng)Service。所以:onCreater->onStartCommand。只有當(dāng)Service沒(méi)有創(chuàng)建的時(shí)候才會(huì)回調(diào)這個(gè)方法,一旦Service被創(chuàng)建后再次startService啟動(dòng)Service也不會(huì)再次回調(diào)這個(gè)方法。
onStartCommand
返回值
默認(rèn)返回super.onStartCommand(intent, flags, startId),看下這個(gè)方法。
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
默認(rèn)情況mStartCompatibility 為false,返回START_STICKY。其實(shí),這個(gè)返回值有三種。
還記得前面講解Service的生命周期的時(shí)候說(shuō)過(guò),Service的生命周期很NB,因?yàn)樗幌到y(tǒng)殺死后,在內(nèi)存充足的情況下回重新啟動(dòng)。
這里的重新啟動(dòng),有三種重新啟動(dòng)方式,上面的返回的值就對(duì)應(yīng)三種重新啟動(dòng)方式之一。下面就來(lái)看看者這三個(gè)具體的返回值??雌饋?lái)很煩人,不過(guò)都很好理解的,實(shí)在不理解沒(méi)關(guān)系,我們只需要返回默認(rèn)的就可以了。
- START_NO_STICKY
sticky是“粘性的”,這里指的是非粘性啟動(dòng)。下面翻譯Google文檔的解釋:
如果返回START_NOT_STICKY,表示當(dāng)Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,不會(huì)重新創(chuàng)建該Service,當(dāng)然如果在其被殺掉之后一段時(shí)間又調(diào)用了startService,那么該Service又將被實(shí)例化。
那什么情境下返回該值比較恰當(dāng)呢?
如果我們某個(gè)Service執(zhí)行的工作被中斷幾次無(wú)關(guān)緊要或者對(duì)Android內(nèi)存緊張的情況下需要被殺掉且不會(huì)立即重新創(chuàng)建這種行為也可接受,那么我們便可將 onStartCommand的返回值設(shè)置為START_NOT_STICKY。舉個(gè)例子,某個(gè)Service需要定時(shí)從服務(wù)器獲取最新數(shù)據(jù):通過(guò)一個(gè)定時(shí)器每隔指定的N分鐘讓定時(shí)器啟動(dòng)Service去獲取服務(wù)端的最新數(shù)據(jù)。當(dāng)執(zhí)行到Service的onStartCommand時(shí),在該方法內(nèi)再規(guī)劃一個(gè)N分鐘后的定時(shí)器用于再次啟動(dòng)該Service并開(kāi)辟一個(gè)新的線程去執(zhí)行網(wǎng)絡(luò)操作。假設(shè)Service在從服務(wù)器獲取最新數(shù)據(jù)的過(guò)程中被Android系統(tǒng)強(qiáng)制殺掉,Service不會(huì)再重新創(chuàng)建,這也沒(méi)關(guān)系,因?yàn)樵龠^(guò)N分鐘定時(shí)器就會(huì)再次啟動(dòng)該Service并重新獲取數(shù)據(jù)。 - START_STICKY(默認(rèn)返回值)
這里指的是粘性啟動(dòng)。既然粘性啟動(dòng),就不會(huì)向上面的非粘性啟動(dòng)那么的容易被干死而不啟動(dòng)。這種啟動(dòng)方式,啟動(dòng)后,如果在某一刻該服務(wù)被干死,那么系統(tǒng)會(huì)將這個(gè)Service標(biāo)記為" started"狀態(tài),這意味著Service是已經(jīng)啟動(dòng)的狀態(tài)。然后系統(tǒng)會(huì)嘗試著去重新啟動(dòng)該服務(wù)(try to re-create the service)的實(shí)例,然后再回調(diào)onStartCommand方法,但是此時(shí)intent的數(shù)據(jù)已經(jīng)是null了。所以使用這種方式啟動(dòng)的服務(wù)在使用intent時(shí)需要進(jìn)行非空判斷。
當(dāng)我們使用服務(wù)時(shí)不需要intent時(shí),以及可以在任何時(shí)間重啟Service都沒(méi)問(wèn)題的話就可以使用這種方式啟動(dòng)服務(wù)。例如:后臺(tái)播放音樂(lè)。 - START_REDELIVER_INTENT
redeliver是“再交付”的意思。也就是在交付intent的意思。這是基于START_STICKY啟動(dòng)方式的一種加強(qiáng),使用這種方式啟動(dòng)的Service會(huì)保留intent的值。
使用場(chǎng)景:需要重啟,需要保留intent。
入?yún)?/h6>
intent
這個(gè)intent就是剛才startService(intent)中的intent。在創(chuàng)建啟動(dòng)的服務(wù)所在的組件(這里是Activity)中如果需要傳遞給Service中數(shù)據(jù),可以將要傳遞的額數(shù)據(jù)放到這個(gè)Intent里面。
-
flags
代表了創(chuàng)建啟動(dòng)Service的方式,是下面三種方式值的一種。這三種方式的值對(duì)應(yīng)著上面的三種啟動(dòng)方式。
- 0
對(duì)應(yīng)START_NO_STICKY啟動(dòng)方式
- START_FLAG_RETRY(0x0002)
對(duì)應(yīng)START_STICKY啟動(dòng)方式
- START_FLAG_REDELIVERY(0x0001)
對(duì)應(yīng)START_REDELIVER_INTENT啟動(dòng)方式
startId
這個(gè)代表著每次創(chuàng)建啟動(dòng)Service的唯一身份ID,每次startService,這個(gè)startId均不相同。(可以從上面的圖“通過(guò)startService創(chuàng)建啟動(dòng)的Service生命周期”看出來(lái))。這個(gè)用于處理多個(gè)onStartCommand請(qǐng)求時(shí),關(guān)閉Service時(shí)使用的。下面會(huì)對(duì)這個(gè)使用詳細(xì)講解的。
總結(jié)onStartCommand方法:
- 這個(gè)方法在第一次創(chuàng)建啟動(dòng)Service的時(shí)候,由于沒(méi)有創(chuàng)建過(guò)Service,所以會(huì)onCreate->onStartCommand。
- 第二次乃至以后再重復(fù)啟動(dòng)Service的時(shí)候,Service已經(jīng)被創(chuàng)建過(guò),無(wú)需再被創(chuàng)建了。故:只回調(diào)了onStartCommand。所以,創(chuàng)建啟動(dòng)Service的時(shí)候,每次啟動(dòng)都會(huì)回調(diào)onStartCommand方法
onDestory
- 服務(wù)停止的時(shí)候會(huì)回調(diào)onDestory方法。
總結(jié)創(chuàng)建啟動(dòng)服務(wù):
- 其他組件可以通過(guò)startService(intent)創(chuàng)建啟動(dòng)服務(wù)。該服務(wù)被啟動(dòng)后,服務(wù)與啟動(dòng)他的組件沒(méi)有任何關(guān)系了。即使該組件被干掉,該Service任然不會(huì)被干掉。
- 這種方式的服務(wù)一旦被啟動(dòng)后,基本不會(huì)被關(guān)閉。除非不足以支持優(yōu)先級(jí)較高的進(jìn)程(前臺(tái)進(jìn)程、可見(jiàn)進(jìn)程)繼續(xù)運(yùn)行,系統(tǒng)才會(huì)停止該服務(wù)。系統(tǒng)一旦停止該服務(wù)后,當(dāng)系統(tǒng)內(nèi)存足夠的時(shí)候會(huì)再次重啟(具體依賴于onStartCommand的返回值)。但是如果手動(dòng)關(guān)閉了該服務(wù),該服務(wù)不會(huì)再次重啟了(我記得好像是Android4.0以前的時(shí)候是會(huì)重啟的,Android4.0以后不會(huì)重啟了,具體哪個(gè)版本忘記了~~~)。
- 可以將要傳遞給Service的數(shù)據(jù)放到Intent里面。服務(wù)被啟動(dòng)后回調(diào)onStartCommand方法,在這個(gè)方法中會(huì)給我們剛才傳遞的Intent,然后就可以為所欲為了。
-
Service默認(rèn)情況下不會(huì)單獨(dú)啟動(dòng)一個(gè)進(jìn)程或者線程,默認(rèn)情況下與主線程所在的進(jìn)程在同一個(gè)進(jìn)程中。所以,不能在Service中進(jìn)行耗時(shí)操作,否則會(huì)引起ANR(響應(yīng)時(shí)間超過(guò)5s)。鑒于這種情況,強(qiáng)烈建議在服務(wù)啟動(dòng)后,在onStartCommand方法中開(kāi)啟一個(gè)子線程去做具體的操作。相應(yīng)的操作做完后去關(guān)閉服務(wù),以免于多余的消耗系統(tǒng)資源給用戶帶來(lái)不適感。
intent
這個(gè)intent就是剛才startService(intent)中的intent。在創(chuàng)建啟動(dòng)的服務(wù)所在的組件(這里是Activity)中如果需要傳遞給Service中數(shù)據(jù),可以將要傳遞的額數(shù)據(jù)放到這個(gè)Intent里面。
flags
代表了創(chuàng)建啟動(dòng)Service的方式,是下面三種方式值的一種。這三種方式的值對(duì)應(yīng)著上面的三種啟動(dòng)方式。
- 0
對(duì)應(yīng)START_NO_STICKY啟動(dòng)方式 - START_FLAG_RETRY(0x0002)
對(duì)應(yīng)START_STICKY啟動(dòng)方式 - START_FLAG_REDELIVERY(0x0001)
對(duì)應(yīng)START_REDELIVER_INTENT啟動(dòng)方式
startId
這個(gè)代表著每次創(chuàng)建啟動(dòng)Service的唯一身份ID,每次startService,這個(gè)startId均不相同。(可以從上面的圖“通過(guò)startService創(chuàng)建啟動(dòng)的Service生命周期”看出來(lái))。這個(gè)用于處理多個(gè)onStartCommand請(qǐng)求時(shí),關(guān)閉Service時(shí)使用的。下面會(huì)對(duì)這個(gè)使用詳細(xì)講解的。
總結(jié)onStartCommand方法:
下面就來(lái)講講如何優(yōu)雅的關(guān)閉服務(wù)。
關(guān)閉服務(wù)
關(guān)閉服務(wù)有兩種方法,stopService和stopSelf(),以及stopSelf(int startId)。
stopService
public void stopService(View view){
Intent intent = new Intent();
intent.setClass(this, RemoteService.class);
stopService(intent);//停止RemoteService服務(wù)
}
這種方法適用于第三方組件關(guān)閉該服務(wù)。
stopSelf()
public int onStartCommand(Intent intent, int flags, int startId) {//Service被啟動(dòng)時(shí)回調(diào)
Log.i("hui", "--intent:"+ intent.getStringExtra("sendData")+ "flags="+flags+"---startId="+startId);
new Thread(){
@Override
public void run() {
super.run();
try {
sleep(6000);//6S后干掉自己
stopSelf();//干掉自己
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
return super.onStartCommand(intent, flags, startId);}
上面例子中,在onStartCommand中開(kāi)啟一個(gè)子線程,6s后調(diào)用stopSelf方法干掉自己,停止服務(wù)。
那么問(wèn)題來(lái)了。如果我要進(jìn)行多次的下載圖片,也就是說(shuō)會(huì)多次調(diào)用onStartCommand方法。但是,不能每次下載完一張關(guān)閉一次,然后新的下載重新打開(kāi)再重新下載,再停止服務(wù)在重新啟動(dòng)服務(wù)下載,這是不合理的。
下載完所有圖片后需要關(guān)閉服務(wù)??墒俏覀儾恢烂繌垐D片需要下載圖片多久,不能再這次下載圖片完成后就立即停止服務(wù),而是在關(guān)閉服務(wù)的時(shí)候看看在自己下載圖片的過(guò)程中是否有新的請(qǐng)求過(guò)來(lái),如果沒(méi)有新的下載圖片的請(qǐng)求,那么我們就可以關(guān)閉服務(wù)了,這樣就省了一次停止與啟動(dòng)服務(wù)。
那么就可以這樣做,下載圖片開(kāi)始的時(shí)候,保存一個(gè)TAG標(biāo)記。每次有新的下載請(qǐng)求的時(shí)候就更改TAG標(biāo)記的值。而這個(gè)TAG標(biāo)記是與下載的線程攜帶的。當(dāng)下載圖片的線程結(jié)束后,看看現(xiàn)在的TAG還是不是當(dāng)初的TAG(現(xiàn)在的你還是以前的你么?O(∩_∩)O哈哈~)。如果lastTag != oldTag,說(shuō)明已經(jīng)有新的請(qǐng)求下載的Service,那么我們就不能關(guān)閉服務(wù)。關(guān)閉服務(wù)就由這個(gè)新的線程自己去關(guān)閉,關(guān)閉時(shí)候做同樣的判斷。
如果有一個(gè)子線程它存儲(chǔ)的lastTag == oldTag,說(shuō)明在自己下載的過(guò)程中沒(méi)有新的下載需求。那么這是我就可以停止服務(wù)了。
Google就為我們提供這么一個(gè)方法,stopSelf(startId)。拿下面例子講一講:
stopSelf(int startId)
public int onStartCommand(Intent intent, int flags, final int startId) {//Service被啟動(dòng)時(shí)回調(diào)
Log.i("hui", "--intent:"+ intent.getStringExtra("sendData")+ "flags="+flags+"---startId="+startId);
new Thread(){
@Override
public void run() {
super.run();
try {
sleep(1000 * startId);
stopSelf(startId);//干掉自己
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
return super.onStartCommand(intent, flags, startId);
}
每次一個(gè)onStartCommand請(qǐng)求的時(shí)候,都會(huì)傳過(guò)來(lái)唯一的startId。這個(gè)startId被線程攜帶著的。當(dāng)代碼執(zhí)行到stopSelf的時(shí)候,進(jìn)行r.getLastStartId != startId(最近啟動(dòng)的Service的id跟startId進(jìn)行是否相等判斷)的判斷,如果不等于就return掉,從而不會(huì)停止服務(wù);如果最近啟動(dòng)的一個(gè)Service的id等于startId,說(shuō)明在自己線程運(yùn)行的過(guò)程中沒(méi)有新的onStartCommand請(qǐng)求過(guò)來(lái),這時(shí)就可以停止掉服務(wù)了。
小結(jié)創(chuàng)建啟動(dòng)的Service
- 清單文件注冊(cè),繼承Service(沒(méi)什么說(shuō)的,必須的)
- 純啟動(dòng)Service生命周期onCreate-onStartCommand-onDestory
- 在onStartCommand方法中開(kāi)啟子線程進(jìn)行估計(jì)耗時(shí)的操作
- 子線程結(jié)束后stopSelf關(guān)閉Service(自己關(guān)閉自己)
- 也可通過(guò)其他組件stopService(intent)來(lái)關(guān)閉指定的Service
對(duì)于這些總結(jié)有疑問(wèn)的,堅(jiān)持重新再看一遍,重新捋一下思路。
創(chuàng)建綁定的Service
我們知道創(chuàng)建啟動(dòng)的Service后,Service與啟動(dòng)它的組件不會(huì)有任何關(guān)系了。這時(shí)的Service就像一個(gè)斷了線的風(fēng)箏一樣,不會(huì)受我們的控制了。
需求:如果我們需要開(kāi)發(fā)一個(gè)音樂(lè)播放器,主頁(yè)面有上一首(last)、下一首(next)、播放(play)、暫停(pause)這幾個(gè)基本按鈕。
分析:
- 那么,我們能把播放音樂(lè)的代碼直接放在MainActivity中么?肯定不行的,那么就需要開(kāi)啟一個(gè)子線程運(yùn)行播放音樂(lè)的代碼。由于音樂(lè)播放器即使退到后臺(tái),還是要繼續(xù)播音樂(lè)的,MainActivity退出的時(shí)候,子線程就變成了空進(jìn)程,很容易被殺掉,所以播放音樂(lè)的代碼不能放到MainActivity的子線程中。這里就用到了Service。
- 那么把 play方法放到onStartCommand中?肯定不行,由于創(chuàng)建啟動(dòng)的Service后,Service與啟動(dòng)它的組件不會(huì)有任何關(guān)系,我們拿不到Service的對(duì)象,也就沒(méi)辦法調(diào)用Service中的play等方法了。。。。
如果我們想使用創(chuàng)建啟動(dòng)的Service中的方法,是沒(méi)用辦法的。怎么辦呢?
這時(shí),就可以使用Service的另一種姿勢(shì)了:創(chuàng)建綁定的Service。
那么我們就來(lái)看看如何一步步使用Service中的方法。分兩部分講綁定服務(wù)和解綁服務(wù)。
綁定Service
一共有三步,打開(kāi)冰箱門、把大象塞進(jìn)去、關(guān)不上冰箱門。
- Service中實(shí)現(xiàn)onBind方法:
/** * 綁定的Service必須實(shí)現(xiàn)的方法,返回非null * @param intent * @return */
@Overridepublic IBinder onBind(Intent intent) {
Log.i("hui", "-onBind-intent:"+ intent.getStringExtra("sendData"));
return myBinder;
}
實(shí)現(xiàn)onBind方法有三種解決方案(先使用第一個(gè)方案詳細(xì)講解,再講解后面的兩個(gè)):
1.使用已經(jīng)實(shí)現(xiàn)了IBinder的Binder子類
2.使用Messenger
3.使用AIDL
- MainActivity中創(chuàng)建ServiceConnection的具體實(shí)現(xiàn)
/** * 服務(wù)連接對(duì)象 */
private ServiceConnection serviceConnection = new ServiceConnection() {
/**
* 當(dāng)創(chuàng)建綁定Service與組件成功連接后,此方法會(huì)
* 被調(diào)用。
* @param name Service組件名稱
* @param service Service中返回的IBinder對(duì)象
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("hui", "---------onServiceConnected-----------");
}
/**
* 當(dāng)創(chuàng)建綁定Service與組件意外中斷后,此方法會(huì)被調(diào)用。
* @param name
*/ @Override
public void onServiceDisconnected(ComponentName name) {
Log.d("hui", "---------onServiceConnected-----------");
}
};
- MainActivity中綁定Service
public void bindService(View view){
Intent intent = new Intent();
intent.putExtra("sendData", "通過(guò)bindService的intent保存MainActivity組件中要傳遞的數(shù)據(jù)");
intent.setClass(this, RemoteService.class);
/**
* Intent service, ServiceConnection conn, int flags
** @param service 包裝了要啟動(dòng)的Service及要攜帶的信息
* @param conn
* @param flags 啟動(dòng)Service的發(fā)送,一般為BIND_AUTO_CREATE,表示:如果服務(wù)不存在會(huì)自動(dòng)建
*/
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
分析:使用
bindService(Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags)方法創(chuàng)建綁定的Service。
- ServiceConnection
類似于媒婆,是公公家(MainActivity)和丈母娘家(Service)交流的紐帶,拿到的IBnder就是丈母娘家的閨女。 - onServiceConnected
* 當(dāng)兩家喜結(jié)連理后,也就是當(dāng)創(chuàng)建綁定Service與組件成功連接后,此方法會(huì)
* 被調(diào)用。
* @param name Service組件名稱[丈母娘家(Service)名稱]
* @param service 丈母娘家的閨女,Service中返回的IBinder對(duì)象
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("hui", "---------onServiceConnected-----------");
}
婚姻意外over
public void onServiceDisconnected(ComponentName name) {
Log.d("hui", "---------onServiceConnected-----------");
}
四大組件中,只有Activity、Service、ContentProvider支持綁定Service。
解綁Service
解綁Service十分簡(jiǎn)單,干掉兩家人之間的媒婆就行了。
public void unBindService(View view){
unbindService(serviceConnection);
}
當(dāng)?shù)谝淮吸c(diǎn)擊解綁時(shí),unbindService方法解綁了MainActivity與RemoteService之間的紐扣--serviceConnection,之后MainActivity和RemoteService之間沒(méi)有任何關(guān)系了。所以當(dāng)?shù)诙卧俅谓饨墪r(shí),crash了。但是stopService卻可以多次被調(diào)用。兩者之間是有區(qū)別的。
如何調(diào)用Service中的方法呢?
在onServiceConnected(ComponentName name, IBinder service)方法中,這里的IBnder對(duì)象service就是啟動(dòng)Service的組件(這里是MainActivity)所連接的綁定的Service中的IBinder onBinder(Intent)方法返回的IBinder對(duì)象。既然我們都拿到了Service中的某個(gè)對(duì)象值(這里是返回的service),那么一切就簡(jiǎn)單了。。。
調(diào)用Service中方法思路
- 創(chuàng)建一個(gè)實(shí)現(xiàn)了IBinder接口的對(duì)象并且返回
- 這個(gè)對(duì)象中提供一個(gè)返回Service對(duì)象的方法
- 拿到Service對(duì)象,為所欲為
RemoteService
public class RemoteService extends Service {
private final MyBinder myBinder = new MyBinder();
@Override public void onCreate() {//Service被創(chuàng)建時(shí)回調(diào)
super.onCreate();
Log.i("hui", "------onCreate-----");
}
/**
*
* @param intent 傳遞過(guò)來(lái)的包裝的intent數(shù)據(jù)
* @param flags
* @param startId 啟動(dòng)的Service的唯一標(biāo)記
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, final int startId) {//Service被啟動(dòng)時(shí)回調(diào)
Log.i("hui", "-onStartCommand-intent:"+ intent.getStringExtra("sendData")+ "flags="+flags+"---startId="+startId);
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {//Service被銷毀時(shí)回調(diào)
super.onDestroy();
Log.i("hui", "------onDestroy-----");
}
/**
* 綁定的Service必須實(shí)現(xiàn)的方法,返回非null
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
Log.i("hui", "-onBind-intent:"+ intent.getStringExtra("sendData"));
return myBinder;
}
/**
* Binder是實(shí)現(xiàn)了IBinder接口的對(duì)象
*/
public class MyBinder extends Binder{
/**
* 拿到Service的對(duì)象
* @return
*/
public RemoteService getRemoteService(){
return RemoteService.this;
}
}
public void playMusic() {
Log.i("hui", "--------playMusic------");
}
public void pauseMusic() {
Log.i("hui", "--------pauseMusic------");
}
}
MAinActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void bindService(View view){
Intent intent = new Intent();
intent.putExtra("sendData", "通過(guò)bindService的intent保存MainActivity組件中要傳遞的數(shù)據(jù)"); intent.setClass(this, RemoteService.class);
/**
* Intent service, ServiceConnection conn, int flags
** @param service 包裝了要啟動(dòng)的Service及要攜帶的信息
* @param conn
* @param flags 啟動(dòng)Service的發(fā)送,一般為BIND_AUTO_CREATE,表示:如果服務(wù)不存在會(huì)自動(dòng)創(chuàng)建 */
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
//播放
public void play(View view){
if(remoteService != null){
remoteService.playMusic();
}
}
//暫停
public void pause(View view){
if(remoteService != null){
remoteService.pauseMusic();
}
}
private RemoteService remoteService;
/**
* 服務(wù)連接對(duì)象
*/
private ServiceConnection serviceConnection = new ServiceConnection() {
/**
* 當(dāng)創(chuàng)建綁定Service與組件成功連接后,此方法會(huì)
* 被調(diào)用。
* @param name 組件名稱
* @param service Service中返回的IBinder對(duì)象
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("hui", "---------onServiceConnected-----------");
//將拿到的Service中onBInder返回的對(duì)象也就是MyBInder對(duì)象進(jìn)行強(qiáng)制轉(zhuǎn)換
RemoteService.MyBinder myBInder = (RemoteService.MyBinder) service;
//調(diào)用MyBinder的方法,拿到RemoteService對(duì)象
remoteService = myBInder.getRemoteService();
}
/**
* 當(dāng)創(chuàng)建綁定Service與組件意外中斷后,此方法會(huì)被調(diào)用。
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("hui", "---------onServiceConnected-----------");
}
};
}
分析步驟:
當(dāng)創(chuàng)建綁定Service后,由于RemoteService的onBinder返回非null,故RemoteService可以通過(guò)ServiceConnection與MainActivity成功建立連接,然后回調(diào)ServiceConnection的onServiceConnected方法。
同時(shí),會(huì)把RemoteService的onBinder的返回值MyBinder傳遞給MainActivity。拿到service,也就是MyBinder對(duì)象,進(jìn)行轉(zhuǎn)型,拿到RemoteService中的MyBinder。
然后我們調(diào)用myBInder.getRemoteService()方法拿到RemoteService的對(duì)象,進(jìn)而調(diào)用RemoteService中的方法。
創(chuàng)建綁定服務(wù)的生命周期
在剛才的“拿到Service對(duì)象”的GIF圖中,當(dāng)我退出APP的時(shí)候,MainActivity被銷毀,這時(shí)的RemoteService方法的onDestory方法也被調(diào)用了,說(shuō)明:MainActivity被銷毀的時(shí)候RemoteService也被銷毀了。
所以,創(chuàng)建綁定的Service與其綁定的Service同生共死。也就是說(shuō)創(chuàng)建綁定的Service的存活與否依賴于其綁定的組件,如果該Service所綁定的所有的組件均已被銷毀,那么該Service就被銷毀了。
服務(wù)的混合啟動(dòng)
那么對(duì)于音樂(lè)播放器而言這是萬(wàn)萬(wàn)不可的,我Service不能因?yàn)槟憬M件的銷毀就被干掉(組件被銷毀后 ,播放音樂(lè)的子線程所在進(jìn)程就變成了空進(jìn)程,很容易被干掉),怎么辦?
使用創(chuàng)建啟動(dòng)的Service+創(chuàng)建綁定的Service的混合啟動(dòng)方案{【bindService+startService】}
也就是說(shuō)我們?cè)贛ainActivity上綁定Service后,再次啟動(dòng)Service。這樣,對(duì)于創(chuàng)建啟動(dòng)的Service而言不會(huì)因?yàn)榻M件的銷毀而銷毀,創(chuàng)建綁定的Service后,組件可以與Service交互。
playMusic的方法中開(kāi)啟子線程,模擬播放音樂(lè)
public void playMusic() {
new Thread(){
@Override
public void run() {
super.run();
int i = 0;
while (true){
Log.i("hui", "--------playMusic--i=" + (i++));
try {
sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();}

startService+bindService兩個(gè)知識(shí)點(diǎn):
- 對(duì)于startService+bindService可以做一些實(shí)際的可控制的后臺(tái)操作,也就是可以在Service中做我們指定的操作。使得組件與Service之間有了紐帶關(guān)聯(lián)。
- 生命周期方法中,單獨(dú)的解綁Service不會(huì)銷毀Service,只有解綁加上stopService才會(huì)銷毀Service
- 被銷毀后的Service中雖然任然運(yùn)行著播放音樂(lè)的代碼但是,這個(gè)進(jìn)程已經(jīng)變成了空進(jìn)程,一不留神就被系統(tǒng)干掉了。這與運(yùn)行服務(wù)進(jìn)程是完全不同的。
- Google建議:如果您的服務(wù)僅供本地應(yīng)用使用,不需要跨進(jìn)程工作,則可以實(shí)現(xiàn)自有Binder 類,讓您的客戶端通過(guò)該類直接訪問(wèn)服務(wù)中的公共方法。
上面我們幾乎講解完了綁定Service的整體流程,使用Binder或者子類實(shí)現(xiàn)onBinder方法的,但是這種方法有局限性,不支持跨進(jìn)程通信。那么如果我們要跨進(jìn)程通信,怎么辦呢?這就是開(kāi)頭講的綁定Service的實(shí)現(xiàn)onBinder方法的方案二(使用Messenger)和方案三(使用AIDL)。
跨進(jìn)程知識(shí)補(bǔ)充
一般情況下一個(gè)APP的所有組件運(yùn)行在唯一的一個(gè)進(jìn)程中,除非你指定他運(yùn)行的進(jìn)程(例如使用“android:process”的屬性,通過(guò)這個(gè)屬性,我們可以指定某個(gè)組件運(yùn)行的進(jìn)程。我們可以通過(guò)設(shè)置這個(gè)屬性,讓每個(gè)組件運(yùn)行在它自己的進(jìn)程中,也可以只讓某些組件共享一個(gè)進(jìn)程。),否則都在自己的進(jìn)程中運(yùn)行者的。
兩個(gè)APP是運(yùn)行在不同的進(jìn)程中的,默認(rèn)無(wú)法共享資源的。如果讓兩個(gè)APP之間進(jìn)行通信,就涉及到了跨進(jìn)程通信(IPC)。
使用Messenger跨進(jìn)程通信
總體思想,基于Handler處理方案,利用Handler在Service中創(chuàng)建一個(gè)Messenger對(duì)象,然后在客戶端(這里是ClientMainActivity)拿到這個(gè)Messenger對(duì)象,然后使用這個(gè)Messenger對(duì)象將Message消息發(fā)送給Handler處理。按著這個(gè)理解,這種綁定方案理解起來(lái)就簡(jiǎn)單多了。
在原來(lái)的項(xiàng)目中新建一個(gè)MessengerService
public class MessengerService extends Service {
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("hui", "--MessengerService-----handleMessage-----");
}
};
//利用Handler構(gòu)建一個(gè)Messenger對(duì)象
private Messenger message = new Messenger(handler);
@Override
public IBinder onBind(Intent intent) {
return message.getBinder();//返回Messenger中的binder,客戶端拿到這個(gè)Messenger中的binder可以構(gòu)建出唯一的Messenger。
}
}
//清單文件
<service android:name=".MessengerService" >
<intent-filter>
<action android:name="com.example.asia.remoteservice.MessengerService"/>
</intent-filter>
</service>
創(chuàng)建第三方APP-Client
public class ClientMainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startMessengerService(View view){
Intent intent = new Intent();
//這里注意,MessengerService的清單文件配置action為"com.example.asia.remoteservice.MessengerService"
intent.setAction("com.example.asia.remoteservice.MessengerService");
bindService(intent, conn, BIND_AUTO_CREATE);
}
public void sendMsgService(View view){
if(null != messenger){
Message message = Message.obtain();
try {
//使用messenger,將我們要傳遞的消息放到handler上,
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
private Messenger messenger;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("hui", "----client-----onServiceConnected-----------");
//客戶端拿到這個(gè)binder(這個(gè)是Messenger中的Binder)可以構(gòu)建出唯一的Messenger。
messenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}


與 AIDL 比較
當(dāng)您需要執(zhí)行 IPC 時(shí),為您的接口使用[Messenger] 要比使用 AIDL 實(shí)現(xiàn)它更加簡(jiǎn)單,因?yàn)?[Messenger] 會(huì)將所有服務(wù)調(diào)用排入隊(duì)列,而純粹的 AIDL 接口會(huì)同時(shí)向服務(wù)發(fā)送多個(gè)請(qǐng)求,服務(wù)隨后必須應(yīng)對(duì)多線程處理。
由于對(duì)于大多數(shù)應(yīng)用,服務(wù)不需要執(zhí)行多線程處理,因此使用 Messenger可讓服務(wù)一次處理一個(gè)調(diào)用。如果您的服務(wù)必須執(zhí)行多線程處理,則應(yīng)使用 AIDL 來(lái)定義接口。
---來(lái)自Google文檔
好了,到這里為止。回顧一下知識(shí)點(diǎn),如有疑問(wèn),重新看看:
-
Service兩種使用方式
- 創(chuàng)建啟動(dòng)Service
- 創(chuàng)建綁定服務(wù)
-
創(chuàng)建啟動(dòng)Service
- startService
- stopService
- stopSelf
- onStartCommand
-
創(chuàng)建綁定服務(wù)
- 實(shí)現(xiàn)IBinder,繼承Binder
- 使用Messenger
- 使用AIDL
-
生命周期
- 創(chuàng)建啟動(dòng)Service:onCreate、onStartCommand、onDestory
- 創(chuàng)建綁定Service : onCreate、onBind、onUnbind、onDestory
混合啟動(dòng)
AIDL
這個(gè)單獨(dú)一片講解
對(duì)于以上講解的,如有問(wèn)題,不吝賜教!
END.