中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

詳解Android Service 使用時的注意事項

發布時間:2020-10-17 06:11:17 來源:腳本之家 閱讀:155 作者:蘭蘭笑笑生 欄目:移動開發

最近有個項目剛好使用了Service,特別是AIDL遠程服務,經過這次項目對Service有了更好的理解,在這里作個總結。

startService / bindService 混合使用

  1. 每一次調用 startService 都會回調onStartCommand,之后調用了stopService之后就會 destroy Service。即使有多個client啟動服務,那調用一次stopService 就能 destroy Service 。通過這種方式還有一個好處就是Service可以通過調用 stopSelf 主動退出。
  2. 第一次調用bindService 的時候才會回調 onBind,如果有多個client連接服務,在最后一個client調用unbindService時才會回調 onUnbind,并destroy Service。

如果startService / bindService 混合使用 那Service的生命同期會怎樣呢,其實只要記住以上的思路,這種問題很好理解。首先 startService 與 stopService 對應 ,沒有stopService 之前不會 destroy Service , bindService 與 unbindService對應 ,沒有 unbindService 前也不會destroy Service。

為服務添加權限

相信大家做的服務都是公共的,即所有應用都可以調用。但是如果我想我的服務只給特定的應用調用,應該如何設置呢?我們可以給服務添加權限。關于權限,Android系統給權限為了四個類別:

  1. 普通級: 這些權限并不能真正傷害到用戶(比如更換壁紙),當程序需要這些權限是,開發者不需要指定程序會自動賦予這些權限。
  2. 危險級: 這些權限可能會帶來真的傷害(比如打電話,打開網絡鏈接等),如果要使用它們需要開發者在AndroidManifest.xml中聲明對應的權限。
  3. 簽名級: 如果應用使用的是相同的簽名證書時,這些權限會自動授予給聲明或者創建這些權限的程序。設計這一層級權限的目的是方便組件間數據共享。
  4. 簽名/系統級: 和簽名級一樣,例外的是系統鏡像是自動獲取這些權限的,這一層級是專為設備制造商設計的。
<uses-permission android:name="android.permission.custom.XXX"/>

如果我們想讓自己開發的Service只能被特定的Client調用,那就可以添加自定義的權限。比如危險級,我們可以在AndroidManifest.xml中聲明對應的權限,只有應用也設置了這個權限,才能正常啟動服務。

關于AIDL遠程服務

所謂的AIDL遠程服務 就是運行在另一個進程的服務,平時我們調用的服務都運行在主線程。要使用AIDL服務就必須寫AIDL接口,向外暴露接口就可以與遠程服務進行交互了。對于AIDL有如下幾個值得注意的地方:

  1. AIDL接口的函數都不支持重載,即函數名不能一樣,即使函數參數個數不一樣。
  2. AIDL接口傳遞的參數只有是基本數據類型、String 和CharSequence、List 和 Map、實現android.os.Parcelable 接口的類。
  3. 既然AIDL是在另一個進程的服務,那客戶端調用的AIDL接口是否堵塞? 答案是肯定的。如果不加 oneway 修飾符,那客戶端調用的接口都是堵塞的,但是oneway修飾符也有限制,就是oneway接口下面的方法都必須是返回void類型,不能返回其他類型的數據。

AIDL的接口如何升級?

在做一個比較大型的項目,那項目會不斷迭代,那就有可能增加、修改AIDL接口,那如何保證AIDL接口和老的接口不會混亂呢,根據我的經驗有如下總結:

  1. 對于增刪參數的接口:AIDL函數的訪問會檢測參數,Client有參數的接口可以調用Service的接口(不管有無參數),反過來,Client的接口沒參數就只能調用Service沒有參數的接口。比如我們的新接口定義函數添加了參數,那client必須同時或提前修改,不然我們發布了新接口的Service應用,Client就不能調用了,但是Client用新接口是可以去訪問老接口的服務的。
  2. 對于增刪函數的接口:服務端增加函數并不影響客戶端,相反客戶端增加服務端沒有的接口就會訪問無效果,如果客戶端增加接口有返回值就返回默認值。

Service管理多個客戶端

如果Service有多個客戶端,如何安全地與它們通信呢?如何給各個客戶端回調結果呢? 在這里我要說說我在最近項目出現的一個問題,我在項目中要做一個公共的服務,類似于指紋解鎖,其它應用通過調用我的服務來獲取結果,我設計了start(callback), stop()兩個接口,一開始我就用單回調的方式,即在代碼中定義一個callback的屬性,誰調用了start就把callback設置成誰,只有最后一個調用start的Client能夠獲得回調,代碼如下 :

private Callbak mCallback;
public void start(Callback callback) {
   this.mCallback = callback;
}
public void stop() {
   this.mCallback = null;
}

這種方式,在單個應用時是很有效的,在多個應用時,只要應用能按順序執行start、stop 那這個接口的設計也沒什么問題。但是事情沒想象中那么簡單,如果Client1調用了start,跟著Client2也調用了start,這時Client1 要stop,那會怎樣,那整個服務都stop了。這個就是我設計的服務中出現的大問題,之后我想著為我的服務接口作一些改變,以適應這種多應用的不按順序的調用 。我第一個想法就是用register、unregister的方式,用一個list收集所有的callback ,回調時可以輪循,stop時也可以通過判斷list的個數,如果是小于等于1,那就執行stop :

private List<Callbak> mCallbacks;
public void start(Callback callback) {
   mCallbacks.add(callback);
}
public void stop(Callback callback) {
   mCallbacks.remove(callback);
   if(list.size() >= 1)
     return;
}

考慮到接口的升級,這個改動是最小的,只給stop添加了一個參數。但是這個方式也有毛病,我們服務對Callback的引用是強引用,如果Client異常退出了,那引用還在并且會越積越多,在回調的時候,也可能出現DeadObjectException的錯誤。通過網絡查找資料,我找到了RemoteCallbackList,RemoteCallbackList也是一個列表,保存的是回調接口,使用Link-To-Death回調 (在Sevice中接受到這個Binder對象,并且使用 binder.linkToDeath(),注冊一個DeathRecipient回調;實現DeathRecipient。當Client意外退出的時候,DeathRecipient.binderDied()將被回調,我們可以在這里釋放相關的資源。)。最終代碼如下:

private RemoteCallbackList<Callback> mCallbacks = new RemoteCallbackList<>();
public void start(Callback callback) {
   mCallbacks.register(callback);
}
public void stop(Callback callback) {
   mCallbacks.unregister(callback);
   if(mCallbacks.getBroadcastItem() >= 1)
     return;
}
private void notifyResult(String result) { 
 final int len = mCallbacks.beginBroadcast();
 for (int i = 0; i < len; i++) {
    try {
      mCallbacks.getBroadcastItem(i).onResult(result);
    } catch (RemoteException e) {
      e.printStackTrace();
    }
 }
 mCallbacks.finishBroadcast();
}

使用Messenger 實現 Servie與Client端通信

Messenger是基于Handler的,通過為Messenger添加Handler來傳遞處理數據,之后Service與Client的通信都是通過傳遞的Handler來進行。用這種方式可以不需要定義AIDL接口,也就不出現因為修改AIDl接口所造成的接口版本不對應的麻煩。

Messenger的使用就是通過 Handler傳遞消息, 客戶端send方法發送的是一個Message,這個Message.replyTo指向的是一個Messenger,Messenger又持有客戶端的一個Binder對象(MessengerImpl),服務端正是利用這個Binder對象做的與客戶端的通信。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

博罗县| 梁平县| 常熟市| 泰州市| 历史| 同德县| 通辽市| 富裕县| 通河县| 绍兴市| 大城县| 东乌| 宜宾市| 林周县| 横峰县| 虎林市| 绥阳县| 扬中市| 阳春市| 克山县| 安顺市| 多伦县| 昌宁县| 三江| 南靖县| 保靖县| 谢通门县| 龙泉市| 凉城县| 丰镇市| 台中市| 三河市| 疏勒县| 五家渠市| 伊金霍洛旗| 泉州市| 阳山县| 合川市| 抚顺市| 屯昌县| 衡山县|