Android應(yīng)用程序開發(fā) 課件 第6章 Android核心組件-Service_第1頁
Android應(yīng)用程序開發(fā) 課件 第6章 Android核心組件-Service_第2頁
Android應(yīng)用程序開發(fā) 課件 第6章 Android核心組件-Service_第3頁
Android應(yīng)用程序開發(fā) 課件 第6章 Android核心組件-Service_第4頁
Android應(yīng)用程序開發(fā) 課件 第6章 Android核心組件-Service_第5頁
已閱讀5頁,還剩67頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

第6章Android核心組件—Service

本章簡介按照工作方式的不同,可將Android應(yīng)用程序劃分為前臺程序和后臺服務(wù)。Activity一旦啟動后便向用戶呈現(xiàn)出某種交互界面,它是一種典型的前臺程序。與前臺程序不同,后臺服務(wù)無需使用窗口或界面與用戶進(jìn)行交互,它通常駐留在系統(tǒng)后臺執(zhí)行某些耗時的任務(wù),或者為其它應(yīng)用程序提供邏輯處理。Android系統(tǒng)為開發(fā)后臺服務(wù)提供了大量的Service組件。本章將在對Service基本概念進(jìn)行介紹的基礎(chǔ)上,重點(diǎn)為讀者講解以啟動方式和綁定方式使用Service的方法、Service的生命周期、IntentService以及Android提供的系統(tǒng)服務(wù)等。本章目錄6.1Service的基本概念6.2Service的功能和特點(diǎn)6.3以啟動方式運(yùn)行Service6.4以綁定方式運(yùn)行Service6.5Service的生命周期本章目錄6.6Service與多線程6.7IntentService6.8Service的優(yōu)先級6.9使用系統(tǒng)提供的Service6.10小結(jié)6.11習(xí)題6.1Service的基本概念Service是Android中為支持應(yīng)用程序后臺運(yùn)行所提供的應(yīng)用組件。它沒有用戶界面,即使將應(yīng)用程序切換到后臺或者另外打開一個新應(yīng)用程序,它仍然能夠持續(xù)運(yùn)行。在AndroidSDK中,Service類從android.content.ContextWrapper類繼承,并向下派生了AbstractInputMethodService、AccessibilityService和IntentService等多個子類。按照類型,Service分為兩種:1.本地服務(wù)(LocalService)2.遠(yuǎn)程服務(wù)(RemoteService)1.本地服務(wù)(LocalService)本地服務(wù)通常在應(yīng)用程序內(nèi)部使用,執(zhí)行需長時間運(yùn)行的耗時操作。例如:應(yīng)用程序數(shù)據(jù)自動更新和音視頻播放服務(wù)等。2.遠(yuǎn)程服務(wù)(RemoteService)遠(yuǎn)程服務(wù)通常用于在應(yīng)用程序之間進(jìn)行通信??蓪⒛承?yīng)用程序的邏輯處理功能封裝成Service,提供給其它應(yīng)用程序使用。例如:遠(yuǎn)程文件下載和在線翻譯等。Service依賴于創(chuàng)建服務(wù)所在的應(yīng)用程序進(jìn)程,當(dāng)應(yīng)用程序進(jìn)程結(jié)束時,所有依賴于該進(jìn)程的服務(wù)也將停止運(yùn)行。此外,創(chuàng)建Service時不會自動開啟執(zhí)行線程,為運(yùn)行Service需創(chuàng)建子線程。應(yīng)用程序可根據(jù)需要,選擇下述任一方式運(yùn)行Service。(1)以啟動方式運(yùn)行Service以啟動方式使用Service的一旦被啟動,它將一直駐留在系統(tǒng)的后臺連續(xù)運(yùn)行。此外,以啟動方式使用的Service不受調(diào)用組件是否停止運(yùn)行的影響。通常,以啟動方式運(yùn)行的Service僅執(zhí)行單一操作,不向調(diào)用者返回執(zhí)行結(jié)果。(2)以綁定方式運(yùn)行Service以綁定方式使用的Service會向外提供一個允許應(yīng)用組件與Service進(jìn)行交互的“客戶端—服務(wù)器”接口。通過該接口,應(yīng)用組件可對Service發(fā)送請求、獲取結(jié)果,甚至還能夠執(zhí)行跨進(jìn)程通信。6.2Service的功能和特點(diǎn)Service的功能類似Linux系統(tǒng)中的守護(hù)進(jìn)程,它能夠長時間運(yùn)行在應(yīng)用程序后臺。某些服務(wù)甚至能夠隨Android系統(tǒng)的啟動開始運(yùn)行,直至系統(tǒng)關(guān)閉。Service不是一個獨(dú)立的進(jìn)程,它通常作為應(yīng)用程序的一部分提供下述功能:1.應(yīng)用程序邏輯功能的調(diào)度者以鬧鐘應(yīng)用程序為例,它在開機(jī)時啟動,讀取用戶設(shè)定好的鬧鐘信息;然后,注冊使用Android系統(tǒng)提供的定時服務(wù),以便能夠準(zhǔn)時地向用戶提醒鬧鐘信息。此外,功能完善的鬧鐘應(yīng)用還需監(jiān)聽某些和時間變化相關(guān)的用戶事件和系統(tǒng)事件;例如:當(dāng)用戶修改完系統(tǒng)時間或時區(qū)發(fā)生改變時,鬧鐘應(yīng)用應(yīng)立刻檢查、處理預(yù)設(shè)的鬧鐘信息。在這種情況下,Service組件的角色便是應(yīng)用程序邏輯功能的調(diào)度者。它收集應(yīng)用程序執(zhí)行過程中可能發(fā)生的各類事件,對這些事件進(jìn)行分析、處理,進(jìn)而更新UI界面、修改應(yīng)用數(shù)據(jù),并能調(diào)度應(yīng)用程序的各個組件使其保持在正確的運(yùn)行狀態(tài)。2.界面組件的功能提供者

界面組件是Android中功能復(fù)用的基本單元。這種功能復(fù)用包含了對整個應(yīng)用界面、應(yīng)用數(shù)據(jù)和應(yīng)用邏輯的復(fù)用。但是對某些應(yīng)用場景而言,無需這種大粒度的功能復(fù)用。例如Android的輸入法框架,它是一種基于Service組件的功能復(fù)用框架。Android設(shè)備中可能安裝多個輸入法應(yīng)用程序,每個應(yīng)用都有不同的用戶界面和詞典數(shù)據(jù)。但是,這些應(yīng)用卻不需要復(fù)用系統(tǒng)默認(rèn)輸入法提供的用戶界面,而僅需系統(tǒng)從后臺提供對控制輸入法的消隱和綁定等操作的支持即可。

綜上所述,Service具有下述特點(diǎn):無用戶界面,不與用戶進(jìn)行交互。長時間駐留運(yùn)行,不占用應(yīng)用程序控制權(quán)??捎糜谶M(jìn)程間通信,支持不同應(yīng)用之間進(jìn)行數(shù)據(jù)交換。不會輕易被系統(tǒng)終止運(yùn)行。6.3

以啟動方式運(yùn)行ServiceService的功能類似Linux系統(tǒng)中的守護(hù)進(jìn)程,它能夠長時間運(yùn)行在應(yīng)用程序后臺。某些服務(wù)甚至能夠隨Android系統(tǒng)的啟動開始運(yùn)行,直至系統(tǒng)關(guān)閉。Service不是一個獨(dú)立的進(jìn)程,它通常作為應(yīng)用程序的一部分提供下述功能:6.3.1創(chuàng)建Service創(chuàng)建以啟動方式運(yùn)行的Service需要三個步驟:自定義一個繼承自Service的子類;在該Service子類中實現(xiàn)或覆蓋父類的onBind()和onStartCommand()等方法;將Service子類注冊到項目目錄下的AndroidManifest.xml清單文件中。下面給出應(yīng)用示例6-1,創(chuàng)建一個以啟動方式運(yùn)行的Service。圖6-1是應(yīng)用程序的主界面,它包括“啟動Service”和“停止Service”兩個按扭。單擊“啟動Service”按鈕將啟動一個計時服務(wù),該服務(wù)運(yùn)行后將循環(huán)累加應(yīng)用程序的運(yùn)行時間,并將累加結(jié)果輸出到AndroidStudio的Logcat窗口。“停止Service”按鈕則用于停止計時服務(wù)。

圖6-1計時Service使用下面的代碼定義一個從android.app.Service類繼承的子類—CountService。publicclassCountServiceextendsService{//結(jié)束服務(wù)標(biāo)識privatebooleanthreadDisable=false;privateintcount=0;@OverridepublicIBinderonBind(Intentintent){//TODO:Returnthecommunicationchanneltotheservice.returnnull;}

@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){while(!threadDisable){try{//休眠1秒鐘Thread.sleep(1000);}catch(InterruptedExceptione){}//計數(shù)器增加1count++;//打印計數(shù)器數(shù)值Log.v("CountService","Countis"+count);}//配置服務(wù)一直在后臺執(zhí)行returnSTART_STICKY;}@OverridepublicvoidonDestroy(){super.onDestroy();threadDisable=true;}}代碼解釋:Service子類的創(chuàng)建方法用粗體字標(biāo)記的代碼段表示??蓮腶ndroid.app.Service類繼承創(chuàng)建自定義Service子類。在Service子類中,實現(xiàn)或覆寫父類提供的onBind()、onStartcommand()和onDestory()等方法。其中:onBind()方法是父類中唯一抽象方法,必須在子類中實現(xiàn),此處未添加任何邏輯代碼,僅僅是返回一個空的IBinder對象;onStartcommand()方法會在每次服務(wù)啟動時被調(diào)用,可將Service的應(yīng)用邏輯添加到方法內(nèi);此處,使用了一段循環(huán)代碼實現(xiàn)了計數(shù)器的日志輸出功能;可為onStartcommand()方法設(shè)置START_STICKY返回值,將Service配置到后臺一直運(yùn)行;onDestory()方法則是在服務(wù)銷毀時被調(diào)用,此處,調(diào)用了父類的同名方法以回收不再使用的系統(tǒng)資源;同時,設(shè)置了結(jié)束循環(huán)標(biāo)識以結(jié)束服務(wù)的連續(xù)運(yùn)行。當(dāng)完成了對Service子類的定義后,接下來需要在項目目錄下的AndroidManifest.xml清單文件中,添加<service>標(biāo)簽,完成對Service對象的注冊。與Activity組件的注冊方法類似,也可在<service>內(nèi)添加<intent-filter>子標(biāo)簽,指明該Service能夠被哪些Intent啟動。在AndroidManifest.xml文件中添加的<service>標(biāo)簽如下:

<!—注冊一個Service組件--><service

android:name=".CountService"android:enabled="true"android:exported="true"></service>代碼解釋:粗體字標(biāo)記的代碼段表示向Android系統(tǒng)注冊類名為CountService的服務(wù)。6.3.2啟動和停止Service當(dāng)服務(wù)創(chuàng)建完成后,可使用Intent對象啟動和停止服務(wù)。在項目視圖中,雙擊打開“MainActivity.java”文件,找到onCreate(…)方法,分別為“啟動Service”和“停止Service”按鈕添加服務(wù)的啟動和停止代碼。protectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);StartServiceBtn=(Button)findViewById(R.id.startSrvbtn);StopServiceBtn=(Button)findViewById(R.id.stopSrvbtn);StartServiceBtn.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){

Intentintent=newIntent(MainActivity.this,CountService.class);//啟動服務(wù)startService(intent);}});StopServiceBtn.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){

Intentintent=newIntent(MainActivity.this,CountService.class);//停止服務(wù)stopService(intent);}});}代碼解釋:服務(wù)的啟動和停止方法用粗體字標(biāo)記的代碼段表示。在“啟動Service”按鈕的點(diǎn)擊事件監(jiān)聽器中,首先構(gòu)造出一個用于啟動服務(wù)的Intent對象;然后,再調(diào)用startService()方法完成了對CountService服務(wù)的啟動。在“停止Service”按鈕的點(diǎn)擊事件監(jiān)聽器中,首先,構(gòu)建出了一個用于停止服務(wù)的Intent對象,然后,再調(diào)用stopService()方法完成了對CountService服務(wù)的停止。編譯并運(yùn)行該應(yīng)用程序,點(diǎn)擊“啟動Service”按鈕,觀察由AndroidStudio的“LogCat”窗口打印出的日志信息,如圖6-2所示。可以看到,服務(wù)啟動后將不間斷地對計時器計數(shù)進(jìn)行累加。圖6-2Service啟動后的打印日志點(diǎn)擊“停止Service”按鈕,模擬器將彈出圖6-3所示的異常提示窗口,提示應(yīng)用程序無法對用戶操作進(jìn)行響應(yīng)。圖6-3停止Service時的異常提示出現(xiàn)該類異常的主要原因是創(chuàng)建CountService時,將其指定到了應(yīng)用程序的UI線程中執(zhí)行,而CountService的執(zhí)行的又是耗時的循環(huán)代碼。因此,CountService的執(zhí)行阻塞了UI線程的執(zhí)行,導(dǎo)致應(yīng)用程序無法響應(yīng)的用戶的界面操作。可進(jìn)一步修改CountService的onStartCommand()方法,將計數(shù)邏輯用一個獨(dú)立線程執(zhí)行。publicintonStartCommand(Intentintent,intflags,intstartId){//創(chuàng)建獨(dú)立的線程執(zhí)行Service

newThread(newRunnable()

{@Overridepublicvoidrun(){while(!threadDisable){try{//休眠1秒鐘Thread.sleep(1000);}catch(InterruptedExceptione){}//計數(shù)器增加1count++;//打印計數(shù)器數(shù)值Log.v("CountService","Countis"+count);}}});returnSTART_STICKY;}6.3.3

Service的運(yùn)行模式當(dāng)以啟動方式運(yùn)行Service時,通常會將服務(wù)的實現(xiàn)邏輯放置在onStartCommand()方法內(nèi)。該方法使用3個返回值標(biāo)識Service的運(yùn)行模式。START_STICKY該返回值標(biāo)識的運(yùn)行模式是,當(dāng)Service因系統(tǒng)內(nèi)存不足而被Kill后,當(dāng)經(jīng)過一段時間后系統(tǒng)內(nèi)存再次空閑時,系統(tǒng)將嘗試重新生成Service對象,并在對象成功生成后回調(diào)onStartCommand()方法。該運(yùn)行模式適用于不執(zhí)行命令、但需長期駐留運(yùn)行的服務(wù)。例如,媒體播放器的后臺音樂播放服務(wù)。(2)START_NOT_STICKY該返回值標(biāo)識的運(yùn)行模式是,當(dāng)Service因系統(tǒng)內(nèi)存不足而被Kill后,即使系統(tǒng)內(nèi)存再次空閑,系統(tǒng)也不會再嘗試重新生成Service。但是,可以通過再次調(diào)用startService()的方法啟動Service。該運(yùn)行模式適用于對未完成的服務(wù)重啟運(yùn)行。例如,網(wǎng)絡(luò)環(huán)境下的文件下載服務(wù)。(3)START_REDELIVER_INTENT該返回值標(biāo)識的運(yùn)行模式是,當(dāng)Service因系統(tǒng)內(nèi)存不足而被Kill后,系統(tǒng)會重新生成Service對象,并在回調(diào)onStartCommand()方法時使用傳遞給服務(wù)的最后一個Intent(最后一次調(diào)用startService()方法啟動服務(wù)時傳遞的Intent對象)。這種運(yùn)行模式確保了傳遞給Service的Intent一定會被處理完成,十分適合處理關(guān)鍵業(yè)務(wù)邏輯。6.4

以啟動方式運(yùn)行Service6.4.1創(chuàng)建Service當(dāng)Service以啟動方式運(yùn)行后,Service與訪問組件之間是無法進(jìn)行方法調(diào)用的。如果要將Service實現(xiàn)的某些底層操作和業(yè)務(wù)邏輯提供給其它組件調(diào)用,可以用綁定方式的運(yùn)行Service。在綁定運(yùn)行方式下,外部訪問組件通過Context.bind()方法綁定Service;Service組件則向訪問組件返回一個實現(xiàn)了IBinder接口的對象,通過該對象即可實現(xiàn)兩者之間的通信。可按下述步驟創(chuàng)建以綁定方式運(yùn)行的Service。將Service提供給外部訪問的功能封裝到接口中;在Service中添加一個內(nèi)部類,它從Bind類繼承并實現(xiàn)步驟1)為Service定義的外部接口;將步驟2)中的內(nèi)部類對象用onBind()方法返回,以提供給外部訪問組件使用。下面將6.3.1中創(chuàng)建的CountService類修改為支持以綁定方式運(yùn)行的Service。首先,為CountService類增加一個外部接口定義。publicinterfaceICountService{publicabstractintgetCount();}代碼解釋:ICountService接口定義出了一個提供返回計數(shù)值的抽象方法,外部訪問組件可使用該方法讀取CountService的計數(shù)值。然后,為CountService添加一個內(nèi)部類,并用onBind()方法返回該內(nèi)部類的一個對象,使得CountService能夠支持以綁定方式使用。publicclassCountServiceextendsService{privatebooleanthreadDisable=false;privateintcount=0;//創(chuàng)建ServiceBinder對象

privateServiceBinderserviceBinder=newServiceBinder();publicclassServiceBinderextendsBinderimplementsICountService{@OverridepublicintgetCount(){returncount;}}@OverridepublicIBinderonBind(Intentintent){returnserviceBinder;}@OverridepublicvoidonCreate(){super.onCreate();newThread(newRunnable(){@Overridepublicvoidrun(){while(!threadDisable){try{//休眠1秒鐘Thread.sleep(1000);}catch(InterruptedExceptione){}//計數(shù)器增加1count++;//打印計數(shù)器數(shù)值Log.v("CountService","Countis"+count);}}}).start();}

@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){returnSTART_STICKY;}}代碼解釋:CountService內(nèi)部子類的創(chuàng)建方法用粗體字標(biāo)記的代碼段表示。ServiceBinder子類從Binder類繼承而來,它實現(xiàn)了前文定義的ICountService接口。onBind()方法返回了ServiceBinder類的一個對象,以便外部訪問組件能夠調(diào)用服務(wù)中提供的getCount()方法。以綁定方式運(yùn)行的Service不會調(diào)用onStartCommand()方法,為啟動執(zhí)行服務(wù)的循環(huán)計數(shù)的線程,可以將其由onStartCommand()方法移入到onCreate()方法中。最大程度的解耦外部訪問組件與Service組件,以綁定方式運(yùn)行Service常常使用隱式Intent啟動。因此,當(dāng)完成了對CountService類的修改后,可在項目目錄下的AndroidManifest.xml清單文件中,找到表示該類對象的<service>標(biāo)簽,為其增加<intent-filter>子標(biāo)簽,配置intent-filter的action屬性,以支持用隱式Intent啟動該Service。在AndroidManifest.xml文件對<service>標(biāo)簽修改如下:

<!—注冊一個Service組件--><serviceandroid:name=".CountService"><intent-filter><actionandroid:name="com.example.bindmodeservicedemo.CountService"/></intent-filter></service>代碼解釋:粗體字標(biāo)記的代碼段表示為CountService的服務(wù)添加的<intent-filter>標(biāo)簽。該標(biāo)簽指明可以使用Action屬性配置相同的Intent啟動該服務(wù)。6.4.2綁定Service當(dāng)以綁定方式運(yùn)行Service時,外部訪問組件將不再使用startService()方法和stopService()方法來啟動或關(guān)閉服務(wù)。Android為外部訪問組件提供了bindService()方法以綁定服務(wù)。如果服務(wù)對象尚未創(chuàng)建,該方法會自動調(diào)用Service.onCreate()方法創(chuàng)建一個新的服務(wù)對象。此外,bindService()方法使用ServiceConnection對象表示外部訪問組件到Service的連接。如果Service綁定成功,可使用ServiceConnection對象的onServiceConnected()方法獲得由Service返回的IBind對象,由該對象可實現(xiàn)外部訪問組件對Service的功能調(diào)用。下面修改應(yīng)用示例6-1中的“MainActivity.java”文件,以綁定方式運(yùn)行Service。publicclassMainActivityextendsAppCompatActivity{privateICountServiceLocalcountService;ButtonStartServiceBtn,StopServiceBtn;//創(chuàng)建ServiceConnection對象

privateServiceConnectionserviceConnection=newServiceConnection(){@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){LocalcountService=(ICountService)service;Log.v("CountService","OnServiceconnected,Countis"+LocalcountService.getCount());}@OverridepublicvoidonServiceDisconnected(ComponentNamename){LocalcountService=null;}};@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);

StartServiceBtn=(Button)findViewById(R.id.startSrvbtn);StopServiceBtn=(Button)findViewById(R.id.stopSrvbtn);StartServiceBtn.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){

//綁定服務(wù)bindService(newIntent("com.example.bindmodeservicedemo.CountService"),serviceConnection,BIND_AUTO_CREATE);}});StopServiceBtn.setOnClickListener(newView.OnClickListener(){@Override

publicvoidonClick(Viewv){//解綁服務(wù)unbindService(serviceConnection);}});}}代碼解釋:第一段粗體字標(biāo)記的代碼段表示了ServiceConnection匿名對象的創(chuàng)建方法。ServiceConnection的匿名類覆蓋了兩個方法:onServiceConnected()和onServiceDisconnected()。使用onServiceConnected()方法可以接收由綁定服務(wù)返回的IBind對象,只需將該對象強(qiáng)制轉(zhuǎn)換成ICountService對象,即可調(diào)用countService提供的getCount()方法了。第二段粗體字標(biāo)記的代碼段表示了countService的綁定方法。通過調(diào)用bindService()方法即可綁定countService。該方法有三個輸入?yún)?shù):第一參數(shù)是一個隱式Intent,它用于啟動countService;第二個參數(shù)是ServerConnection對象,使用該對象可取回由Service返回給外部訪問的接口對象;第三個參數(shù)為常量BIND_AUTO_CREATE,它指示如果沒有countService對象存在時,是否允許創(chuàng)建一個新的對象。第三段粗體字標(biāo)記的代碼段表示了countService的解綁方法。在可使用unbindService()方法解除對countService的綁定,釋放連接占用的系統(tǒng)資源。6.5Service的生命周期與Activity類似,Service也有生命周期。例如,onCreate()、onStartCommand()、onBind()和onDestory()等方法都是Service生命周期內(nèi)的回調(diào)方法。Service有兩種運(yùn)行方式,它們對Service生命周期的影響是不同的。圖6-4左側(cè)子圖給出了以啟動方式運(yùn)行Service時,與生命周期相關(guān)的不同函數(shù)的調(diào)用順序;圖6-4右側(cè)子圖則給出了以綁定方式運(yùn)行Service時,與生命周期相關(guān)的不同函數(shù)的調(diào)用順序。圖6-4Service的生命周期以啟動方式運(yùn)行Service一旦應(yīng)用程序調(diào)用了Context.startService()方法,Service就會被啟動并自動回調(diào)onStartCommand()方法。如果Service從未被創(chuàng)建過,Android將調(diào)用onCreate()方法初始化Service,然后再調(diào)用onStartCommand()執(zhí)行。當(dāng)Service啟動后將一直運(yùn)行下去,直到使用stopService()或stopSelf()方法停止服務(wù)。需要指出的是,無論調(diào)用多少次startService()方法,只需調(diào)用一次stopService()或stopSelf()方法就可停止服務(wù)的運(yùn)行。這是因為,雖然每次調(diào)用startService()方法,onStartCommand()方法都會被再次調(diào)用,但實際上每個服務(wù)都僅有一個運(yùn)行對象。(2)以綁定方式運(yùn)行Service外部應(yīng)用可以調(diào)用Context.bindService()方法,以綁定方式運(yùn)行Service。該方法會獲得Service的持久連接;然后,通過回調(diào)Service內(nèi)的onBind()方法,便可返回由Service提供給外部的IBinder對象;這樣,外部應(yīng)用就可以調(diào)用Service中的方法了。如果Service從未被創(chuàng)建過,Android將調(diào)用onCreate()方法初始化Service,然后再調(diào)用onBind()執(zhí)行。只要外部應(yīng)用和Service之間的連接沒有斷開,Service將一直運(yùn)行下去。只有當(dāng)外部應(yīng)用調(diào)用了unbindService()方法以后,Service才會停止運(yùn)行。相應(yīng)地,onDestory()方法就會被回調(diào)執(zhí)行。還有一種特殊情況,當(dāng)某個Service同時以啟動和綁定方式使用。那么,在這種情況下該如何停止Service呢?根據(jù)Android系統(tǒng)的機(jī)制,一個服務(wù)只要被啟動或綁定了之后,就一直處于運(yùn)行狀態(tài);如果要停止Service的運(yùn)行,就必須將stopService()和unbindService()方法都調(diào)用一次。6.6

Service與多線程Service默認(rèn)運(yùn)行在應(yīng)用程序的UI線程,如果Service執(zhí)行非常耗時的應(yīng)用邏輯,就很容易阻塞UI線程的執(zhí)行,導(dǎo)致應(yīng)用程序無法響應(yīng)用戶操作(見圖6-3)。Android系統(tǒng)提供了多線程編程機(jī)制能夠很好地解決該類問題。當(dāng)使用Service組件時,可為啟動運(yùn)行的Service創(chuàng)建獨(dú)立于UI的子線程。6.6.1線程的基本用法Android采用了與Java語言類似的多線程編程方法。可使用下述方法對線程進(jìn)行定義。1.從Thread父類繼承

可創(chuàng)建一個從Thread類繼承的子類,定義一個線程。在子類中,通過覆寫父類的run()方法,就可以添加線程的運(yùn)行邏輯。例如,可使用下述代碼定義了一個線程。ClassMyThreadextendsThread{@overridePublicvoidrun(){//線程的運(yùn)行邏輯…}}針對上述代碼定義,可使用下述代碼創(chuàng)建一個線程對象,并調(diào)用父類的Thread.start()方法運(yùn)行run()中添加的的邏輯代碼。 newMyThread().start();2.實現(xiàn)Runnable接口Java的單繼承機(jī)制導(dǎo)致了Thread的子類不能再繼承其它類。為降低應(yīng)用組件的耦合度,可采用實現(xiàn)Runnable接口的方法來定義一個線程。例如,可使用下述代碼定義了一個線程。ClassMyThreadimplementsRunnable{@overridePublicvoidrun(){//線程的運(yùn)行邏輯…}}如果使用了上述代碼定義,就需要使用下述代碼啟動線程。MyThread=newMyThread();newThread(MyThread).start();

更常用的線程啟動方法是不直接定義一個實現(xiàn)了Runnable接口的類,而是使用匿名類。如下所示:newThread(newRunnable(){

@overridePublicvoidrun(){//線程的運(yùn)行邏輯…}}).start();6.6.2異步消息處理機(jī)制Android系統(tǒng)不允許子線程直接操作UI控件。而在編程實踐中,很多子線程卻往往需要將執(zhí)行的結(jié)果實時更新到UI控件。針對該問題,Android提供了一套異步消息處理機(jī)制,用以解決子線程中更新UI控件的問題。Android的異步消息處理主要由4個組件構(gòu)成:Message、Handler、MessageQueue和Looper。(1)MessageMessage用于在不同線程間發(fā)送消息,可使用Bundle對象封裝不同類型的消息值。Message類常用的屬性包括Message.what、Message.obj、Message.arg1和Message.arg2。其中:Message.what用于指定用戶自定義的消息代碼,它能夠方便接收端了解消息攜帶的信息;Message.obj用于保存發(fā)送給接收端的Object對象;Message.arg1和Message.arg2則用于存放整型數(shù)據(jù)。(2)HandlerHandler主要用于發(fā)送和接收消息。可以使用Handler.sendMessage()方法發(fā)送消息。發(fā)出的消息經(jīng)過邏輯處理后,最終傳遞到Handler.handleMessage()。(3)MessageQueueMessageQueue即消息隊列,它主要用于保存所有通過Handler發(fā)送的消息。每個線程只有一個MessageQueue對象。(4)LooperLooper負(fù)責(zé)對MessageQueue中的消息進(jìn)行管理。每個線程只有一個Looper對象。當(dāng)使用Looper.loop()方法將Looper對象啟動后,Looper對象便開始監(jiān)視MessageQueue中存放的消息。當(dāng)發(fā)現(xiàn)MessageQueue中存在一條消息時,Looper對象便將該消息取出并傳遞給Handler對象的handleMessage()方法。圖6-5是異步消息處理機(jī)制的處理流程。首先,由應(yīng)用程序的UI線程創(chuàng)建一個Handler對象,并覆寫Handler對象的handleMessage()方法。然后,當(dāng)子線程需要更新UI控件時,會創(chuàng)建一個Message對象,并使用Handler對象將這條消息發(fā)送出去。隨后,該消息就會被Android系統(tǒng)加入到子線程的MessageQueue中等待被處理。Looper對象則始終嘗試從同一子線程的MessageQueue中取出待處理消息,將其再傳遞給Handler對象的handleMessage()方法。因為Handler對象是由UI線程創(chuàng)建的,所以Handler對象的handleMessage()方法中的代碼也會運(yùn)行在UI線程。從而,子線程可以通過這種異步消息處理機(jī)制安全地更新UI控件。圖6-5異步消息處理方式示意圖6.7

IntentServiceService組件是Android中最易被誤用的組件。當(dāng)Service被應(yīng)用程序創(chuàng)建后,它默認(rèn)將在應(yīng)用程序的主線程(UI線程)中運(yùn)行。此外,Service不是以新線程啟動的,因此也不宜直接使用Service處理耗時的任務(wù)。因此,為執(zhí)行耗時任務(wù),通常會在Service中創(chuàng)建新的子線程。但是,當(dāng)應(yīng)用程序沒有任何活動的組件后,Service的宿主進(jìn)程就可能被Android系統(tǒng)隨時終止,這將導(dǎo)致執(zhí)行Service的子線程無法繼續(xù)運(yùn)行。為了可以簡單地創(chuàng)建一個異步的、會自動停止的服務(wù),Android提供了一個從Service派生的子類——IntentService,以解決Service在使用上的上述不足。IntentService使用隊列來管理請求的Intent,每當(dāng)客戶端通過Intent啟動IntentService時,IntentService就會將其加入到隊列中;然后,開啟一個新的worker線程來處理該Intent。IntentService具有下述特點(diǎn):內(nèi)部包含單獨(dú)的worker線程,用以處理接收到的Intent請求。內(nèi)部包含單獨(dú)的worker線程,用以處理Service.onHandleIntent()方法。當(dāng)所有Intent請求處理完畢后,無再需調(diào)用Service.stopself()方法即可停止運(yùn)行。提供了一個返回null值的Service.onBind()方法的默認(rèn)實現(xiàn)。提供了Service.onStartCommand()方法的默認(rèn)實現(xiàn),它會將Intent添加到隊列中。下面,給出應(yīng)用示例6-2,定義一個IntentService的子類,并說明IntentService對象的啟動、停止方法。首先,定義一個從IntentService類繼承的子類——CountIntentService,代碼如下:publicclassCountIntentServiceextendsIntentService{intspan;

publicCountIntentService(){super("CountIntentService");}publicStringgetCurrentTime(){SimpleDateFormatTimeFormat=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");StringCurTime=TimeFormat.format(newjava.util.Date());returnCurTime;}@Override

protectedvoidonHandleIntent(@NullableIntentintent){Log.v("onHandleIntent","服務(wù)啟動時間"+getCurrentTime());Bundleb=intent.getBundleExtra("attachment");span=b.getInt("waitingtime");longendTime=System.currentTimeMillis()+span*1000;Log.v("onHandleIntent","服務(wù)持續(xù)時間"+span);while(System.currentTimeMillis()<endTime){synchronized(this){try{wait(endTime-System.currentTimeMillis());}catch(Exceptione){}}}}@OverridepublicvoidonDestroy(){

Log.v("onDestroy","服務(wù)銷毀時間"+getCurrentTime());

super.onDestroy();}}代碼解釋:第一段粗體字標(biāo)記的代碼段給出了IntentService子類的無參數(shù)構(gòu)造方法。在構(gòu)造方法中可通過向父類的構(gòu)造方法傳遞字符串,來設(shè)置IntentService的worker線程名稱。第二段粗體字標(biāo)記的代碼段給出了onHandleIntent()方法在IntentService子類中的實現(xiàn)代碼。它通過讀取由Intent對象傳遞而來的服務(wù)持續(xù)運(yùn)行時間,并使用wait()方法模擬服務(wù)中執(zhí)行的邏輯代碼。第三段粗體字標(biāo)記的代碼段給出了onDestory()方法在IntentService子類中的實現(xiàn)代碼。除onDestory()方法之外,IntentService類還可以覆寫Service生命周期中的其它回調(diào)方法。但是,覆寫方法應(yīng)在最后一行代碼處添加對父類的同名方法的調(diào)用;否則,IntentService執(zhí)行時將拋出異常。最后,為應(yīng)用程序添加啟動Activity——MainActivity。在項目視圖中,雙擊打開“MainActivity.java”文件,找到onCreate(…)方法,分別為“啟動Service”和“停止Service”按鈕添加服務(wù)的啟動和停止代碼。protectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);

StartServiceBtn=(Button)findViewById(R.id.startSrvbtn);StopServiceBtn=(Button)findViewById(R.id.stopSrvbtn);

StartServiceBtn.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){

Intentintent=newIntent(MainActivity.this,CountIntentService.class);Bundlebd=newBundle();bd.putInt("waitingtime",5);intent.putExtra("attachment",bd);//啟動服務(wù)startService(intent);}});StopServiceBtn.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){

Intentintent=newIntent(MainActivity.this,CountIntentService.class);//停止服務(wù)stopService(intent);}});}代碼解釋:第一段粗體字標(biāo)記的代碼段給出了IntentService的啟動方法。為設(shè)置服務(wù)啟動后的運(yùn)行時間,可為請求Intent附加一個攜帶定時信息的Bundle對象。最后,使用startService()方法即可啟動服務(wù)。第二段粗體字標(biāo)記的代碼段給出了IntentService的停止方法??墒褂胹topService()方法停止IntentService服務(wù)的運(yùn)行。編譯并運(yùn)行應(yīng)用程序。在MainActivity上連續(xù)點(diǎn)擊三次“啟動Service”的按鈕,以模擬多次向CountIntentService發(fā)出請求。圖6-6是由AndroidStudioLogcat窗口記錄的CountIntentService的運(yùn)行日志。可以觀察到,當(dāng)Intent請求到達(dá)CountIntentService后,onHandleIntent()方法將會被調(diào)用執(zhí)行;雖然存在對IntentService的多次調(diào)用,但不會產(chǎn)生多線程同步異常;待所有Intent請求全部處理完畢后,服務(wù)將自動關(guān)閉。圖6-6IntentService運(yùn)行時打印的日志6.8

Service的優(yōu)先級系統(tǒng)為Service分配的優(yōu)先級默認(rèn)是“background”。當(dāng)系統(tǒng)出現(xiàn)資源緊張時,為保證某些優(yōu)先級更高的進(jìn)程執(zhí)行,就有可能回收后臺運(yùn)行的Service。如果希望Service一直保持運(yùn)行狀態(tài),而不被回收,可以考慮將Service的優(yōu)先級提升至“foreground”。前臺Service區(qū)別于后臺的最大特征是,它會有一個正在運(yùn)行的圖標(biāo)顯示在系統(tǒng)的狀態(tài)欄,通過下拉狀態(tài)欄也能夠看到更詳細(xì)的服務(wù)運(yùn)行信息,類似于系統(tǒng)通知。已啟動的Service可以使用startForeground()方法將Service設(shè)置為前臺服務(wù)。反之,也可使用stopForeground()方法將Service切換至后臺服務(wù)。6.9使用系統(tǒng)提供的ServiceAndroid包含了一系列的服務(wù)管理器,用于提供系統(tǒng)服務(wù)。例如,TelephonyManager是一個管理手機(jī)通話狀態(tài)、電話網(wǎng)絡(luò)信息的系統(tǒng)服務(wù)類,它提供了許多形如getXXX()的方法以獲取電話網(wǎng)絡(luò)的相關(guān)信息;SmsManager是一個管理短信的服務(wù)類,該類提供了sendTextMessage()方法用于發(fā)送短信;WindowManager則用于提供與應(yīng)用窗口相關(guān)的服務(wù)。通過Activity.getSystemService()方法可以獲得由Android應(yīng)用程序框架提供的系統(tǒng)服務(wù)。getSystemService()方法只有一個String類型的參數(shù),用于唯一地標(biāo)識Android提供的系統(tǒng)服務(wù)。例如,可以用下述字符串表示不同的系統(tǒng)服務(wù):audio表示可獲得系統(tǒng)的音頻服務(wù),window表示可獲得系統(tǒng)的窗口服務(wù),notification表示可獲得系統(tǒng)的通知服務(wù)。為便于記憶和管理這些系統(tǒng)服務(wù),AndroidSDK在android.content.Context類中定義了應(yīng)用程序可直接引用的字符串常量,例如:publicstaticfinalStringAUDIO_SERVICE="audio";//定義音頻服務(wù)的IDpublicstaticfinalStringWINDOW_SERVICE="window";//定義窗口服務(wù)的IDpublicstaticfinalStringNOTIFICATION_SERVICE="notification";//定義通知服務(wù)的ID下面給出應(yīng)用示例6-3

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論