私は、ユーザーが複数のデバイス間で複数のログインを持つことができるプロジェクトを持っています。Firebase Event Listenerのバックオフポリシーとは何ですか?
ユーザーは任意のデバイス上の特定のトピックを購読することができ、残りのデバイスログインも同様に行う必要があります。同様のケースは、1つのデバイスがサブスクライブしないときであり、残りはスイートに従うべきである。
これを行うために、firebaseデータベース内のすべてのサブスクリプションが維持されている各ユーザの下にNodeを作成しました。私は、この場所にFirebaseリスナーを添付し、変更が発生したときにトピックからサブサブ/アンサブスクライブするSTART_STICKYサービスを持っています。サービスのコードは説明の下に添付されています。
通常の観測では、システムがそれを殺す場合には、起動がスティッキーなので、私が持っているサービスが再発生します。開発者のオプションを使用してユーザがそれを改ざんした場合、明示的に再生成されます。それは完全に中止するようになります唯一の例は以下のとおりです。
- サインアウトデータは
- 力が
私の質問は
どのようにひどくは維持されますある停止クリアリスナが接続されているとバッテリの寿命に影響します。 AFAIK Firebaseは、ウェブソケットが切断されてバッテリーが一定に流れるのを防ぐために指数関数的バックオフを持っています
接続がかなりオフになっても、Firebase Listenerは再接続を断念できますか?そうであれば、いつバックオフ限界に達するか。
トピックが複数のデバイスに登録され、登録解除されていることを確認する良い方法はありますか?
これを行うにはサービスが適していますか?以下のサービスを最適化できますか?そして、はい、それは絶え間なく実行する必要があります。
コード
public class SubscriptionListenerService extends Service {
DatabaseReference userNodeSubscriptionRef;
ChildEventListener subscribedTopicsListener;
SharedPreferences sessionPref,subscribedTopicsPreference;
SharedPreferences.Editor subscribedtopicsprefeditor;
String userid;
boolean stoppedInternally = false;
SharedPreferences.OnSharedPreferenceChangeListener sessionPrefChangeListener;
@Nullable
@Override
public IBinder onBind(Intent intent) {
//do not need a binder over here
return null;
}
@Override
public void onCreate(){
super.onCreate();
Log.d("FragmentCreate","onCreate called inside service");
sessionPref = getSharedPreferences("SessionPref",0);
subscribedTopicsPreference=getSharedPreferences("subscribedTopicsPreference",0);
subscribedtopicsprefeditor=subscribedTopicsPreference.edit();
userid = sessionPref.getString("userid",null);
sessionPrefChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d("FragmentCreate","The shared preference changed "+key);
stoppedInternally=true;
sessionPref.unregisterOnSharedPreferenceChangeListener(this);
if(userNodeSubscriptionRef!=null && subscribedTopicsListener!=null){
userNodeSubscriptionRef.removeEventListener(subscribedTopicsListener);
}
stopSelf();
}
};
sessionPref.registerOnSharedPreferenceChangeListener(sessionPrefChangeListener);
subscribedTopicsListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if(!(dataSnapshot.getValue() instanceof Boolean)){
Log.d("FragmentCreate","Please test subscriptions with a boolean value");
}else {
if ((Boolean) dataSnapshot.getValue()) {
//here we subscribe to the topic as the topic has a true value
Log.d("FragmentCreate", "Subscribing to topic " + dataSnapshot.getKey());
subscribedtopicsprefeditor.putBoolean(dataSnapshot.getKey(), true);
FirebaseMessaging.getInstance().subscribeToTopic(dataSnapshot.getKey());
} else {
//here we unsubscribed from the topic as the topic has a false value
Log.d("FragmentCreate", "Unsubscribing from topic " + dataSnapshot.getKey());
subscribedtopicsprefeditor.remove(dataSnapshot.getKey());
FirebaseMessaging.getInstance().unsubscribeFromTopic(dataSnapshot.getKey());
}
subscribedtopicsprefeditor.commit();
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
//either an unsubscription will trigger this, or a re-subscription after an unsubscription
if(!(dataSnapshot.getValue() instanceof Boolean)){
Log.d("FragmentCreate","Please test subscriptions with a boolean value");
}else{
if((Boolean)dataSnapshot.getValue()){
Log.d("FragmentCreate","Subscribing to topic "+dataSnapshot.getKey());
subscribedtopicsprefeditor.putBoolean(dataSnapshot.getKey(),true);
FirebaseMessaging.getInstance().subscribeToTopic(dataSnapshot.getKey());
}else{
Log.d("FragmentCreate","Unsubscribing from topic "+dataSnapshot.getKey());
subscribedtopicsprefeditor.remove(dataSnapshot.getKey());
FirebaseMessaging.getInstance().unsubscribeFromTopic(dataSnapshot.getKey());
}
subscribedtopicsprefeditor.commit();
}
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
//Log.d("FragmentCreate","Unubscribing from topic "+dataSnapshot.getKey());
//FirebaseMessaging.getInstance().unsubscribeFromTopic(dataSnapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
//do nothing, this won't happen --- rather this isnt important
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.d("FragmentCreate","Failed to listen to subscriptions node");
}
};
if(userid!=null){
Log.d("FragmentCreate","Found user id in service "+userid);
userNodeSubscriptionRef = FirebaseDatabase.getInstance().getReference().child("Users").child(userid).child("subscriptions");
userNodeSubscriptionRef.addChildEventListener(subscribedTopicsListener);
userNodeSubscriptionRef.keepSynced(true);
}else{
Log.d("FragmentCreate","Couldn't find user id");
stoppedInternally=true;
stopSelf();
}
}
@Override
public int onStartCommand(Intent intent,int flags,int startId){
//don't need anything done over here
//The intent can have the following extras
//If the intent was started by the alarm manager ..... it will contain android.intent.extra.ALARM_COUNT
//If the intent was sent by the broadcast receiver listening for boot/update ... it will contain wakelockid
//If it was started from within the app .... it will contain no extras in the intent
//The following will not throw an exception if the intent does not have an wakelockid in extra
//As per android doc... the following method releases the wakelock if any specified inside the extra and returns true
//If no wakelockid is specified, it will return false;
if(intent!=null){
if(BootEventReceiver.completeWakefulIntent(intent)){
Log.d("FragmentCreate","Wakelock released");
}else{
Log.d("FragmentCreate","Wakelock not acquired in the first place");
}
}else{
Log.d("FragmentCreate","Intent started by regular app usage");
}
return START_STICKY;
}
@Override
public void onDestroy(){
if(userNodeSubscriptionRef!=null){
userNodeSubscriptionRef.keepSynced(false);
}
userNodeSubscriptionRef = null;
subscribedTopicsListener = null;
sessionPref = null;
subscribedTopicsPreference = null;
subscribedtopicsprefeditor = null;
userid = null;
sessionPrefChangeListener = null;
if(stoppedInternally){
Log.d("FragmentCreate","Service getting stopped due to no userid or due to logout or data clearance...do not restart auto.. it will launch when user logs in or signs up");
}else{
Log.d("FragmentCreate","Service getting killed by user explicitly from running services or by force stop ... attempt restart");
//well basically restart the service using an alarm manager ... restart after one minute
AlarmManager alarmManager = (AlarmManager) this.getSystemService(ALARM_SERVICE);
Intent restartServiceIntent = new Intent(this,SubscriptionListenerService.class);
restartServiceIntent.setPackage(this.getPackageName());
//context , uniqueid to identify the intent , actual intent , type of pending intent
PendingIntent pendingIntentToBeFired = PendingIntent.getService(this,1,restartServiceIntent,PendingIntent.FLAG_ONE_SHOT);
if(Build.VERSION.SDK_INT>=23){
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+600000,pendingIntentToBeFired);
}else{
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+600000,pendingIntentToBeFired);
}
}
super.onDestroy();
}
}
編集用フランク – Kushan
@フランク・ヴァン・プフレンはこの問題を親切に助けてくれました。数日以来これに固執しています。私が持っているサービスは確かに私がそれを望む方法で動作しますが、それが確実なショットの方法であるかどうかわかりません。 – Kushan
_はい、それは常に実行する必要があります_:[ドーズモード]のため、API 23以上のデバイスでは不可能です(https://developer.android.com/training/monitoring-device-state/doze-standby .html) –