Android學(xué)習(xí)的第一步從四大組件開(kāi)始
在四大組件中Activity
和Service
應(yīng)該算的上是一對(duì)親兄弟了韧掩,兩者的使用都比較頻繁而且在使用上亦有很多相似之處(畢竟都是有Context
的人)济似。但是“親兄弟也要明算賬”這對(duì)兄弟一個(gè)負(fù)責(zé)在外面“拋頭露面”巡社,一個(gè)負(fù)責(zé)在后面“默默無(wú)聞”删掀。
“拋頭露面”的那位已經(jīng)介紹完了,現(xiàn)在來(lái)看看久居后臺(tái)的這位。
Service 是一個(gè)可以在后臺(tái)執(zhí)行長(zhǎng)時(shí)間運(yùn)行操作而不提供用戶(hù)界面的應(yīng)用組件饰豺。
服務(wù)可由其他應(yīng)用組件啟動(dòng)齐莲,而且即使用戶(hù)切換到其他應(yīng)用痢站,服務(wù)仍將在后臺(tái)繼續(xù)運(yùn)行。 此外选酗,組件可以綁定到服務(wù)阵难,以與之進(jìn)行交互,甚至是執(zhí)行進(jìn)程間通信 (IPC)星掰。
1.注冊(cè)
既然進(jìn)行注冊(cè)那肯定少不了AndroidManifest.xml
文件多望,畢竟package
中所有暴露的組件都需要在這里聲明。與Activity
相同氢烘,Service
的注冊(cè)就是在 <application/>
節(jié)點(diǎn)下添加一個(gè)<service/>
節(jié)點(diǎn):
<manifest ... >
...
<application ... >
<service android:name=".TestService" />
...
</application>
</manifest>
在注冊(cè)時(shí)只有android:name
屬性是必須的屬性,其他屬性可以根據(jù)自己的情況進(jìn)行添加家厌,這里不額外進(jìn)行介紹了播玖,推薦在開(kāi)發(fā)者文檔進(jìn)行查看。
注意一點(diǎn)點(diǎn):
為了確保應(yīng)用的安全性饭于,請(qǐng)始終使用顯式 Intent 啟動(dòng)或綁定 Service蜀踏,且不要為服務(wù)聲明 Intent 過(guò)濾器。
2.生命周期的前一章(啟動(dòng)/綁定狀態(tài))
因?yàn)?code>service的生命周期和使用service
的處理方式密切相關(guān)掰吕,所以這里插一段說(shuō)明一下潮瓶。
對(duì)于service
的處理方式良瞧,有兩種態(tài)度,一種是 "放養(yǎng)" :任務(wù)給你了,你去干吧基跑,其他的我就不管了,但是讓你停下時(shí)必須停下引瀑;另一種是 "放風(fēng)箏" :任務(wù)給你了驼鹅,但是你要跟著繩子走,如果繩子斷了纽门,你就會(huì)掉下來(lái)薛耻。
啟動(dòng)狀態(tài)
??當(dāng)應(yīng)用組件(如Activity
)通過(guò)調(diào)用startService()
啟動(dòng)服務(wù)時(shí),服務(wù)即處于“啟動(dòng)”狀態(tài)赏陵。一旦啟動(dòng)饼齿,服務(wù)即可在后臺(tái)無(wú)限期運(yùn)行,即使啟動(dòng)服務(wù)的組件已被銷(xiāo)毀也不受影響蝙搔,除非手動(dòng)調(diào)用才能停止服務(wù)缕溉, 已啟動(dòng)的服務(wù)通常是執(zhí)行單一操作,而且不會(huì)將結(jié)果返回給調(diào)用方杂瘸。綁定狀態(tài)
??當(dāng)應(yīng)用組件通過(guò)調(diào)用bindService()
綁定到服務(wù)時(shí)倒淫,服務(wù)即處于“綁定”狀態(tài)。綁定服務(wù)提供了一個(gè)客戶(hù)端-服務(wù)器接口,允許組件與服務(wù)進(jìn)行交互敌土、發(fā)送請(qǐng)求镜硕、獲取結(jié)果,甚至是利用進(jìn)程間通信 (IPC) 跨進(jìn)程執(zhí)行這些操作返干。 僅當(dāng)與另一個(gè)應(yīng)用組件綁定時(shí)兴枯,綁定服務(wù)才會(huì)運(yùn)行。 多個(gè)組件可以同時(shí)綁定到該服務(wù)矩欠,但全部取消綁定后财剖,該服務(wù)即會(huì)被銷(xiāo)毀。
3.生命周期
不一樣的選擇會(huì)有不一樣的人生癌淮,不同的狀態(tài)自然有不同的生命周期躺坟。下面我們看一下Service
這位默默無(wú)聞的人的“人生路線(xiàn)”。
使用且將被銷(xiāo)毀時(shí)乳蓄,系統(tǒng)將調(diào)用此方法咪橙。服務(wù)應(yīng)該實(shí)現(xiàn)此方法來(lái)清理所有資源,如線(xiàn)程虚倒、注冊(cè)的偵聽(tīng)器美侦、接收器等,這是服務(wù)接收的最后一個(gè)調(diào)用魂奥。(就是在這里寫(xiě)遺書(shū)菠剩,分配一下“死”后的“財(cái)產(chǎn)”)
然后就是Service
正式“參與工作”(有效生命周期):
- 第一種是通過(guò) 調(diào)用startService() 開(kāi)啟服務(wù):
-
onStartCommand()
當(dāng)開(kāi)啟者(如 Activity)通過(guò)調(diào)用startService()
請(qǐng)求啟動(dòng)服務(wù)時(shí),系統(tǒng)將調(diào)用此方法耻煤。一旦執(zhí)行此方法具壮,服務(wù)即會(huì)啟動(dòng)并可在后臺(tái)無(wú)限期運(yùn)行(一直運(yùn)行到onDestroy
,與開(kāi)啟者無(wú)關(guān))违霞。 如果自己實(shí)現(xiàn)此方法嘴办,則需要在服務(wù)工作完成后,通過(guò)調(diào)用stopSelf()
或stopService()
來(lái)停止服務(wù)买鸽。
- 第二種是通過(guò)調(diào)用bindService() 開(kāi)啟服務(wù):
-
onBind()
當(dāng)開(kāi)啟者(如 Activity)想通過(guò)調(diào)用bindService()
與服務(wù)綁定時(shí)涧郊,系統(tǒng)將調(diào)用此方法。在此方法的實(shí)現(xiàn)中眼五,必須返回 一個(gè)IBinder
接口的實(shí)現(xiàn)類(lèi)妆艘,供客戶(hù)端用來(lái)與服務(wù)進(jìn)行通信。 -
onUnbind()
當(dāng)開(kāi)啟者(如 Activity)想通過(guò)調(diào)用unbindService()
與服務(wù)解除綁定時(shí)看幼,系統(tǒng)將調(diào)用此方法批旺。Service
解除綁定后將被銷(xiāo)毀。
注意一點(diǎn)點(diǎn):
無(wú)論是啟動(dòng)狀態(tài)還是綁定狀態(tài)诵姜,onBind() 方法必須重寫(xiě)汽煮,但在啟動(dòng)狀態(tài)的情況下直接返回 null。
在綁定狀態(tài)下,無(wú)需實(shí)現(xiàn)onStartCommand() 方法
4.Service啟動(dòng)狀態(tài)
從上面生命周期的圖中暇赤,我們可以看到啟動(dòng)服務(wù)是通過(guò)startService()
啟動(dòng)心例,這會(huì)調(diào)用服務(wù)的onCreate()(僅第一次)和onStartCommand()方法,直到stopService()
停止服務(wù)鞋囊。直到了基本流程了止后,我們來(lái)寫(xiě)一個(gè)啟動(dòng)狀態(tài)的Service:
package com.test.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* Service 啟動(dòng)狀態(tài)
*
* @author ydd
*/
public class StartServiceTest extends Service{
private static final String TAG = "StartServiceTest";
/**
* 服務(wù)綁定時(shí)調(diào)用,且必須要實(shí)現(xiàn)該方法(abstract函數(shù))
* @param intent 接收 bindService()的傳入
* @return 返回IBinder接口的實(shí)現(xiàn)溜腐,在綁定狀態(tài)使用译株,本處為空
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind()");
return null;
}
/**
* 首次創(chuàng)建服務(wù)時(shí)調(diào)用,且super中無(wú)任何實(shí)現(xiàn)
*/
@Override
public void onCreate() {
Log.d(TAG,"onCreate()");
super.onCreate();
}
/**
* 每次通過(guò)startService()方法啟動(dòng)Service時(shí)都會(huì)被回調(diào)挺益。
* @param intent 啟動(dòng)時(shí)startService()傳輸過(guò)來(lái)的數(shù)據(jù)包
* @param flags 表示啟動(dòng)請(qǐng)求時(shí)是否有額外數(shù)據(jù)
* @param startId 指定當(dāng)前服務(wù)的唯一Id
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
/**
* 銷(xiāo)毀服務(wù)時(shí)調(diào)用
*/
@Override
public void onDestroy() {
Log.d(TAG,"onDestroy()");
super.onDestroy();
}
}
服務(wù)寫(xiě)起來(lái)很簡(jiǎn)單Service
中的abstract
方法只有onBind()
,所以這里必須實(shí)現(xiàn)onBind
歉糜,為了看一下完整的生命周期運(yùn)行,這里在幾個(gè)關(guān)鍵的回調(diào)函數(shù)中打印Log矩肩,看一下運(yùn)行情況现恼。下面再實(shí)現(xiàn)一下操作界面:
package com.test.servicetest;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
/**
* 主界面
*
* @author ydd
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
startServiceBtn.setOnClickListener(this);
stopServiceBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent intent = new Intent(this , StartServiceTest.class);
switch (v.getId()){
case R.id.btnStartService:
startService(intent);
break;
case R.id.btnStopService:
stopService(intent);
break;
default:
break;
}
}
}
界面只添加了兩個(gè)按鈕一個(gè)用于啟動(dòng)服務(wù)(調(diào)用startService()
)一個(gè)用于關(guān)閉服務(wù)(調(diào)用stopService()
).代碼都準(zhǔn)備好了,下面我們開(kāi)始進(jìn)行測(cè)試黍檩。
說(shuō)明一下:本次操作為連續(xù)2次啟動(dòng)服務(wù),然后停止服務(wù)始锚。再次開(kāi)啟服務(wù)并停止服務(wù)刽酱。
——————————第一次開(kāi)啟————————————
com.test.servicetest D/StartServiceTest: onCreate()
com.test.servicetest D/StartServiceTest: onStartCommand()
——————————第二次開(kāi)啟————————————
com.test.servicetest D/StartServiceTest: onStartCommand()
——————————關(guān)閉服務(wù)—————————————
com.test.servicetest D/StartServiceTest: onDestroy()
——————————重新開(kāi)啟服務(wù)———————————
com.test.servicetest D/StartServiceTest: onCreate()
com.test.servicetest D/StartServiceTest: onStartCommand()
——————————關(guān)閉服務(wù)—————————————
com.test.servicetest D/StartServiceTest: onDestroy()
第一次啟動(dòng)時(shí)從Log中我們可以清晰的看出第一次啟動(dòng)時(shí)調(diào)用了onCreate()
和onStartCommand()
方法,之后在開(kāi)啟時(shí)瞧捌,因?yàn)?Service 已經(jīng)存在了棵里,就只調(diào)用onStartCommand()
方法。關(guān)閉服務(wù)調(diào)用onDestroy()
方法姐呐,當(dāng)服務(wù)關(guān)閉后殿怜,如果再次啟動(dòng)就會(huì)開(kāi)始新的生命周期。
下面來(lái)點(diǎn)好玩的東西:
從上面可以看出onStartCommand()
是整個(gè)開(kāi)啟服務(wù)的一個(gè)“有趣”點(diǎn)曙砂,這里簡(jiǎn)單介紹一下這個(gè)函數(shù)头谜。
我們先看傳入的參數(shù):
intent:是
startService()
時(shí)傳過(guò)來(lái)的;-
flags:是系統(tǒng)傳入 有如下三種值:
- 直接通過(guò)
startService()
啟動(dòng)時(shí),flags為0鸠澈; -
onStartCommand()
返回為START_STICKY_COMPATIBILITY或者START_STICKY并且服務(wù)異常殺死后由系統(tǒng)啟動(dòng)柱告;flags為START_FLAG_REDELIVERY=1: -
onStartCommand()
返回為START_REDELIVER_INTENT并且服務(wù)異常殺死后由系統(tǒng)啟動(dòng);flags為START_FLAG_REDELIVERY=2:
- 直接通過(guò)
startId:表示此特定啟動(dòng)請(qǐng)求的唯一整數(shù)笑陈。用于唯一表示某次請(qǐng)求际度。大概是
onStartCommand()
的啟動(dòng)次數(shù),第一次通過(guò)startService啟動(dòng)為是1涵妥,不斷startService啟動(dòng)依次累加乖菱,一般配合stopSelf(startId)使用可以看IntentService中使用。
上面出現(xiàn)了幾個(gè)陌生的面孔:START_STICKY_COMPATIBILITY、START_STICKY窒所、START_REDELIVER_INTENT
這幾個(gè)參數(shù)對(duì)應(yīng)了onStartCommand()
的函數(shù)返回值:
-
START_STICKY
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后鹉勒,一段時(shí)間后內(nèi)存再次空閑時(shí),系統(tǒng)將會(huì)嘗試重新創(chuàng)建此Service墩新,一旦創(chuàng)建成功后將回調(diào)onStartCommand方法贸弥,但其中的Intent將是null,除非有掛起的Intent海渊,如pendingintent绵疲,這個(gè)狀態(tài)下比較適用于不執(zhí)行命令、但無(wú)限期運(yùn)行并等待作業(yè)的媒體播放器或類(lèi)似服務(wù)臣疑。 -
START_NOT_STICKY
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后盔憨,即使系統(tǒng)內(nèi)存再次空閑時(shí),系統(tǒng)也不會(huì)嘗試重新創(chuàng)建此Service讯沈。除非程序中再次調(diào)用startService啟動(dòng)此Service郁岩,這是最安全的選項(xiàng),可以避免在不必要時(shí)以及應(yīng)用能夠輕松重啟所有未完成的作業(yè)時(shí)運(yùn)行服務(wù)缺狠。 -
START_REDELIVER_INTENT
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后问慎,則會(huì)重建服務(wù),并通過(guò)傳遞給服務(wù)的最后一個(gè) Intent 調(diào)用 onStartCommand()挤茄,任何掛起 Intent均依次傳遞如叼。與START_STICKY不同的是,其中的傳遞的Intent將是非空穷劈,是最后一次調(diào)用startService中的intent笼恰。這個(gè)值適用于主動(dòng)執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(例如下載文件)的服務(wù)。 -
START_STICKY_COMPATIBILITY
START_STICKY的兼容版本歇终,但不保證服務(wù)被kill后一定能重啟社证。
有點(diǎn)亂,這里梳理一下思路:使用startService()
啟動(dòng)服務(wù)后评凝,在Service中onStartCommand()
被調(diào)用(如果是第一次追葡,會(huì)首先調(diào)用onCreate()
)。我們?cè)?code>onStartCommand()方法中根據(jù)接收傳入的參數(shù)肥哎,并根據(jù)flags和startId確認(rèn)當(dāng)前開(kāi)啟服務(wù)的狀態(tài)辽俗,開(kāi)始處理事務(wù),最后根據(jù)當(dāng)前的需求設(shè)置不同的返回值篡诽,以便程序更好的對(duì)當(dāng)前Service進(jìn)行控制崖飘。
5.Service綁定狀態(tài)
不同于啟動(dòng)狀態(tài)的服務(wù),綁定狀態(tài)的Service不能單獨(dú)存在杈女,需要與其他的組件之間建立聯(lián)系朱浴。從生命周期上看吊圾,只需要通過(guò)onBind()
函數(shù)返回一個(gè)IBinder
接口的實(shí)現(xiàn)類(lèi),用以提供客戶(hù)端用來(lái)與服務(wù)進(jìn)行交互的編程接口翰蠢,再通過(guò)onUnbind()
接觸綁定結(jié)束工作项乒。想起來(lái)總是比實(shí)際簡(jiǎn)單,根據(jù)情況的不同實(shí)際可以通過(guò)三種方法定義接口:
-
擴(kuò)展 Binder 類(lèi)
如果服務(wù)是供您的自有應(yīng)用專(zhuān)用梁沧,并且在與客戶(hù)端相同的進(jìn)程中運(yùn)行(常見(jiàn)情況)檀何,則應(yīng)通過(guò)擴(kuò)展 Binder 類(lèi)并從 onBind() 返回它的一個(gè)實(shí)例來(lái)創(chuàng)建接口⊥⒅В客戶(hù)端收到 Binder 后频鉴,可利用它直接訪(fǎng)問(wèn) Binder 實(shí)現(xiàn)中乃至 Service 中可用的公共方法。
如果服務(wù)只是您的自有應(yīng)用的后臺(tái)工作線(xiàn)程恋拍,則優(yōu)先采用這種方法垛孔。 不以這種方式創(chuàng)建接口的唯一原因是,您的服務(wù)被其他應(yīng)用或不同的進(jìn)程占用施敢。 -
使用 Messenger
如需讓接口跨不同的進(jìn)程工作周荐,則可使用 Messenger 為服務(wù)創(chuàng)建接口。服務(wù)可以這種方式定義對(duì)應(yīng)于不同類(lèi)型Message 對(duì)象的 Handler僵娃。此 Handler 是 Messenger 的基礎(chǔ)概作,后者隨后可與客戶(hù)端分享一個(gè) IBinder,從而讓客戶(hù)端能利用 Message 對(duì)象向服務(wù)發(fā)送命令默怨。此外仆嗦,客戶(hù)端還可定義自有 Messenger,以便服務(wù)回傳消息先壕。
這是執(zhí)行進(jìn)程間通信 (IPC) 的最簡(jiǎn)單方法,因?yàn)?Messenger 會(huì)在單一線(xiàn)程中創(chuàng)建包含所有請(qǐng)求的隊(duì)列谆甜,這樣您就不必對(duì)服務(wù)進(jìn)行線(xiàn)程安全設(shè)計(jì)垃僚。 -
使用 AIDL
AIDL(Android 接口定義語(yǔ)言)執(zhí)行所有將對(duì)象分解成原語(yǔ)的工作,操作系統(tǒng)可以識(shí)別這些原語(yǔ)并將它們編組到各進(jìn)程中规辱,以執(zhí)行 IPC谆棺。 之前采用 Messenger 的方法實(shí)際上是以 AIDL 作為其底層結(jié)構(gòu)。 如上所述罕袋,Messenger 會(huì)在單一線(xiàn)程中創(chuàng)建包含所有客戶(hù)端請(qǐng)求的隊(duì)列改淑,以便服務(wù)一次接收一個(gè)請(qǐng)求。 不過(guò)浴讯,如果您想讓服務(wù)同時(shí)處理多個(gè)請(qǐng)求朵夏,則可直接使用 AIDL。 在此情況下榆纽,您的服務(wù)必須具備多線(xiàn)程處理能力仰猖,并采用線(xiàn)程安全式設(shè)計(jì)捏肢。
如需直接使用 AIDL,您必須創(chuàng)建一個(gè)定義編程接口的 .aidl 文件饥侵。Android SDK 工具利用該文件生成一個(gè)實(shí)現(xiàn)接口并處理 IPC 的抽象類(lèi)鸵赫,您隨后可在服務(wù)內(nèi)對(duì)其進(jìn)行擴(kuò)展。
5.1 擴(kuò)展 Binder 類(lèi)
本方法無(wú)法跨進(jìn)程工作躏升,所以只適用于僅提供本應(yīng)用服務(wù)的 Service辩棒。通過(guò)實(shí)現(xiàn)自有的 IBinder ,讓客戶(hù)端通過(guò)該接口直接訪(fǎng)問(wèn)服務(wù)中的公共方法膨疏。
擴(kuò)展 Binder 類(lèi)的使用方法大致分為三步:
- 在您的服務(wù)中一睁,創(chuàng)建一個(gè)可滿(mǎn)足下列任一要求的IBinder 實(shí)例:
- 包含客戶(hù)端可調(diào)用的公共方法
- 返回當(dāng)前 Service 實(shí)例,其中包含客戶(hù)端可調(diào)用的公共方法
- 或返回由服務(wù)承載的其他類(lèi)的實(shí)例成肘,其中包含客戶(hù)端可調(diào)用的公共方法
- 從
onBind()
回調(diào)方法返回此 Binder 實(shí)例卖局。 - 在客戶(hù)端中,從
onServiceConnected()
回調(diào)方法接收 Binder實(shí)例双霍,并使用提供的方法調(diào)用綁定服務(wù)砚偶。
有啟動(dòng)狀態(tài)的鋪墊,這里直接上代碼:
package com.test.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* Service 綁定狀態(tài)
*
* @author ydd
*/
public class BindServiceTest extends Service{
private static final String TAG = "BindServiceTest";
private final IBinder mBinder = new TestBinder();
private int count;
private boolean stop;
/**
* 服務(wù)綁定時(shí)調(diào)用洒闸,且必須要實(shí)現(xiàn)該方法(abstract函數(shù))
* @param intent 接收 bindService()的傳入
* @return 返回IBinder接口的實(shí)現(xiàn)
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind()");
return mBinder;
}
/**
* 首次創(chuàng)建服務(wù)時(shí)調(diào)用染坯,且super中無(wú)任何實(shí)現(xiàn)
*/
@Override
public void onCreate() {
Log.d(TAG,"onCreate()");
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
while (!stop) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
}).start();
}
/**
* 解除Service綁定
* @param intent
* @return
*/
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG,"onUnbind()");
return super.onUnbind(intent);
}
/**
* 銷(xiāo)毀服務(wù)時(shí)調(diào)用
*/
@Override
public void onDestroy() {
Log.d(TAG,"onDestroy()");
stop = true;
super.onDestroy();
}
/**
* 獲取服務(wù)時(shí)長(zhǎng)
*/
public int getCount(){
return count;
}
/**
* 定制需要傳出的Binder
*/
public class TestBinder extends Binder {
BindServiceTest getService(){
return BindServiceTest.this;
}
}
}
BindService
的創(chuàng)建和StartService
的最大的區(qū)別是增加了Binder
的實(shí)現(xiàn)以及,在onBind()
函數(shù)中不再返回空值丘逸,更改為一個(gè)Binder
對(duì)象单鹿。另外既然是可以互相通信的,我們就在Service中記錄開(kāi)啟的時(shí)間長(zhǎng)短深纲,并在開(kāi)啟者組件中獲取一下這個(gè)數(shù)據(jù)看一下仲锄。下面看一下控制界面的代碼。
package com.test.servicetest;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
/**
* 主界面
*
* @author ydd
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "BindServiceTest";
private ServiceConnection connection;
private BindServiceTest mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
Button getDataBtn = (Button) findViewById(R.id.btnGetData);
startServiceBtn.setOnClickListener(this);
stopServiceBtn.setOnClickListener(this);
getDataBtn.setOnClickListener(this);
connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG,"——onServiceConnected");
mService = ((BindServiceTest.TestBinder) service).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG,"——onServiceDisconnected");
mService = null;
}
};
}
@Override
public void onClick(View v) {
Intent intent = new Intent(this, BindServiceTest.class);
switch (v.getId()) {
case R.id.btnStartService:
bindService(intent, connection, Service.BIND_AUTO_CREATE);
break;
case R.id.btnStopService:
unbindService(connection);
break;
case R.id.btnGetData:
if (mService != null) {
Log.d(TAG, "服務(wù)當(dāng)前已經(jīng)開(kāi)啟:" + mService.getCount() + "秒");
}else {
Log.d(TAG, "服務(wù)當(dāng)前未開(kāi)啟湃鹊!");
}
break;
default:
break;
}
}
}
根據(jù)啟動(dòng)模式的Service的講解和兩種狀態(tài)的生命周期儒喊,我們應(yīng)該可以知道兩者的開(kāi)啟和關(guān)閉調(diào)用的方法是不同的。在綁定狀態(tài)下币呵,我們使用的是bindService()
和unbindService()
函數(shù)來(lái)控制Service
怀愧。另外這里使用了ServiceConnected
,并實(shí)現(xiàn)了兩個(gè)函數(shù):
-
onServiceConnected(ComponentName name, IBinder service)
該函數(shù)在服務(wù)連接成功后調(diào)用余赢,獲取onBind()
方法返回的IBinder
芯义。這里的service
便是w我們?cè)?code>Service中定義的Binder
實(shí)現(xiàn)類(lèi)對(duì)象。通過(guò)該對(duì)象我們便可以按照我們?cè)O(shè)計(jì)的方法對(duì)Service
進(jìn)行操作妻柒,在上面的例子中我們獲取了BindServiceTest
的實(shí)例扛拨,進(jìn)而調(diào)用服務(wù)端的公共方法。
ComponentName
是一個(gè)封裝了組件(Activity, Service, BroadcastReceiver, or ContentProvider)信息的類(lèi)蛤奢,如包名鬼癣,組件描述等信息陶贼,較少使用該參數(shù)。
-
onServiceDisconnected(ComponentName name)
Android 系統(tǒng)會(huì)在與服務(wù)的連接意外中斷時(shí)(例如當(dāng)服務(wù)崩潰或被終止時(shí))調(diào)用該方法待秃。
注意:當(dāng)客戶(hù)端取消綁定時(shí)拜秧,系統(tǒng)“絕對(duì)不會(huì)”調(diào)用該方法。
看完ServiceConnected
我們?cè)僦匦驴匆幌潞瘮?shù)bindService()
章郁,在參數(shù)中處理Intent和剛剛提到的ServiceConnected
還有一個(gè)int flags
,這個(gè)參數(shù)有時(shí)負(fù)責(zé)控制什么的枉氮?
簡(jiǎn)單的說(shuō)就是對(duì)綁定進(jìn)行控制操作(因?yàn)槲抑皇侵篮?jiǎn)單的),例如上面使用到的參數(shù):
-
BIND_AUTO_CREATE
:就是用來(lái)控制創(chuàng)建Service暖庄。在啟動(dòng)模式中調(diào)用startService()
時(shí)實(shí)際上是通過(guò)啟動(dòng)者來(lái)創(chuàng)建一個(gè)Service
實(shí)例聊替。在綁定狀態(tài)中有些區(qū)別,是向serviceMessenger
發(fā)送一個(gè)請(qǐng)求培廓,由它來(lái)控制是否創(chuàng)建Service
惹悄。
補(bǔ)充一點(diǎn)點(diǎn)
- 當(dāng) flags 不等于 BIND_AUTO_CREATE 時(shí),bindService是不會(huì)自動(dòng)啟動(dòng)service的肩钠。
- 與啟動(dòng)模式與綁定模式的參數(shù)不同:
bindService(Intent service, ServiceConnection conn, int flags)
unbindService(ServiceConnection conn)
5.2 Messenger
終于到Messenger了泣港,一種可以跨進(jìn)程通訊的方式,用起來(lái)稍微比上面的方式復(fù)雜一點(diǎn)价匠,不過(guò)畢竟功能更強(qiáng)(可以跨進(jìn)程通訊)当纱,稍微難一點(diǎn)也是可以理解的。這里想大致介紹一下使用方法:
- 服務(wù)端(Service):
- 創(chuàng)建一個(gè)handler(Service)對(duì)象踩窖,并實(shí)現(xiàn)hanlemessage方法坡氯,用于接收來(lái)自客戶(hù)端的消息,并作處理洋腮;
- 創(chuàng)建一個(gè)messenger(Service)箫柳,,封裝handler(Service);
- 用messenger(Service)的getBinder()方法獲取一個(gè)IBinder對(duì)象啥供,通過(guò)onBind返回給客戶(hù)端滞时;
- 等待客戶(hù)端發(fā)來(lái)的消息;
- 客戶(hù)端(Activity):
- 在activity中綁定服務(wù)滤灯;
- 創(chuàng)建一個(gè)handler(Client)對(duì)象,并實(shí)現(xiàn)hanlemessage方法曼玩,用于接收Service返回的消息鳞骤,并作處理;
- 創(chuàng)建一個(gè)messenger(Client)黍判,,封裝handler(Client)豫尽;
- 創(chuàng)建ServiceConnection并在其中使用 IBinder 將 messenger(Service)實(shí)例化(這里就是Service中的創(chuàng)建一個(gè)messenger,通過(guò)IBinder的方式來(lái)到了客戶(hù)端) 顷帖;
- 創(chuàng)建Message并在其中加載數(shù)據(jù)美旧,注意為了能接收Service的回復(fù)信息渤滞,這里將Message.replyTo設(shè)置為messenger(Client);
- 使用Messenger(Service)向服務(wù)端發(fā)送消息榴嗅;
- 服務(wù)端(Service):
- 接收到Messager(Service)帶回來(lái)的消息妄呕,進(jìn)行處理;
- 創(chuàng)建一個(gè)新的Message嗽测,裝載處理結(jié)果绪励;
- 使用Message.replyTo調(diào)用Messenger(Client)向客戶(hù)端發(fā)送消息;
- 客戶(hù)端(Activity):
- 接收到Messager(Client)帶回來(lái)的消息唠粥,進(jìn)行處理進(jìn)行相關(guān)顯示操作疏魏;
- 整體操作完成,解除綁定晤愧;
補(bǔ)充一點(diǎn)點(diǎn):
Messenger個(gè)人感覺(jué)大莫,理解為收信人比較好。如果A需要向B發(fā)消息官份,A中就需要有一個(gè)B的收信人B在只厘,A將Message交給收信人B,并由收信人B帶給(send)B贯吓。這時(shí)如果A想知道B的回復(fù)懈凹,就派自己的收信人A,跟著(replyTo)收信人B一起去B,等B的處理結(jié)果,由收信人A帶回A悄谐。
說(shuō)起來(lái)有點(diǎn)繞介评,大家看一下代碼,應(yīng)該會(huì)更好理解:
服務(wù)端
package com.test.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.Nullable;
/**
* 綁定服務(wù)使用Messenger
*
* @author ydd
*/
public class MessengerService extends Service {
private static final int SIGN = 1;
private final Messenger mMessenger = new Messenger(new CustomHandler());
private class CustomHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Message message = new Message();
switch (msg.what) {
case SIGN:
message.arg1 = msg.arg1 + msg.arg2;
message.what = SIGN;
try {
msg.replyTo.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
super.handleMessage(msg);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
客戶(hù)端
package com.test.servicetest;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
/**
* 主界面
*
* @author ydd
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final int SIGN = 1;
private ServiceConnection connection;
private boolean mBound;
private Messenger mService;
private Messenger mMessenger = new Messenger(new MainHandler());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
Button setDataBtn = (Button) findViewById(R.id.btnSetData);
startServiceBtn.setOnClickListener(this);
stopServiceBtn.setOnClickListener(this);
setDataBtn.setOnClickListener(this);
connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
};
}
@Override
public void onClick(View v) {
Intent intent = new Intent(this, MessengerService.class);
switch (v.getId()) {
case R.id.btnStartService:
bindService(intent, connection, Service.BIND_AUTO_CREATE);
break;
case R.id.btnStopService:
unbindService(connection);
break;
case R.id.btnSetData:
if (mBound) {
try {
Message message = new Message();
message.arg1 = 3;
message.arg2 = 4;
message.what = SIGN;
message.replyTo = mMessenger;
mService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Toast.makeText(this,
"當(dāng)前服務(wù)未打開(kāi)爬舰,請(qǐng)開(kāi)啟服務(wù)们陆!", Toast.LENGTH_LONG).show();
}
break;
default:
break;
}
}
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SIGN:
Toast.makeText(MainActivity.this,
"服務(wù)器說(shuō)等于:" + msg.arg1, Toast.LENGTH_LONG).show();
break;
default:
break;
}
super.handleMessage(msg);
}
}
}
客戶(hù)端連接到服務(wù)器后,向服務(wù)器發(fā)送兩個(gè)值“3”“4”情屹,然后服務(wù)器求和計(jì)算回復(fù)“7”坪仇,服務(wù)器接收到回復(fù)消息,并顯示在界面垃你。
補(bǔ)充一點(diǎn)點(diǎn)
想深入了解Messager的話(huà)推薦《Android 基于Message的進(jìn)程間通信 Messenger完全解析》鴻洋大神的文章
6.注意事項(xiàng)
6.1同時(shí)啟動(dòng)和綁定
前面介紹了Service的啟動(dòng)狀態(tài)和綁定狀態(tài)椅文,應(yīng)該還記得在Service中onBind()
是必須實(shí)現(xiàn)的方法,那是不是就意味著惜颇,在啟動(dòng)狀態(tài)的Service中皆刺,onBind()
不返回null,就可以啟動(dòng)綁定模式凌摄?同理羡蛾,在綁定狀態(tài)的Service中,是不是也可以進(jìn)入啟動(dòng)狀態(tài)锨亏?
答案是肯定的痴怨。
需要注意一點(diǎn)的時(shí)忙干,只要Service進(jìn)入過(guò)啟動(dòng)狀態(tài)(調(diào)用startService()
),服務(wù)將在后臺(tái)運(yùn)行,直到有Context調(diào)用了stopService()或是服務(wù)本身調(diào)用了stopSelf()方法抑或內(nèi)存不足時(shí)才會(huì)銷(xiāo)毀服務(wù)浪藻。
6.2生命周期控制
因?yàn)镾ervice本身沒(méi)有界面空著捐迫,為了把控Service的生命周期,通常情況下需要在客戶(hù)端(例如Activity)生命周期中于合適的時(shí)間開(kāi)啟(綁定)和關(guān)閉(解綁)Service珠移。需要注意的是切勿在 Activity 的 onResume() 和 onPause() 期間綁定和取消綁定弓乙,因?yàn)槊恳淮紊芷谵D(zhuǎn)換都會(huì)發(fā)生這些回調(diào),這樣反復(fù)綁定與解綁是不合理的钧惧。
6.3使用服務(wù)還是線(xiàn)程暇韧?
簡(jiǎn)單地說(shuō),服務(wù)是一種即使用戶(hù)未與應(yīng)用交互也可在后臺(tái)運(yùn)行的組件浓瞪。 因此懈玻,您應(yīng)僅在必要時(shí)才創(chuàng)建服務(wù)。
如需在主線(xiàn)程外部執(zhí)行工作乾颁,不過(guò)只是在用戶(hù)正在與應(yīng)用交互時(shí)才有此需要涂乌,則應(yīng)創(chuàng)建新線(xiàn)程而非服務(wù)。 例如英岭,如果您只是想在 Activity 運(yùn)行的同時(shí)播放一些音樂(lè)湾盒,則可在 onCreate() 中創(chuàng)建線(xiàn)程,在 onStart() 中啟動(dòng)線(xiàn)程诅妹,然后在 onStop() 中停止線(xiàn)程罚勾。您還可以考慮使用 AsyncTask 或 HandlerThread,而非傳統(tǒng)的 Thread 類(lèi)吭狡。如需了解有關(guān)線(xiàn)程的詳細(xì)信息尖殃,請(qǐng)參閱進(jìn)程和線(xiàn)程文檔。
請(qǐng)記住划煮,如果您確實(shí)要使用服務(wù)送丰,則默認(rèn)情況下,它仍會(huì)在應(yīng)用的主線(xiàn)程中運(yùn)行弛秋,因此器躏,如果服務(wù)執(zhí)行的是密集型或阻止性操作,則您仍應(yīng)在服務(wù)內(nèi)創(chuàng)建新線(xiàn)程蟹略⊙#——《Android Developers Doc》