2017-09-01 98 views
0

我剛剛開始學習編寫服務昨天,在這裏我有一個服務,我希望即使我的應用程序終止,所有的時間運行。此服務在我的Firebase數據庫上具有偵聽器,並在數據發生更改時生成通知。Android:關閉應用程序後服務崩潰

令我困惑的是我的服務只有在應用程序運行時才能成功運行。我關閉應用程序後立即通知我,「不幸的是,這個應用程序已停止(崩潰)」。警報來了兩次,然後服務從未重新啓動(我猜)。

我無法弄清楚爲什麼我的服務在關閉應用程序後崩潰。我在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

您的服務使用MainActivity的靜態成員:databaseReferenceuidsharedPreferences。正如你在帖子中解釋的那樣,服務從MainActivity.onCreate()開始。在這種情況下,databaseReference,uidsharedPreferences(假定)已被初始化並具有有效值。

服務中的onStartCommand()方法返回START_STICKY。例如,如果你的應用程序被殺死了,例如,如果你把它放在後臺,然後從最近的任務列表中滑過它,應用程序的進程將被銷燬,然後,由於你的服務是START_STICKY,系統會創建一個新的實例應用程序(新進程)。這個新實例包含服務組件,但沒有有效的MainActivity實例。 MainActivity的靜態成員將具有默認值。特別是,uid將爲空,當您撥打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)以再次導致包含啓動數據的意圖。 –