Android四大組件之Service

来源:https://www.cnblogs.com/xmkh/archive/2019/08/28/11423490.html
-Advertisement-
Play Games

前言 Hi,大家好,上一期我們講瞭如何使用BroadcastReceiver,這一期我們講解Android四大組件之Service相關知識。每天一篇技術乾貨,每天我們一起進步。 耐心專註不僅僅是美德,更是一筆財富。 1.簡介與定義 Service是一個可以在後臺執行長時間運行操作而不提供用戶界面的應 ...


前言

Hi,大家好,上一期我們講瞭如何使用BroadcastReceiver,這一期我們講解Android四大組件之Service相關知識。每天一篇技術乾貨,每天我們一起進步。

耐心專註不僅僅是美德,更是一筆財富。

 

1.簡介與定義

簡介

Service是一個可以在後臺執行長時間運行操作而不提供用戶界面的應用組件。Service可由其他應用組件啟動,而且即使用戶切換到其他應用,Service仍將在後臺繼續運行。 此外,組件可以綁定到Service,以與之進行交互,甚至是執行進程間通信 (IPC)。 例如,Service可以處理網路事務、播放音樂,執行文件 I/O 或與內容提供程式交互,而所有這一切均可在後臺進行。

定義

Service是一個專門在後臺處理長時間任務的Android組件。

1.Service不是一個單獨的進程;

2.Service也不是一個單獨的線程;

3.Service是一個單獨的Android組件,Service運行在主線程上,如果想在Service中處理很占時間的操作時,必須在Service中開線程,以降低Activity沒有響應的風險;

4.Service不提供用戶界面;

它有兩種啟動方式:startServicebindService

 

2.用途

Service有三個常見用途。

1.功能調度:Service接收指定的廣播信息,從而進一步分析和處理事件,最後修改數據、更新界面或者進行其他相關的操作,調度整個應用使其保持正確的狀態。

2.功能提供:Service並不會接收任何的廣播,只接收指定的廣播提供狀態數據,這時需要綁定Service,綁定Service時要管理好Service,一般在Activity的onStop函數里進行解綁unBindService操作。

3.遠程調用:定義AIDL服務,跨進程調用Service,先定義一個遠程調用介面,然後為該介面提供一個IBinder實現類,客戶端獲取了遠程的Service的IBinder對象的代理後,通過該IBinder對象去回調遠程Service的屬性或方法。

 

3.應用場景

如果某個程式組件需要在運行時向用戶呈現界面,或者程式需要與用戶交互,就需要用Activity,否則就應該考慮使用Service。

 

4.Service與Activity對比

相似點:

1.都是單獨的Android組件;

2.都擁有獨立的生命周期;

3.都是Context的派生類,所以可以調用Context類定義的如getResources()getContentResolver()等方法;

4.都擁有自己生命周期回調方法;

不同點:

1.Activity運行於前臺有圖形用戶界面,負責與用戶交互;Service通常位於後臺運行,不需要與用戶交互,也沒有圖形用戶界面。

 

5.Service的生命周期

隨著應用程式啟動Service方式不同,Service的生命周期也略有差異,如下圖:

如果應用程式通過startService()方法來啟動Service,Service的生命周期如上圖左半部分所示。

通過調用startService() 方法啟動Service: 當其他組件調用startService()方法時,Service被創建,並且無限期運行,其自身必須調用stopSelf()方法或者其他組件調用stopService() 方法來停止Service,當Service停止時,系統將其銷毀。

如果應用程式通過bindService()方法來啟動Service,Service的生命周期如上圖右半部分所示。

通過bindService() 方法啟動Service: 當其他組件調用bindService()方法時,Service被創建。接著客戶端通過IBinder介面與Service通信。客戶端通過unbindService() 方法關閉連接。多個客戶端能綁定到同一個Service,並且當他們都解除綁定時,系統將銷毀Service(Service不需要被停止)

特別說明:當Activity調用bindService()綁定一個已通過startService()啟動的Service時,系統只是把Service內部的IBinder對象傳給Activity,並不會把該Service生命周期完全綁定到該Activity,因而當Activity調用unBindService()方法取消與該Service的綁定時,也只是切斷該Activity與Service之間的關聯,並不能停止該Service組件。要停止該Service組件,還需調用stopService()方法。

 

Service的生命周期里,常用的有:

4個手動調用的方法

手動調用方法作用
startService() 啟動服務
stopService() 關閉服務
bindService() 綁定服務
unbindService() 解綁服務

5個自動調用的方法

內部自動調用的方法作用
onCreate() 創建服務
onStartCommand() 開始服務
onDestroy() 銷毀服務
onBind() 綁定服務
onUnbind() 解綁服務

 

6.Service的使用

當我們開始使用Service的時候當然是啟動一個Service了,啟動Service的方法和啟動Activity很類似,都需要藉助Intent來實現,下麵我們就通過一個具體的例子來看一下。

public class MyService extends Service {  
  
    public static final String TAG = "MyService";  
  
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate() executed");  
    }  
  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "onStartCommand() executed");  
        return super.onStartCommand(intent, flags, startId);  
    }  
    
    @Override  
    public IBinder onBind(Intent intent) {  
        return null;  
    }
      
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy() executed");  
    }  
  
}  

要創建一個這樣的Service,你需要讓該類繼承Service類,然後重寫以下方法:

  • onCreate() 1.如果service沒被創建過,調用startService()後會執行onCreate()和onStartCommand()方法; 2.如果service已處於運行中,調用startService()不會執行onCreate()方法,只執行onStartCommand()方法。 也就是說,onCreate()只會在第一次創建service時候調用,多次執行startService()不會重覆調用onCreate(),此方法適合完成一些初始化工作。

  • onStartCommand() 如果多次執行了Context的startService()方法,那麼Service的onStartCommand()方法也會相應的多次調用。onStartCommand()方法很重要,我們在該方法中根據傳入的Intent參數進行實際的操作,比如會在此處創建一個線程用於下載數據或播放音樂等。

  • onBind() Service中的onBind()方法是抽象方法,Service類本身就是抽象類,所以onBind()方法是必須重寫的,即使我們用不到。

  • onDestroy() 在銷毀的時候會執行Service的該方法。

這幾個方法都是回調方法,且在主線程中執行,由Android操作系統在合適的時機調用。

註意:每個Service必須在manifest中 通過<service>來聲明。

<service android:name="com.demo.service.MyService" > 
  ... 
</service>

現在我們通過繼承Service的方式定義了我們自己的MyService類,並且在manifest中聲明瞭我們的MyService,接下來我們應該啟動我們自己的服務。

啟動Service

第一種方式:我們是通過一個Intent對象,並調用startService()方法來啟動MyService

Intent startIntent = new Intent(this, MyService.class);  
startService(startIntent); 

註意:假如我們是通過點擊Button執行上面的代碼,那麼第一次點擊的時候回執行其中的onCreate()onStartCommand()方法,但是當我們第二次點擊的時候就只會執行onStartCommand()方法。

為什麼會這樣呢? 這是由於onCreate()方法只會在Service第一次被創建的時候調用,如果當前Service已經被創建過了(第一次點擊創建了MyService),不管怎樣調用startService()方法,onCreate()方法都不會再執行。

第二種方式:通過bindService啟動Service

bindService啟動服務特點: 1.bindService啟動的服務和調用者之間是典型的client-server模式。調用者是clientservice則是server端。service只有一個,但綁定到service上面的client可以有一個或很多個。這裡所提到的client指的是組件,比如某個Activity

2.client可以通過IBinder介面獲取Service實例,從而實現在client端直接調用Service中的方法以實現靈活交互,這在通過startService()方法啟動中是無法實現的。

3.bindService啟動服務的生命周期與其綁定的client息息相關。當client銷毀時,client會自動與Service解除綁定(client會有ServiceConnectionLeaked異常,但程式不會崩潰)。當然,client也可以明確調用ContextunbindService()方法與Service解除綁定。當沒有任何clientService綁定時,Service會自行銷毀。

啟動了之後,當我們想停止服務的時候該怎麼做呢?

停止Service

第一種方式:我們也是通過一個Intent對象,並調用stopService()方法來停止MyService

Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); 

第二種方式:調用unbindService(conn)方法來停止MyService

unbindService(ServiceConnection conn)
Service和Activity通信

在上面我們高高興興的啟動了Service了,但是細心的你可能發現了,貌似我們僅僅只是啟動了而已,ActivityService並沒有多少"交流",下麵我們就讓ActivityService交流一下。

public class MyService extends Service {
​
    public static final String TAG = "MyService";
​
    private MyBinder mBinder = new MyBinder();
​
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate() executed");
    }
​
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand() executed");
        return super.onStartCommand(intent, flags, startId);
    }
​
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() executed");
    }
​
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
​
    class MyBinder extends Binder {
​
        public void startDownload() {
            Log.d("TAG", "startDownload() executed");
            // 執行具體的下載任務
        }
​
    }
​
}

接下來我們在MainActivity中通過Button來綁定Service和解除綁定

public class MainActivity extends Activity implements OnClickListener {
        
    private Button bindService;
​
    private Button unbindService;
​
    private MyService.MyBinder myBinder;
​
    private ServiceConnection connection = new ServiceConnection() {
​
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
​
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
            myBinder.startDownload();
        }
    };
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }
​
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.bind_service:
            Intent bindIntent = new Intent(this, MyService.class);
            bindService(bindIntent, connection, BIND_AUTO_CREATE);
            break;
        case R.id.unbind_service:
            unbindService(connection);
            break;
        default:
            break;
        }
    }
​
}

可以看到,這裡我們首先創建了一個ServiceConnection的匿名類,在裡面重寫了onServiceConnected()方法和onServiceDisconnected()方法,這兩個方法分別會在ActivityService建立關聯和解除關聯的時候調用。在onServiceConnected()方法中,我們又通過 向下轉型 得到了MyBinder的實例,有了這個實例,ActivityService之間的關係就變得非常緊密了。現在我們可以在Activity中根據具體的場景來調用MyBinder中的任何public方法,即實現了Activity指揮Service乾什麼Service就去乾什麼的功能。

 

當然,現在ActivityService其實還沒關聯起來了呢,這個功能是在Bind Service按鈕的點擊事件里完成的。可以看到,這裡我們仍然是構建出了一個Intent對象,然後調用bindService()方法將ActivityService進行綁定。bindService()方法接收三個參數,第一個參數就是剛剛構建出的Intent對象,第二個參數是前面創建出的ServiceConnection的實例,第三個參數是一個標誌位,這裡傳入BIND_AUTO_CREATE表示在ActivityService建立關聯後自動創建Service,這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行(只有當我們通過 startService()方法請求啟動服務時,調用此方法)。

解除Activity和Service之間的關聯,調用

unbindService(connection);  
關於銷毀Service說明
  • MyService的內部通過stopSelf()方法來銷毀的;

  • 一個Service必須要在既沒有和任何Activity關聯又處理停止狀態的時候才會被銷毀;

  • 在Service的onDestroy()方法里去清理掉那些不再使用的資源,防止在Service被銷毀後還會有一些不再使用的對象仍占用著記憶體;

     

7.IntentService

IntentService是Service的子類,在介紹IntentService之前,先來瞭解使用Service時需要註意的兩個問題

  • Service 不會專門啟動一個線程執行耗時操作,所有的操作都是在主線程中進行的,以至於容易出現ANR,所以需要手動開啟一個子線程;

  • Service 不會自動停止,需要調用stopSelf()方法 或者 是stopService() 方法停止;

使用IntentService不會出現這兩個問題,因為IntentService在開啟Service時,會自動開啟一個新的線程來執行它,另外,當Service運行結束後,會自動停止。

 

8.如何保證服務不會被殺死

第一種方式,返回 START_STICKYSTART_REDELIVER_INTENT

Service因記憶體不足而被系統kill後,一段時間後記憶體再次空閑時,系統將會嘗試重新創建此Service,一旦創建成功後將回調onStartCommand方法,但其中的Intent將是null,除非有掛起的Intent,如pendingintent,這個狀態下比較適用於不執行命令、但無限期運行並等待作業的媒體播放器或類似的服務。

     /**
     * 返回 START_STICKY 或 START_REDELIVER_INTENT
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //return super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

 

第二種方式,提高service的優先權

<service  
    android:name="com.demo.UploadService"  
    android:enabled="true" >  
    <intent-filter android:priority="1000" >  
        <action android:name="com.demo.MyService" />  
    </intent-filter>  
</service>

結語

Service作為Android的四大組件之一,並且項目開發過程中一些場景下經常被使用到,小伙伴們趕緊上手實操,把它靈活的運用到項目中,結合上兩期的Activity和BroadcastReceiver實現有趣的交互吧。

PS:如果還有未看懂的小伙伴,歡迎關註我們的WXGZH:下碼看花,裡面有各種大神回答小伙伴們遇到的問題哦~

 


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

-Advertisement-
Play Games
更多相關文章
  • [學習筆記] 3)配置環境變數:(環境變數中的~1,~2,~3的用法)i)JAVA_HOME:註意C:\Program Files目錄存在空格,變成C:\Progra~1\Java\jdk1.8.0_144。(註意:長於8個字元的文件名和文件夾名,都被簡化成前面6個有效字元,後面~1,有重名的就 ~ ...
  • 1. 簡介 資料庫,現代化的數據存儲存儲手段,是一種特殊的文件,其中存儲著需要的數據。 特點: 持久化存儲 讀寫速度極高 保證數據的有效性 對程式支持性非常好,容易擴展 2. Mysql (1)具有數據完整性: 一個資料庫就是一個完整的業務單元,可以包含多張表,數據被存儲在表中。在表中為了更加準確的 ...
  • 設置主機名 [root@localhost ~]# cat /etc/hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomai... ...
  • 前言 最近在處理一個歷史遺留項目的時候飽受其害,主要表現為偶發性的 SharedPreferences 配置文件數據錯亂,甚至丟失。經過排查發現是多進程的問題。項目中有兩個不同進程,且會頻繁的讀寫 SharedPreferences 文件,所以導致了數據錯亂和丟失。趁此機會,精讀了一遍 Shared ...
  • 本篇概要 小程式開發前的準備 小程式視圖與渲染 小程式事件 註:總的來說,微信小程式的開發代碼寫法,和 HTML、CSS、JavaScript類似很像.jpg 一,微信小程式開發前的準備 於微信小程式官網進行註冊 ==> https://mp.weixin.qq.com/cgi-bin/wx 註冊完 ...
  • void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);然後在創建上下文以pxdata 所指向的記憶體作為上下文數據存貯的容器, 最後 渲染 上下文[self.webView.layer renderInContext:context];這樣就將 we ...
  • 使用SharedPreferences(保存用戶偏好參數)保存數據, 當我們的應用想要保存用戶的一些偏好參數,比如是否自動登陸,是否記住賬號密碼,是否在Wifi下才能 聯網等相關信息,如果使用資料庫的話,顯得有點大材小用了!我們把上面這些配置信息稱為用戶的偏好 設置,就是用戶偏好的設置,而這些配置信 ...
  • 問題 問題 ld: library not found for -lstdc++.6.0.9 clang: error: linker command failed with exit code 1 (use -v to see invocation) 官方解釋 官方解釋 Xcode更新後沒有這個庫 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...