解析Service之你需要瞭解的一些東東

来源:http://www.cnblogs.com/shouce/archive/2016/03/28/5327680.html
-Advertisement-
Play Games

何為Service Service,俗名服務。在Android系統中,Service與Activity就像一個媽生的,不僅長得像,而且行為(生命周期)也有一些類似。對於Activity來說大家肯定不會陌生,開發Android應用中打過交道最多的莫非就是Activity了,所以今天我們藉助Activi ...


何為Service

  Service,俗名服務。在Android系統中,Service與Activity就像一個媽生的,不僅長得像,而且行為(生命周期)也有一些類似。對於Activity來說大家肯定不會陌生,開發Android應用中打過交道最多的莫非就是Activity了,所以今天我們藉助Activity來引入講解Service。Service跟Activity一樣是Android的四大組件之一,需要在AndroidManifest清單文件中進行註冊。Service不像Activity在前臺運行,而且是與之呼應進行後臺運行的服務;如果把Activity當成下載軟體的用戶交互界面,而Service就是那個默默在後臺運行的下載線程,所以Service的應用場景就是那些我們不需要它常駐前臺但需要它一直在後臺工作的時候,例如下載、播放音樂、IM軟體監聽客戶端消息等。

Service的啟動

  啟動Service有兩種方式,不單調且奢華。在組件中我們可以用類似於Activity的啟動方式的startService()來啟動Service,用stopService()來關閉啟動的服務;當然也可以用比較獨特的綁定啟動方式bindService()來啟動Service這個服務,對應的解綁方法自然就是unbindService()。當然這兩種啟動方式的不同也意味著Service有著不一樣的生命周期調用方法,我們先來看看Service啟動的代碼,詳細如下:

定義一個Service類:

複製代碼
 1 public class MyService extends Service {
 2     //onBind為必須實現的抽象方法
 3     @Override
 4     public IBinder onBind(Intent arg0) {
 5         return new MyBinder();
 6     }
 7     @Override
 8     public void onCreate() {
 9         super.onCreate();
10     }
11     @Override
12     public void onDestroy() {
13         super.onDestroy();
14     }
15     @Override
16     public void onRebind(Intent intent) {
17         super.onRebind(intent);
18     }
19     @Override
20     public void onStart(Intent intent, int startId) {
21         super.onStart(intent, startId);
22     }
23     @Override
24     public boolean onUnbind(Intent intent) {
25         return super.onUnbind(intent);
26     }
27     /**
28      * 構造一個MyBinder類,其繼承於Binder,而Binder實現了IBinder的介面
29      */
30     public class MyBinder extends Binder{
31         //返回Service實例的引用
32         public MyService getId(){
33             return MyService.this;
34         }
35     }
36 }
複製代碼

啟動方式為startService:

//啟動一個Service
Intent intent = new Intent(this, MyService.class);
startService(intent);
//停止Service
stopService(intent);

startService()啟動方式的生命周期為:

  context.startService() -> onCreate() -> onStart() -> Service 運行中 -> context.stop() -> onDestroy() -> Service 被關閉

啟動方式為bindService:

需要先構造一個實現ServiceConnection介面的一個連接類

複製代碼
 1 private class MyServiceConnection implements ServiceConnection{
 2   public MyServiceConnection(){}
 3   @Override
 4   public void onServiceConnected(ComponentName arg0, IBinder binder) {
 5     MyService Myservice = ((MyService.MyBinder)binder).getService();
 6     //綁定成功後調用
 7   }
 8   @Override
 9   public void onServiceDisconnected(ComponentName arg0) {
10     //當服務奔潰時調用
11   }
12 }
複製代碼

然後進行綁定啟動Service

//綁定一個Service
Intent intent = new Intent(this, MyService.class);
MyServiceConnection conn = new MyServiceConnection();
bindService(intent, conn, Context.BIND_AUTO_CREATE);
//解綁Service
unbindService(conn);

當我們執行bindService()後,系統將會調用onCreate()和onBinde(),而在onBind()執行完畢後,將會回調ServiceConnection中的onServiceConnected()的方法,同時傳回一個實現了IBinder介面的類(一般會選擇傳回繼承至Binder的子類,因為Binder實現了IBinder的介面),然後我們可以通過某些手段(參考上面代碼)從IBinder中獲取Service的實例,一旦拿到了引用就相當於拿到了控制權,那時你要怎麼玩弄Service還不是你自家兒的事情了。。。

bindService()啟動方式生命周期:

  context.bindService() -> onCreate() -> onBind() -> Service 運行中 -> context.unBindService() -> onUnbind() -> onDestroy() -> Service被關閉

以上兩條生命周期非常正規的執行流程,但是哲學觀告訴我們,有普遍就有特例,所以有一些奇葩的生命流程的存在也是可能的。

情形一:當我們在組件中調用startService()了來啟動Service,但是我們並沒有調用stopService()來銷毀這個服務,那麼我們在再次startService()來啟動這個服務的時候,並沒有調用onCreate()這個回調方法,而是僅僅執行了onStart()。為什麼呢?因為在Android系統,每個服務都被設置成單例模式,無論調用多少次startService()來啟動Service,在系統永遠只會運行一個Service。而onCreate()是Service被創建時被調用的,所以在多次重覆啟動Service不會調用onCreate(),因為此時系統中已經存在Service這個服務了,所以只會調用onStart()。也就是說在多次啟動服務時,onCreate()只會被調用一次,而onStart()可以被調用多次。

情形二:當我們需要把通過startService()啟動的Service掃地出門的時候,調用stopService()就能滿足我們小小的要求,這時候程式一般如我們預料的執行onDestroy()。但是如果我們並沒有祭出stopService()這把寶劍,而是把調用者(如Activity)直接kill掉,那麼會執行onDestroy()這個方法嗎?答案是不會的,因為自從啟動了Service後,該服務就跟調用者撇清關係了,不論調用者生老病死都跟他沒半毛錢關係,除非打出stopService()通過意圖(intent)把服務回收,當然這一切是建立在該Service是通過startService的方式啟動的前提上。

情形三:現在說一下綁定服務(bindService)的情況,同startService的方式一樣,如果多次重覆啟動Service,onCreate()依然只會調用創建Service服務的那個第一次。而對於onBind()則會在每次重覆綁定服務的時候被調用,請示在這裡onBind()最主要的作用就是將Ibinder傳回到綁定者,借用其建立綁定者與Service的聯繫。而如果有多個綁定的存在,那麼執行unbindService()僅僅只會觸發onUnbind()而不會觸發onDestroy(),只有最後一個綁定者調用unbindService()才會觸發服務的onDestroy()的調用。還有一個特別的地方就是,如果銷毀綁定者(例如Activity),那麼綁定的這個Service會隨他而去(執行onUnbid->onDestroy)。

關於Service的一些小知識小技巧

1、如果你需要一個在調用者退出後仍然不會被銷毀,但同時需要獲取他的引用,那麼有這麼一個小方法。先startService()啟動服務,然後bindService綁定該服務,此時可以獲取到Service的引用,然後在解綁unbindService,由於先startService所以只會執行onUnbind(),這時由於沒有綁定關係,就算該調用者掛掉了,Service依然運行如舊。

2、Service雖然說是後臺運行,但是實際上說它仍然是運行在主線程,這裡說得運行在主線程是其onCreate、onStart、onBind等生命周期方法運行在主線程,如果這些方法進行諸如下載、讀取大文件等耗時工作,會引起主線程的阻塞;所以我們一般會在Service中另起子線程運行我們需要的業務。

3、Service的啟動、綁定和停止、解綁應該在對應的調用者相應的生命周期中,例如需要Service貫穿整個Activity,我們可以再onCreate中啟動、綁定Service、在onDestroy中停止或解綁Service。如果只是需要在用戶前臺時運行服務,那麼應該在onStart和onStop中進行相應的處理。不建議在onPause和onResume中對於服務進行啟動停止的操作,因為這樣會可能造成不必要的性能消耗,舉個例子,當兩個activity同時需要這個服務,那麼在前個activity的onPause剛結束服務時,下個activity在onResume馬上啟動該服務,造成一些噁心的問題。

4、有人認為為什麼在bindService的時候不把Ibinder返回到調用者那裡,這個是因為啟動服務時非同步的,在調用bindService的時候是無法獲取IBinder並返回的,所以只能在後面通過調用onbind的時候把IBinder扔到onServiceConnected的參數裡面。

5、有些人會碰到onServiceConnected()在bindService後並沒有被調用,請檢查你的onBind()函數,如果返回值為null是不會觸發onServiceConnected()的回調的,所以我們要確保onBind返回的是一個實現了IBinder介面的類。

6、我們可以在onUnbind()方法中返回true,這樣的話,在我們解綁Service後再次綁定該Service,將會調用onRebind()這個不怎麼常見的回調方法,而不會去執行onBind()。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最終的演示如下 這次是用多線程進行圖片的下載與存儲,而且考慮到下載失敗,占點陣圖片的問題(第一張就是下載失敗的圖片) 閑話少說,上代碼吧,因為有一部分和上次的一樣,所以這裡只上傳不一樣的 依舊都是在ViewController.m中 1. 前兩個和前面的一致 operations使用來存儲下載圖片的線 ...
  • 前言 GCD 全稱 Grand Central DisPath NSOperation便是基於GCD的封裝 基礎知識 1.GCD的優勢 (1)為多核的並行運算提出瞭解決方案 (2)GCD會自動利用更多的CPU內核 比和雙核 四核 (3).GCD自動管理線程的生命周期(創建線程 調度任務 銷毀線程) ...
  • 最近在學慣用 Markdown 寫筆記呢,感覺還不錯的樣子,排版挺好看的,再也不用擔心被管理員因為排版問題把我的博客從首頁移除了 !! 1.0 NSOperation 的作用 使用 NSOperation 的目的就是為了讓開發人員不再關心線程 配合使用 NSOperation(任務) 和 NSOpe ...
  • 前言 基礎知識 在一定情況下我們需要用到自動佈局(autolayout) 這樣我們就能使視圖與視圖之間的位置相互關聯起來 橫向:距離父視圖左側100 視圖本身的寬度最小是100 距離父視圖右側是100 豎向:距離父視圖頂部150 視圖本身的高度是40 VFL:Visual Format Lanagu ...
  • 最近在弄一個視頻會議的項目,但今天要說的跟視頻基本沒關係,我們來說一下在一個view中創建一個button,在controller中載入這個view 當button被點擊後將時間響應傳遞給controller,去做其他修改 大致就是這個環境下,我們很快想到用代理,當然可以,這裡不闡述代理的實現過程我 ...
  • 一開始在剛接觸到Alert和ActionSheet的時候,經常傻傻分不清楚,好不容易用習慣了,蘋果又給合併了,好在用起來也不困難,到底哪個好呢?見仁見智吧! 現在稍微介紹一下怎麼用。 1.初始化,一般用這個類方法 最後一個參數是個枚舉類型,有兩個值,分別就是Alert和ActionSheet 2.推 ...
  • 一,代碼。 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSLog(@"現在所在的周:%@",[self get ...
  • 示例:(在storyboard中拖兩個控制項textField、一個UIButton,連線(userNameText、password、login)) @interface ViewController() @property(nonatomic,weak)IBOutlet UITextField * ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...