2017-09-01 54 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)以再次导致包含启动数据的意图。 –

相关问题