abstract: Service 是Android 中的(四大)組件之一。服務(wù)是沒(méi)有界面的組件,運(yùn)行在后臺(tái),服務(wù)是運(yùn)行在當(dāng)前應(yīng)用程序進(jìn)程里。如果有耗時(shí)的操作,不想有界面、而且還不想程序退出就停止運(yùn)行的邏輯,放在服務(wù)里。要注意的是,服務(wù)也是運(yùn)行在主線(xiàn)程中,如果有耗時(shí)操作,要放在子線(xiàn)程里,如果服務(wù)被系統(tǒng)殺死了,會(huì)默認(rèn)重啟。另外,組件也可以通過(guò)綁定的形式跟一個(gè)Service進(jìn)行交
Service 是Android 中的(四大)組件之一。服務(wù)是沒(méi)有界面的組件,運(yùn)行在后臺(tái),服務(wù)是運(yùn)行在當(dāng)前應(yīng)用程序進(jìn)程里。如果有耗時(shí)的操作,不想有界面、而且還不想程序退出就停止運(yùn)行的邏輯,放在服務(wù)里。要注意的是,服務(wù)也是運(yùn)行在主線(xiàn)程中,如果有耗時(shí)操作,要放在子線(xiàn)程里,如果服務(wù)被系統(tǒng)殺死了,會(huì)默認(rèn)重啟。另外,組件也可以通過(guò)綁定的形式跟一個(gè)Service進(jìn)行交互,甚至完成進(jìn)程間通信。比如:Service 可以在后臺(tái)處理網(wǎng)絡(luò)傳輸、播放音樂(lè)、進(jìn)行I/O 流的讀寫(xiě)或者跟內(nèi)容提供者進(jìn)行交互。startService()和bindService()是開(kāi)啟服務(wù)的兩種方式,startService()不能調(diào)用服務(wù)里的方法,不可以與服務(wù)進(jìn)行通信,服務(wù)一旦開(kāi)啟,會(huì)長(zhǎng)時(shí)間在后臺(tái)運(yùn)行,與開(kāi)啟在不再有關(guān)系, 開(kāi)啟在退出了,服務(wù)還是在運(yùn)行的,而且不能調(diào)用服務(wù)里的方法;bindService()可以間接的調(diào)用服務(wù)里的方法,可以與服務(wù)進(jìn)程通信,服務(wù)開(kāi)啟了是和開(kāi)啟在的生命周期綁定的,如果開(kāi)啟在關(guān)閉了,服務(wù)也就關(guān)閉了, 開(kāi)啟者可以間接的調(diào)用服務(wù)里的方法。如果服務(wù)同時(shí)被開(kāi)啟和綁定,那么服務(wù)就停不掉了,需要解除綁定服務(wù)才能停止服務(wù)。需要服務(wù)長(zhǎng)期在后臺(tái)運(yùn)行,還需要調(diào)用服務(wù)里的方法,用混合方式開(kāi)啟服務(wù),即采用兩種方式,但調(diào)用方式需要嚴(yán)格的順序。首先,用start方式開(kāi)啟服務(wù),目的是保證服務(wù)在后臺(tái)能夠長(zhǎng)時(shí)間運(yùn)行;其次,用bind方式綁定服務(wù),綁定了服務(wù),方便調(diào)用服務(wù)里的方法;然后解綁時(shí),要先用unBind方式解綁,最后才stop方式停止服務(wù)。
以上就是對(duì)服務(wù)的基本描述,接下來(lái)實(shí)現(xiàn)服務(wù)的具體操作。首先,需要明確一下服務(wù)的具體操作步驟。
startService()的方式開(kāi)啟服務(wù)的大體流程如下:
1. 寫(xiě)一個(gè)類(lèi)繼承 Service;
2. 重寫(xiě)onCreate()方法;
3. 在清單文件的下面聲明service,在name標(biāo)簽中寫(xiě)下服務(wù)的包名加類(lèi)名;
bindService()的方式綁定服務(wù)的具體寫(xiě)法:
1. 創(chuàng)建服務(wù)類(lèi), 繼承 Service;
2. 定義一個(gè)接口,暴露對(duì)外提供的方法;
public interface IService{ public void callServiceMethed(); }
3. 在服務(wù)類(lèi)里定義代理對(duì)象,定義一個(gè)方法可以間接的調(diào)用服務(wù)的方法, 這樣寫(xiě)可以防止不想被暴露的方法被別人調(diào)用了,將希望被調(diào)用的方法寫(xiě)在接口中,其他方法不被其他類(lèi)調(diào)用;
private class MyBinder extends Binder implements IService{ public void callServiceMethed(){ 調(diào)用服務(wù)的方法 } ... }
4. 在onBinder方法里返回代理對(duì)象,如果不返回,調(diào)用方拿到的對(duì)象就是空的
public IBinder onBind(Intent intent) { return new MyBinder(); }
5. 創(chuàng)建類(lèi)實(shí)現(xiàn) ServiceConnection,實(shí)現(xiàn)里面的兩個(gè)方法
private class MyConn implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service) { //當(dāng)服務(wù)連接成功時(shí)候調(diào)用 } @Override public void onServiceDisconnected(ComponentName name) { //當(dāng)服務(wù)斷開(kāi)連接時(shí)調(diào)用 } }
6. activity采用綁定的方式開(kāi)啟服務(wù),bindService()方法綁定服務(wù);
7. 調(diào)用代理對(duì)象的方法,間接的調(diào)用了服務(wù)里的方法
接下來(lái)了解一下服務(wù)的生命周期
跟Activity 一樣,Service 也是有生命周期的,不一樣的是Service 的生命周期,從它被創(chuàng)建開(kāi)始,到它被銷(xiāo)毀為止,可以有兩條不同的路徑:標(biāo)準(zhǔn)開(kāi)啟模式和綁定模式。兩種生命周期的可以用下面箭頭表示:
標(biāo)準(zhǔn)開(kāi)啟模式的生命周期:
startService()->onCreate() -> onstartcommand() -> onDestroy()
綁定模式的生命周期:
bindService()->onBind() -> onunBind() -> onDestroy()
被開(kāi)啟的service 通過(guò)其他組件調(diào)用startService()被創(chuàng)建。這種service 可以無(wú)限地運(yùn)行下去,必須調(diào)用stopSelf()方法或者其他組件調(diào)用stopService()方法來(lái)停止它。當(dāng)service 被停止時(shí),系統(tǒng)會(huì)銷(xiāo)毀它。用start方法開(kāi)啟服務(wù),服務(wù)只會(huì)被創(chuàng)建一次,執(zhí)行一次onCreate方法,一旦服務(wù)創(chuàng)建完成,后續(xù)調(diào)用start去開(kāi)啟服務(wù)只會(huì)執(zhí)行onstart和onstartcommand方法,當(dāng)調(diào)用了stop方法,服務(wù)只會(huì)調(diào)用一次onDestroy方法。
被綁定的service 是當(dāng)其他組件模擬一個(gè)客戶(hù)時(shí),調(diào)用bindService()來(lái)創(chuàng)建的??蛻?hù)可以通過(guò)一個(gè)IBinder接口和service 進(jìn)行通信??蛻?hù)可以通過(guò)unbindService()方法來(lái)關(guān)閉這種連接。一個(gè)service 可以同時(shí)和多個(gè)客戶(hù)綁定,當(dāng)多個(gè)客戶(hù)都解除綁定之后,系統(tǒng)會(huì)銷(xiāo)毀service。你可以和一個(gè)已經(jīng)調(diào)用了startService()而被開(kāi)啟的service 進(jìn)行綁定。比如,一個(gè)后臺(tái)音樂(lè)service 可能因調(diào)用startService()方法而被開(kāi)啟了,稍后可能用戶(hù)想要控制播放器或者得到一些當(dāng)前歌曲的信息,可以通過(guò)bindService()將一個(gè)activity 和service 綁定。這種情況下,stopService()或stopSelf()實(shí)際上并不能停止這個(gè)service,除非所有的客戶(hù)都解除綁定。
了解服務(wù)的生命周期是為了更好的理解服務(wù)在整個(gè)工程下的運(yùn)行原理。因此有必要了解一下安卓中進(jìn)程的工作原理。
在Android 中進(jìn)程優(yōu)先級(jí)由高到低,依次分為:前臺(tái)進(jìn)程(Foreground process),可視化進(jìn)程(Visible process),服務(wù)進(jìn)程(Service process),后臺(tái)進(jìn)程(Background process),空進(jìn)程(Empty process)。下面一次給出每一種進(jìn)程的概念和應(yīng)用場(chǎng)景。
前臺(tái)進(jìn)程(Foreground process): 用戶(hù)正在操作的應(yīng)用程序進(jìn)程叫做前臺(tái)進(jìn)程。通常情況下,在任何時(shí)候系統(tǒng)只存在一小部分前臺(tái)進(jìn)程。這些進(jìn)程只會(huì)作為最后的手段才會(huì)被殺死,即當(dāng)內(nèi)存不足以繼續(xù)運(yùn)行他們的時(shí)候。在這個(gè)時(shí)刻,設(shè)備已經(jīng)達(dá)到內(nèi)存分頁(yè)狀態(tài),當(dāng)系統(tǒng)達(dá)到內(nèi)存分頁(yè)狀態(tài)時(shí)只能通過(guò)虛擬地址訪問(wèn)內(nèi)存,可理解為達(dá)到這個(gè)狀態(tài)時(shí)系統(tǒng)已經(jīng)無(wú)法繼續(xù)分配新的內(nèi)存空間即可,因此殺死一些前臺(tái)進(jìn)程,釋放內(nèi)存空間以保證應(yīng)用能夠繼續(xù)響應(yīng)用戶(hù)的交互是必要的手段。
可視化進(jìn)程(Visible process): 用戶(hù)已經(jīng)不能操作這個(gè)應(yīng)用程序了,但是用戶(hù)還能看到應(yīng)用程序界面。一個(gè)可視進(jìn)程被認(rèn)為是極其重要的并且一般不會(huì)被系統(tǒng)殺死,除非為了保證所有的前臺(tái)進(jìn)程去運(yùn)行不得已為之。
服務(wù)進(jìn)程(Service process): 應(yīng)用程序服務(wù)在后臺(tái)運(yùn)行。一個(gè)擁有正在運(yùn)行的Service,并且該Service 是被startService()方法啟動(dòng)起來(lái)的進(jìn)程,并且該進(jìn)程沒(méi)有被歸類(lèi)到前面的兩種(前置進(jìn)程和可視進(jìn)程)類(lèi)型,那么該進(jìn)程就是服務(wù)進(jìn)程。盡管服務(wù)進(jìn)程沒(méi)有與用戶(hù)可見(jiàn)的控件直接綁定,但是這些進(jìn)程干的工作依然是用戶(hù)關(guān)心的(比如在后臺(tái)播放音樂(lè)或者從網(wǎng)絡(luò)上下載數(shù)據(jù)),因此系統(tǒng)保留這些進(jìn)程一直運(yùn)行除非系統(tǒng)沒(méi)有足夠的內(nèi)存去運(yùn)行前臺(tái)進(jìn)程和可視進(jìn)程。
后臺(tái)進(jìn)程(Background process):應(yīng)用程序界面被用戶(hù)最小化。一個(gè)擁有對(duì)用戶(hù)不可見(jiàn)的Activity,該Activity 已經(jīng)被執(zhí)行了onStop()方法進(jìn)程叫做后臺(tái)進(jìn)程。后臺(tái)進(jìn)程對(duì)用戶(hù)體驗(yàn)沒(méi)有直接的影響,并且系統(tǒng)會(huì)在任何需要為前臺(tái)進(jìn)程,可視進(jìn)程,或服務(wù)進(jìn)程申請(qǐng)內(nèi)存的時(shí)候殺死后臺(tái)進(jìn)程。通常系統(tǒng)中運(yùn)行著大量的后臺(tái)進(jìn)程,這些后臺(tái)進(jìn)程保存在一個(gè)LRU(最少最近使用的)列表中,使用LRU 規(guī)則是為了保證讓最近被用戶(hù)使用的Activity 進(jìn)程最后被殺死,就是誰(shuí)最近被使用了,誰(shuí)最后再被殺死。如果一個(gè)Activity 正確實(shí)現(xiàn)了它的生命周期方法,并且保存了它的狀態(tài),通常這個(gè)狀態(tài)是系統(tǒng)自動(dòng)保存的,那么當(dāng)系統(tǒng)殺死它的進(jìn)程的時(shí)候是對(duì)用戶(hù)的體驗(yàn)沒(méi)有看得見(jiàn)的影響的,因?yàn)楫?dāng)用戶(hù)導(dǎo)航到之前的Activity 的時(shí)候,這個(gè)Activity 會(huì)自動(dòng)恢復(fù)之前保存的視圖狀態(tài)。查看Activity 文檔去獲取更多關(guān)于Activity狀態(tài)的保存和恢復(fù)信息。
空進(jìn)程(Empty process): 應(yīng)用程序沒(méi)有任何的activity和service運(yùn)行。不擁有任何系統(tǒng)四大組件的進(jìn)程叫空進(jìn)程。保持空進(jìn)程存活的唯一理由是為了緩存,這樣可以提高下次啟動(dòng)組件的打開(kāi)速度。當(dāng)系統(tǒng)需要維持緩存進(jìn)程和底層內(nèi)核緩存的資源均衡的時(shí)候系統(tǒng)經(jīng)常會(huì)或者隨時(shí)會(huì)殺死該類(lèi)進(jìn)程。
Android 系統(tǒng)有一套內(nèi)存回收機(jī)制,會(huì)根據(jù)優(yōu)先級(jí)進(jìn)行回收。Android 系統(tǒng)會(huì)盡可能的維持程序的進(jìn)程,但是終究還是需要回收一些舊的進(jìn)程節(jié)省內(nèi)存提供給新的或者重要的進(jìn)程使用。進(jìn)程的回收順序是:從低到高,即當(dāng)系統(tǒng)內(nèi)存不夠用時(shí), 會(huì)把空進(jìn)程一個(gè)一個(gè)回收掉,當(dāng)系統(tǒng)回收所有的完空進(jìn)程不夠用時(shí), 繼續(xù)向上回收后臺(tái)進(jìn)程, 依次類(lèi)推。但是當(dāng)回收服務(wù), 可視, 前臺(tái)這三種進(jìn)程時(shí), 系統(tǒng)非必要情況下不會(huì)輕易回收, 如果需要回收掉這三種進(jìn)程, 那么在系統(tǒng)內(nèi)存夠用時(shí), 會(huì)再給重新啟動(dòng)進(jìn)程;但是服務(wù)進(jìn)程如果用戶(hù)手動(dòng)的關(guān)閉服務(wù), 這時(shí)服務(wù)不會(huì)再重啟了。
文章最后給出兩個(gè)案例來(lái)加深對(duì)服務(wù)的理解,一個(gè)是用startService()開(kāi)啟服務(wù),一個(gè)是利用綁定服務(wù)的原理實(shí)現(xiàn)遠(yuǎn)程服務(wù)。
用startService()開(kāi)啟服務(wù)。流程就不再介紹,直接給出相關(guān)的代碼。
開(kāi)啟服務(wù)和停止服務(wù)的java代碼:
import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } /** *開(kāi)啟服務(wù) */ public void start(View view){ Intent intent = new Intent(this,DemoService.class); startService(intent); } /** *停止服務(wù) */ public void stop(View viwe){ Intent intent = new Intent(this,DemoService.class); stopService(intent); } }
startService()方式開(kāi)啟服務(wù)時(shí),服務(wù)的java代碼:
import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.SystemClock; public class DemoService extends Service { private boolean flag; @Override public IBinder onBind(Intent intent) { return null; } /** * 服務(wù)一般用于檢測(cè)設(shè)備 * */ @Override public void onCreate() { super.onCreate(); new Thread() { public void run() { flag = true; while (flag) { System.out.println("檢查是否有設(shè)備插入進(jìn)來(lái)了."); SystemClock.sleep(2000); System.out.println("服務(wù)被創(chuàng)建了,運(yùn)行在:" + Thread.currentThread().getName() + "線(xiàn)程中"); } }; }.start(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { System.out.println("服務(wù)被開(kāi)啟"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); flag = false; System.out.println("服務(wù)被銷(xiāo)毀"); } }
相應(yīng)得布局文件較為簡(jiǎn)單,僅僅需要兩個(gè)按鍵控制服務(wù)的開(kāi)和關(guān),在此也給出布局文件的相關(guān)代碼。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="start" android:text="開(kāi)始服務(wù)" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="stop" android:text="結(jié)束服務(wù)" /> </LinearLayout>
用bindService()綁定服務(wù)的案例,利用服務(wù)實(shí)現(xiàn)遠(yuǎn)程通信。在Android 平臺(tái)中,各個(gè)組件運(yùn)行在自己的進(jìn)程中,他們之間是不能相互訪問(wèn)的,但是在程序之間是不可避免的要傳遞一些對(duì)象,在進(jìn)程之間相互通信。為了實(shí)現(xiàn)進(jìn)程之間的相互通信,Android 采用了一種輕量級(jí)的實(shí)現(xiàn)方式RPC(Remote Procedure Call 遠(yuǎn)程進(jìn)程調(diào)用)來(lái)完成進(jìn)程之間的通信,并且Android 通過(guò)接口定義語(yǔ)言(Android Interface Definition Language ,AIDL)來(lái)生成兩個(gè)進(jìn)程之間相互訪問(wèn)的代碼。如果想讓我們的Service 可以提供遠(yuǎn)程服務(wù),那么就必須定義一個(gè).aidl 文件,該文件使用的是java 語(yǔ)法,類(lèi)似java 的接口。然后將該文件在客戶(hù)端和服務(wù)端的src 目錄下各自保存一份,這樣編譯器就會(huì)根據(jù)aidl 文件自動(dòng)生成一個(gè)java 類(lèi),也就說(shuō)在客戶(hù)端和服務(wù)端都擁有了相同的類(lèi)文件了。
遠(yuǎn)程服務(wù)需要服務(wù)提供者以及服務(wù)調(diào)用者。因此需要兩個(gè)安卓項(xiàng)目。
遠(yuǎn)程服務(wù)提供者的java代碼包括三個(gè)部分:
綁定服務(wù)的JAVA代碼:
import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class remote extends Service { public class myBind extends IService.Stub { @Override public void callMethodInService() throws RemoteException { methodInService(); } } @Override public IBinder onBind(Intent intent) { return new myBind(); } @Override public void onCreate() { System.out.println("服務(wù)被創(chuàng)建"); super.onCreate(); } @Override public void onDestroy() { System.out.println("服務(wù)被銷(xiāo)毀"); super.onDestroy(); } private void methodInService(){ System.out.println("服務(wù)中的方法被調(diào)用"); } }
綁定服務(wù)的主界面,服務(wù)是運(yùn)行在后臺(tái)的不可見(jiàn),因此為了顯示兩個(gè)程序之間傳遞數(shù)據(jù),在此寫(xiě)了一個(gè)主界面。界面的布局可以隨意寫(xiě)。
import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
aidl文件和捷口類(lèi)似:
package com.example.remote; interface IService { void callMethodInService(); }
接下來(lái)是服務(wù)調(diào)用者中的代碼。
import com.example.remote.IService; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.view.View; public class MainActivity extends Activity { private IService iservice; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } /** * 綁定服務(wù) * @param view */ public void bind(View view) { Intent intent = new Intent(); intent.setAction("com.example.remote"); bindService(intent, new myConn(), BIND_AUTO_CREATE); } /** * 調(diào)用遠(yuǎn)程服務(wù)的方法 * @param view */ public void call(View view) { try { iservice.callMethodInService(); } catch (RemoteException e) { e.printStackTrace(); } } private class myConn implements ServiceConnection { //鏈接成功 @Override public void onServiceConnected(ComponentName name, IBinder service) { iservice = IService.Stub.asInterface(service); } //鏈接失敗 @Override public void onServiceDisconnected(ComponentName name) { System.out.println("拒絕鏈接"); } } }
單獨(dú)定義一個(gè)文件夾,文件夾的名字和遠(yuǎn)程服務(wù)的包名一直,里面存放aidl文件:
package com.example.remote; interface IService { void callMethodInService(); }
此外,在服務(wù)調(diào)用者的布局問(wèn)價(jià)中寫(xiě)兩個(gè)按鈕,用來(lái)綁定扶服務(wù)和調(diào)用服務(wù)中的方法。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="bind" android:text="綁定服務(wù)" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="call" android:text="調(diào)用服務(wù)的方法" /> </LinearLayout>