Androidバックグラウンドタスク型Appマルチプロセスアーキテクチャの進化
5769 ワード
バックグラウンドタスク型appとは
音楽やレコーダーのように、ユーザーが長時間バックグラウンドで使用する製品
背景:
筆者の以前のプロジェクトはずっとランニングappをしていたが、ユーザーのシーンはこのようなもので、ユーザーがランニングモードを開いた後、Gps信号を傍受してユーザーの運動データを統計する必要がある.距離、配速、時間を含む.実は「簡単」に見えるユーザーシーンですが、最初は筆者もそう思っていましたが、しばらくの反復完備を経て、今からその中の「簡単ではない」を共有します.筆者はランニングapp開発者の角度からこのようなランニングアプリのアーキテクチャの進化を共有します.
最初のアーキテクチャ
筆者はできるだけ早く製品マネージャーのニーズを実現するために、appの最初の版を完成しました.このアーキテクチャはこのようなものです.
Activity+Forground Service+Sqlite+Eventbusのうち、ActivityはUI層、Serviceはランニングモードがオンのときに起動するforground serviceを表し、モーションデータを記録するために使用され、Sqliteはデータの記憶層を表し、eventbusはイベントバスのlibraryであり、モジュール間のデカップリングに使用される.
問題を引き起こす
最初の版が発行された後、一部のユーザーのフィードバックを受けて、運動データのマイルが失われ、記録が正確ではないという問題は、データ統計の運動appにとって致命的であるのに、なぜこのような問題があるのだろうか.appのプロセスが回収されたので簡単に推測できます
解決方法
主にUIプロセスとサービスプロセスの分離といくつかのサービスの保存の策略をして、主に2つの原因に基づいています Androidプロセス管理メカニズムここではAndroidのプロセス管理メカニズムについて言及せざるを得ない.AndroidシステムはLow Memory Killerメカニズム(参考)によってプロセスを管理し、プロセスについていくつかの優先度に分けられる:
各プロセスの優先度は、システム計算oom_に依存します.adjの値はoom_に影響しますadjの要因はどれらがありますか?主にプロセスがメモリを消費するサイズは、ランニングのようなappにとって、ユーザシーンが長い間バックグラウンドで実行されている状態であり、フロントUIはインタラクションのみを担当し、バックグラウンドのサービスは業務の処理を担当し、UIプロセスのメモリはSeviceのメモリ占有量よりはるかに大きいため、appがバックグラウンドに切り替えられたときにすべてのUIリソースを解放することができれば、このappの実行時に大量のメモリを節約することができる.
第2版の修正
以上の2つの理由に基づいて、第2版の再構築があり、アーキテクチャはこのようになりました.
UIプロセス+Remoteプロセス(サービスプロセス)
では、appが単一プロセスからマルチプロセスに変わるとどのような穴があるのでしょうか.筆者は主に3つの問題にぶつかった. 1.プロセス間の通信方法 2.2つのプロセスがデータ保証プロセスセキュリティにどのようにアクセスするか 3.プロセスの安全を保証する方法sharepreference 第1の問題に対して、マルチプロセス通信の方式:1.Broadcast:この方式のすべての通信プロトコルはintentの中で送信して受け入れる必要があります.非同期の通信方式です.つまり、呼び出し後すぐに結果を返すことができません.また、UIセグメントとサービスセグメントに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で宣言 aidlインタフェース を宣言する RemoteService Binder を書き換える UIプロセスを書き換えるBinder onServiceConnectionの場合、UIプロセスのbinderがremoteプロセス に登録されます.
2つ目の問題は、2つのプロセスがデータにアクセスして一貫性を保証する方法です.ContentProviderはSqliteの上位階層にContentProviderをカプセル化し、既存のアーキテクチャが次のようになりました.
UI process: Activity + eventbus Remote process : Service + ContentProvider + Sqlite + Eventbus
もう3つ目の問題は、ユーザーのニーズです.複数のプロセスは、ランニング中、ランニング一時停止かランニング終了かなど、ランニングの状態情報を取得する必要があります.プロセスの場合はSharePreferenceを使用して永続化された状態を格納し、プロセスを分割した後、MODE_の使用を開始します.MULTI_PROCESS、その後、ドキュメントの注釈が廃棄されていることに気づきました.multi_プロセスモードでは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.
では、どのように解決すればいいのでしょうか.2つのシナリオ 1.ContentProvider+ Sqlite Tray(https://github.com/grandcentrix/tray/) 2.ContentProvider + SharePreference(MODE_PRIVATE) DPreference(https://github.com/DozenWang/DPreference)
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方式ですべてコピーする必要がある場合、シナリオ2は天然にこのような問題を回避し、読み書き性能がより優れているため、シナリオ2を採用し、アーキテクチャがこのようになったという欠点があります.
UI process: Activity + eventbus Remote process : Service + (ContentProvider + Sqlite)+ (ContentProvider + SharePreference) + Eventbus
以上は筆者がマルチプロセス開発で出会ったいくつかの問題と解決策であり、皆さんの役に立つことを望んでいます.
音楽やレコーダーのように、ユーザーが長時間バックグラウンドで使用する製品
背景:
筆者の以前のプロジェクトはずっとランニングappをしていたが、ユーザーのシーンはこのようなもので、ユーザーがランニングモードを開いた後、Gps信号を傍受してユーザーの運動データを統計する必要がある.距離、配速、時間を含む.実は「簡単」に見えるユーザーシーンですが、最初は筆者もそう思っていましたが、しばらくの反復完備を経て、今からその中の「簡単ではない」を共有します.筆者はランニングapp開発者の角度からこのようなランニングアプリのアーキテクチャの進化を共有します.
最初のアーキテクチャ
筆者はできるだけ早く製品マネージャーのニーズを実現するために、appの最初の版を完成しました.このアーキテクチャはこのようなものです.
Activity+Forground Service+Sqlite+Eventbusのうち、ActivityはUI層、Serviceはランニングモードがオンのときに起動するforground serviceを表し、モーションデータを記録するために使用され、Sqliteはデータの記憶層を表し、eventbusはイベントバスのlibraryであり、モジュール間のデカップリングに使用される.
問題を引き起こす
最初の版が発行された後、一部のユーザーのフィードバックを受けて、運動データのマイルが失われ、記録が正確ではないという問題は、データ統計の運動appにとって致命的であるのに、なぜこのような問題があるのだろうか.appのプロセスが回収されたので簡単に推測できます
解決方法
主にUIプロセスとサービスプロセスの分離といくつかのサービスの保存の策略をして、主に2つの原因に基づいています
- native
- persistent
- forground
- visible
- cache
各プロセスの優先度は、システム計算oom_に依存します.adjの値はoom_に影響しますadjの要因はどれらがありますか?主にプロセスがメモリを消費するサイズ
第2版の修正
以上の2つの理由に基づいて、第2版の再構築があり、アーキテクチャはこのようになりました.
UIプロセス+Remoteプロセス(サービスプロセス)
では、appが単一プロセスからマルチプロセスに変わるとどのような穴があるのでしょうか.筆者は主に3つの問題にぶつかった.
2.Messager Messengerの使用方法は比較的簡単で、Messengerを定義してhandlerを通信のインタフェースとして指定し、onBindの時にMessengerのgetBinderメソッドを返し、UIで戻ったIBinderを利用してMessengerも作成し、彼らの間で通信することができます.この呼び出し方法も非同期呼び出しに属する.
3.ResultReceiverはコンポーネント間の非同期通信であり、要求-コールバックモードによく用いられる.
4.Binderというaidlを介した通信を書き換える最後のスキームを選択しました.メインプロセスはbindserviceを介してremoteプロセスを呼び出し、onServiceConnection時にremoteプロセスのcallbackコールバックを登録して、remoteプロセスのメッセージをリスニングし、受信します.
//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);
}
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;
}
}
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
}
}
@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;
}
2つ目の問題は、2つのプロセスがデータにアクセスして一貫性を保証する方法です.ContentProviderはSqliteの上位階層にContentProviderをカプセル化し、既存のアーキテクチャが次のようになりました.
UI process: Activity + eventbus Remote process : Service + ContentProvider + Sqlite + Eventbus
もう3つ目の問題は、ユーザーのニーズです.複数のプロセスは、ランニング中、ランニング一時停止かランニング終了かなど、ランニングの状態情報を取得する必要があります.プロセスの場合はSharePreferenceを使用して永続化された状態を格納し、プロセスを分割した後、MODE_の使用を開始します.MULTI_PROCESS、その後、ドキュメントの注釈が廃棄されていることに気づきました.multi_プロセスモードでは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.
では、どのように解決すればいいのでしょうか.2つのシナリオ
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方式ですべてコピーする必要がある場合、シナリオ2は天然にこのような問題を回避し、読み書き性能がより優れているため、シナリオ2を採用し、アーキテクチャがこのようになったという欠点があります.
UI process: Activity + eventbus Remote process : Service + (ContentProvider + Sqlite)+ (ContentProvider + SharePreference) + Eventbus
以上は筆者がマルチプロセス開発で出会ったいくつかの問題と解決策であり、皆さんの役に立つことを望んでいます.