【问题标题】:Android Background Service causes a lot of crashesAndroid后台服务导致很多崩溃
【发布时间】:2019-08-20 20:27:19
【问题描述】:

我有一个即时按摩器的后台服务,它会导致大量崩溃(每天大约 10.000 次)。

我做了什么:我用onStartCommant 启动服务并返回START_STICKY

onDestroy()onTaskRemoved() 调用一个广播来重新启动服务,因此它会更新死亡,这主要是有效的。

现在我认识到 android 设备管理器给出了将其设置为待机的建议,因为崩溃太多了(在后台用户不会注意到它)。

这是我的服务:

public static boolean isMyServiceRunning(Class<?> serviceClass, Context c) {
    ActivityManager manager = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE);
    for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (serviceClass.getName().equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

public static MassageDataSource getMassageDataSource() {
    return massageDataSource;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);

    if (context == null) {
        context = this;
    }

    SQLiteDatabase.loadLibs(context);
    if (massageDataSource == null) {
        massageDataSource = new MassageDataSource(context);
        massageDataSource.open();
    }

    startTimer();
    return START_STICKY;
    //return START_REDELIVER_INTENT;
}

@Override
public void onDestroy() {
    super.onDestroy();
    Intent broadcastIntent = new Intent(context, RestartService.class);
    sendBroadcast(broadcastIntent);
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    super.onTaskRemoved(rootIntent);
    Intent broadcastIntent = new Intent(context, RestartService.class);
    sendBroadcast(broadcastIntent);
}

public void startTimer() {
    //set a new Timer
    timer = new Timer();

    //initialize the TimerTask's job
    initializeTimerTask();

    //schedule the timer, to wake up every 1 second
    timer.schedule(timerTask, 1000, 1000); //
}

/**
 * it sets the timer to print the counter every x seconds
 */
public void initializeTimerTask() {
    timerTask = new TimerTask() {
        public void run() {
            Log.i("in timer", "in timer ++++  " + (counter++));
        }
    };
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

这是我的广播:

@Override
public void onReceive(Context context, Intent intent) {
    Log.i(RestartService.class.getSimpleName(), "Service Stopped!");

    if (!BackgroundService.isMyServiceRunning(BackgroundService.class, context)) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent service = new Intent(context, BackgroundService.class);
        PendingIntent pendingIntent;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            pendingIntent = PendingIntent.getForegroundService(context, BackgroundService.SERVICE_ID, service, PendingIntent.FLAG_CANCEL_CURRENT);
        } else {
            pendingIntent = PendingIntent.getService(context, BackgroundService.SERVICE_ID, service, PendingIntent.FLAG_CANCEL_CURRENT);
        }
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), intervall, pendingIntent);

        Log.i(RestartService.class.getSimpleName(), "Service restarted.");
    } else {
        Log.i(RestartService.class.getSimpleName(), "Service is already running");
    }
}

服务运行 8 秒,然后被广播停止并重新启动。但是每次它被破坏时都会崩溃并在 logcat 上产生这个输出:

 Thread[3,tid=17948,WaitingInMainSignalCatcherLoop,Thread*=0x75b3a16400,peer=0x13b80020,"Signal Catcher"]: reacting to signal 3

 Wrote stack traces to '[tombstoned]'

 D/ConnectivityManager_URSP: Ursp sIsUrsp=false, sIsCheckUrsp=false, uid=10291

 D/Proxy: urspP is null: 10291

 I/LoadedApk: No resource references to update in package com.test.module1
 I/LoadedApk: No resource references to update in package com.test.module2

 D/SensorManager: registerListener :: 10, TMD4906 lux Sensor, 66667, 0,

 I/RestartService: Service Stopped!
 I/RestartService: Service restarted.

我读到START_STICKY 导致了这种情况,所以我尝试了其他命令,但只有START_STICKY 我的服务不会死。

此外,由于某些原因,服务未在启动时启动。

【问题讨论】:

  • 我不确定,但 timerTask 可能是原因。为什么不取消 onDestroy 中的任务,看看有什么不同?
  • 我试过了,但没有改变

标签: java android service crash


【解决方案1】:

如果您返回START_REDELIVER_INTENTSTART_STICKY 和相关标志,Android 框架将使服务保持活动状态(或为您重新创建)。

文档中START_REDELIVER_INTENT的描述:

onStartCommand(Intent, int, int)返回的常量:如果该服务的进程在启动时被杀死(从onStartCommand(Intent, int, int))返回后,它将被安排重新启动,并通过@再次向它传递最后一次传递的Intent 987654330@。在服务调用stopSelf(int) 并将开始ID 提供给onStartCommand(Intent, int, int) 之前,此Intent 将保持重新发送的计划

所以我建议使用 Android 的框架行为来实现,返回 START_REDELIVER_INTENT 以使服务在因任何原因被终止后再次运行,而无需调用 Service.stopSelf()Context.stopService()

但是如果您需要使用广播机制来做,也许只需返回START_NOT_STICKY 以防止系统为您做这件事。并且系统不会重新创建服务,这将导致onStartCommand()被多次调用并导致崩溃。

有关onStartCommand()的更多详细信息,请参阅document

【讨论】:

  • 我尝试在 onDestroy 和 onTaskRemoved() 中删除 startBroadcast(),并将返回 START_STICKY 更改为返回 START_REDILIVER_INTENT。但是现在在应用程序关闭后,服务永远死了,不会重新启动。 Android 没有尝试重新启动我的服务
  • 我也尝试了第二个建议并使用了 START_NOT_STICKY。现在似乎没有崩溃,但现在设备管理器建议将应用设置为待机,因为“应用不响应”。
  • 发生了一些奇怪的事情:I/RestartService:服务已停止! I/RestartService:服务已经在运行所以由于某些原因 onDestroy 或 onTaskRemoved 被调用,但服务仍在运行
  • @Jones 服务未重启:第一次如何启动服务?如果您使用Context.bindService() 创建服务,它将在所有连接解除绑定后被终止。如果你这样做,请在Context.bindService() 之前使用Context.startService()
  • 我不使用 bindService() 我在检查服务是否已经运行后开始使用 startService()
【解决方案2】:

更新:

按照上面的建议,我现在要返回 START_NOT_STICKY。但这并不能解决问题。所以我决定将 startInForeground 用于 api>=26 (Android Oreo)。 现在服务永远存在,没有任何问题。

我唯一不喜欢的当然是一直有通知。 我想要一个后台服务,例如 Whatsapp 使用。他们的后台服务一直在运行,如果它被破坏或出现其他情况,则会出现“您可能有新消息”的通知,然后该服务再次处于活动状态并且通知正在消失。

所以我现在做了什么:

-广播还是一样

-后台服务:

@Override
public void onCreate() {
    super.onCreate();
    Log.i("BackgroundService", "onCreate was called");
    SQLiteDatabase.loadLibs(this);
    messageDataSource = new MessageDataSource(this);
    messageDataSource.open();
    startForegroundService();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);

    Log.i("BackgroundService", "onStartCommand was called");
    context = this;
    return START_NOT_STICKY;
}


public void startForegroundService() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        ChannelID = createNotificationChannel("app_service_1", "Benachrichtigungen", NotificationManager.IMPORTANCE_NONE);
    } else {
        ChannelID = "app_service_1";
    }

    startForeground(101, buildForegroundNotification("Sie könnten neue Nachrichten haben")); //should use resource string
}

@Override
public void onDestroy() {
    super.onDestroy();
    Intent broadcastIntent = new Intent(context, RestartService.class);
    sendBroadcast(broadcastIntent);
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    super.onTaskRemoved(rootIntent);
    Intent broadcastIntent = new Intent(context, RestartService.class);
    sendBroadcast(broadcastIntent);
}

private Notification buildForegroundNotification(String message) {
    NotificationCompat.Builder b = new NotificationCompat.Builder(this, ChannelID);
    b.setOngoing(true)
            .setContentTitle("MyApp")
            .setContentText(message)
            .setSmallIcon(com.app.R.drawable.schloss_deutsch4);//dauerhafte Benachrichtigung in der Leiste
            //.setSmallIcon(1); //aufgrund eines Fehlers läuft der Dienst dauerhaft aber ohne Benachrichtigung

    return (b.build());
}

@RequiresApi(Build.VERSION_CODES.O)
private String createNotificationChannel(String channelId, String channelName, int importance) {
    android.app.NotificationChannel chan = new NotificationChannel(channelId,
            channelName, importance);
    chan.setLightColor(Color.BLUE);
    chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
    NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    service.createNotificationChannel(chan);
    return channelId;
}

所以我尝试在 onDestroy() 和 onTaskRemoved() 中使用 startForegroundService() 并删除 startBroadcast() 也尝试使用 startBroadcast() 并在 onCreate() 或 onStartCommand() 中使用 stopForeground(true) 停止通知。

想法:如果服务因为在后台运行而没有通知而被破坏,我将淡入通知,如果服务再次稳定,我将再次删除此通知。

有没有可能达到这个目标?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    • 2012-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多