Android 后臺任務型App多進程架構演化

什么是后臺任務型app

類似音樂、錄音機,需要用戶長時間在后臺使用的產品

背景:

筆者之前的項目一直在做跑步app, 用戶的場景是這樣的,用戶開啟跑步模式后,我們需要監聽Gps 信號來統計用戶的運動數據,包括距離,配速,時間。其實是看似很“簡單"的用戶場景, 起初筆者也這么認為,經過了一段時間的迭代完善,現在就來分享一些其中的”不簡單“。筆者會從一個跑步app開發者的角度分享這樣一個跑步App的架構演化。

最初的架構

筆者為了盡快實現產品經理的需求,馬不停蹄的完成了app 的最初版,這時這個架構是這樣的

Activity + Forground Service + Sqlite+Eventbus
其中: Activity 代表UI 層, Service 代表開啟跑步模式時啟動的forground service,用以記錄運動數據,Sqlite 代表數據的存儲層, eventbus 是一個事件總線的library,用于模塊間解耦。

引來的問題

最初版發出之后,收到一些用戶反饋,反應運動數據里程丟失,記錄不準,這樣的問題對于一款數據統計的運動app來說是致命的,那么為什么會有這樣的問題呢?很容易猜到,因為我們app的進程被回收了

如何解決

主要做了UI進程與Service進程分離和一些service保活的策略,主要基于一下兩點原因

  • Android進程管理機制
    這里就不得不提到Android 的對于進程管理的機制,Android 系統是通過Low Memory Killer 機制(參考)來管理進程的,對于進程分為幾個優先級:
- native
- persistent
- forground
- visible
- cache

每個進程的優先級取決于系統計算oom_adj 的值,那么影響oom_adj的因素有哪些呢?主要是進程占用內存的大小

  • 便于系統回收資源
    對于跑步這類app而言,用戶場景很長時間是處于后臺運行的狀態,前臺UI只負責交互,后臺的service負責業務的處理,而且UI進程的內存占遠大于Sevice的內存占用,所以如果能夠在app切換到后臺的時候釋放掉所有的UI資源,那么這個app運行時就能夠 省出大量內存。

第二版的修改

基于以上兩點原因, 于是有了第二版的重構,架構變成了這樣:

UI進程 + Remote進程(service 進程

那么問題來了,app從單進程變成多進程會存在哪些坑呢?筆者主要遇到了三個問題

  • 1.進程間如何通信
  • 2.兩個進程如何訪問數據保證進程安全
  • 3.如何保證進程安全的操作sharepreference

針對第一個問題,多進程通信的方式:
1.Broadcast
這種方式的所有通訊協議都需要放在intent里面發送和接受,是一種異步的通訊方式,即調用后無法立刻得到返回結果。另外還需要在UI和service段都要注冊receiver才能達到他們之間的相互通訊。

2.Messager
Messenger的使用 方法比較簡單,定義一個Messenger并指定一個handler作為通訊的接口,在onBind的時候返回Messenger的getBinder方 法,并在UI利用返回的IBinder也創建一個Messenger,他們之間就可以進行通訊了。這種調用方法也屬于異步調用

3.ResultReceiver 跨組件的異步通訊,常用于請求-回調模式.

4.重寫Binder
這種通過aidl進行通信
我們選擇了最后一種方案:
主進程通過bindservice 調起remote 進程,并在onServiceConnection時,注冊一個remote 進程的callback 回調,用于監聽,接收remote進程的消息。

  • 首先在AndroidManifest.xml 中聲明
<serviceandroid:name=".RemoteService"
android:process=":remote"
android:label="@string/app_name" />
  • 聲明aidl接口
//aidl service 進程持有的對象
interface IRemoteService {
void registerCallback(IRemoteCallback cb);
void unregisterCallback(IRemoteCallback cb);
}
//回調更新UI進程數據的接口
interface IRemoteCallback {
void onDataUpdate(double distance,double duration, double pace, double calorie, double velocity);
}
  • 重寫RemoteService Binder
LocalBinder mBinder = new LocalBinder();
IRemoteCallback mCallback;
class LocalBinder extends IRemoteService.Stub {    
  @Override    
  public void registerCallback(IRemoteCallback cb) throws  RemoteException {        
      mCallback = cb;    
  }    
  @Override    
  public void unregisterCallback(IRemoteCallback cb) throws   RemoteException {        
        mCallback = null;  
  }   
  public IBinder asBinder() {        
        return null;   
   }
}
  • 重寫UI進程的Binder
public class RemoteCallback extends IRemoteCallback.Stub {    
@Override    
public void onActivityUpdate(final double distance, final double duration, final double pace, final double calorie, final double velocity) throws RemoteException {        
  //do something
    }    
}
  • onServiceConnection 時將UI 進程的binder 注冊到remote進程
@Override
public void onServiceConnected(ComponentName name, IBinder service) {   
 try {       
    mService = IRemoteService.Stub.asInterface(service);        
    mService.registerCallback(mCallback);    
}  catch (RemoteException e) {      
  e.printStackTrace();   
 }
}
@Override
public void onServiceDisconnected(ComponentName name) {    
try {     
     if (mService != null) { 
         mService.unregisterCallback(mCallback);     
     } 
 } catch (RemoteException e) {    
    e.printStackTrace();    
}    
    mService = null;
}

第二個問題,兩個進程如何訪問數據保證一致性:ContentProvider
在Sqlite 上層封裝一層ContentProvider
于是現有的架構變成了:

UI process: Activity + eventbus
Remote process : Service + ContentProvider + Sqlite + Eventbus

還有第三個問題:
用戶需求:多個進程需要獲取跑步的狀態信息,比如跑步中,跑步暫停還是跑步結束。
一個進程的時候使用SharePreference存儲一個持久化的狀態,分進程之后,開始使用MODE_MULTI_PROCESS, 而后來發現文檔注釋被廢棄掉了,multi_process 模式下sharepreference工作不會可靠,同步數據不會一致,如下描述:

SharedPreference loading flag: when set, the file on disk will be checked for modification even if the shared preferences instance is already loaded in this process. This behavior is sometimes desired in cases where the application has multiple processes, all writing to the same SharedPreferences file. Generally there are better forms of communication between processes, though.

那么如何解決呢?
兩種方案

性能比較
DPreference setString
called 1000 times cost : 375 ms getString
called 1000 times cost : 186 ms
Tray setString
called 1000 times cost : 13699 ms getString
called 1000 times cost : 3496 ms

方案1還有一個缺點,如果將老的SharePreference 數據遷移到 用sqlite的方式需要全部拷貝,而方案二天然的避免了這樣的問題,并且讀寫性能更佳,于是采用了方案二
于是架構變成了這樣:

UI process: Activity + eventbus
Remote process : Service + (ContentProvider + Sqlite)+ (ContentProvider + SharePreference) + Eventbus

以上就是筆者在多進程開發中遇到的一些問題和解決方案,希望可以對大家有所幫助

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容