2017-09-01 27 views
0

昨日サービスの作成を始めたばかりです。サービスがあります。私のアプリケーションが終了してもいつも実行する予定です。このサービスはFirebaseデータベースにリスナーを持ち、データが変更された場合に通知を生成します。Android:サービスを終了した後にサービスがクラッシュしました

私のサービスは、アプリケーションが実行されている場合にのみ正常に実行できるということを私に混乱させました。私がアプリケーションを終了した直後に、私の電話で「残念ながら、このアプリは停止しました(クラッシュした)」と警告しました。警報は2回来て、その後サービスは再開しなかった(私は推測する)。

私は、アプリケーションを閉じた後に私のサービスがクラッシュした理由を理解できませんでした。私はMainActivityのOnCreateメソッドでサービスを開始しました。サービスクラス全体がここにあります:

public class MyService extends Service 
{ 
private static DatabaseReference databaseReference; 
private static String uid; 
private static SharedPreferences sharedPreferences; 
private static int notification_id=0; 
private static boolean launched=false; 
public MyService(){} 
@Override public int onStartCommand(Intent intent, int flags, int startId) 
{ 
    if(!launched) 
    { 
     launched=true; 
     databaseReference=MainActivity.databaseReference; 
     uid=MainActivity.uid; 
     sharedPreferences=MainActivity.sharedPreferences; 
     databaseReference.child("users").child(uid).addValueEventListener(new ValueEventListener() 
     { 
      @Override public void onDataChange(DataSnapshot dataSnapshot) 
      { 
       MyData myData=dataSnapshot.getValue(MyData.class); 
       if(myData!=null) 
       { 
        ArrayList<String> myGroups=myData.getGroups(); 
        for(String group_id:myGroups) 
        { 
         if(group_record_listener!=null)databaseReference.child("group_records").child(group_id).removeEventListener(group_record_listener); 
         databaseReference.child("group_records").child(group_id).addValueEventListener(group_record_listener); 
        } 
       } 
      } 
      @Override public void onCancelled(DatabaseError databaseError) 
      { 
       System.out.println(databaseError.getMessage()); 
      } 
     }); 
    } 
    return START_STICKY; 
} 
private ValueEventListener group_record_listener=new ValueEventListener() 
{ 
    @Override public void onDataChange(DataSnapshot dataSnapshot) 
    { 
     final String group_id=dataSnapshot.getKey(); 
     Query lastQuery=databaseReference.child("group_records").child(group_id).child("records").orderByKey().limitToLast(1); 
     lastQuery.addListenerForSingleValueEvent(new ValueEventListener() 
     { 
      @Override public void onDataChange(DataSnapshot dataSnapshot) 
      { 
       for(DataSnapshot childSnapshot:dataSnapshot.getChildren()) 
       { 
        final Record new_record=childSnapshot.getValue(Record.class); 
        if(new_record!=null) 
        { 
         if((new_record.getType()==1)&&new_record.getTo_name().equals(sharedPreferences.getString("name",""))) 
         { 
          if(group_record_listener!=null)databaseReference.child("group_records").child(group_id).removeEventListener(group_record_listener); 
         } 
         if(!new_record.getOperator().equals(sharedPreferences.getString("name",""))) 
         { 
          databaseReference.child("groups").child(group_id).child("name").addListenerForSingleValueEvent(new ValueEventListener() 
          { 
           @Override public void onDataChange(DataSnapshot dataSnapshot) 
           { 
            String group_name=(String)dataSnapshot.getValue(); 
            generateNotification(new_record,group_name); 
           } 
           @Override public void onCancelled(DatabaseError databaseError){} 
          }); 
         } 
        } 
       } 
      } 
      @Override public void onCancelled(DatabaseError databaseError){} 
     }); 
    } 
    @Override public void onCancelled(DatabaseError databaseError) 
    { 
     System.out.println(databaseError.getMessage()); 
    } 
}; 
private void generateNotification(Record record,String group_name) 
{ 
    String str=""; 
    switch (record.getType()) 
    { 
     //. . . determine str 
    } 
    Notification notification=new Notification.Builder(this) 
      .setSmallIcon(R.mipmap.ic_launcher) 
      .setContentTitle("group \""+group_name+"\"") 
      .setContentText(str) 
      .setWhen(System.currentTimeMillis()) 
      .build(); 
    NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE); 
    notification_id++; 
    manager.notify(notification_id,notification); 
} 
@Override public IBinder onBind(Intent intent) 
{ 
    return null; 
} 
} 

ご意見がありましたら、助けてください。ありがとうございます~~

答えて

1

MainActivitydatabaseReference,uidおよびsharedPreferencesの静的メンバーを使用しています。あなたの郵便であなたが説明するように、サービスはMainActivity.onCreate()から始まります。その場合、databaseReference,uidおよびsharedPreferencesが(おそらく)初期化され、有効な値を持っています。

サービスのonStartCommand()メソッドは、START_STICKYを返します。あなたのアプリが殺された場合、たとえば、バックグラウンドでそれを入れてから最近のタスクリストからスワイプすると、そのアプリのプロセスは破棄され、サービスがなので、システムは新しいapp(新しいプロセス)。この新しいインスタンスにはサービスコンポーネントが含まれていますが、MainActivityという有効なインスタンスはありません。 MainActivityの静的メンバーにはデフォルト値が設定されます。特にuidはnullになります。databaseReference.child("users").child(uid)に電話すると例外が発生します。デバッグロギングを追加して、uidsharedPreferencesの値を出力することで、これが起こっていることを確認できます。

しかし、この作業をしようと多くの努力を払う前に、現在のデザインはバッテリーを殺していることに注意してください。データベース上のアクティブなリスナを使用して常にサービスを実行させるには、Firebaseがデータベースへのネットワーク接続を維持する必要があります。あなたのデータベースの監視要件は何か分かりませんが、このような場合にはサーバー側の処理(たとえばクラウド機能)を変更して、通知を必要とするユーザーにFCMメッセージを送信する方が良いでしょう。

+0

ありがとうございます〜私はすぐにFCMメッセージを探します。しかしそれ以外にも、uidの値を保持して新しいサービスインスタンスに渡す方法はありますか? – Einsambr

+1

@Einsambr:MainActivityの静的メンバーの代わりに、必要なデータを 'startService()'に使用された 'Intent'に渡し、' onStartCommand() 'のパラメータとして利用できます。 'Service'は' Context'を拡張していますので、 'getSharedPreferences()'を実行することができ、それらを目的地に渡す必要はありません。 START_STICKYの代わりに。 [START_REDELIVER_INTENT](https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT)を返して、サービスが再開されたときに開始データを含むインテントを再度表示させたいと思います。 –

関連する問題