簡體   English   中英

Android后台服務會導致大量崩潰

[英]Android Background Service causes a lot of crashes

我有一個即時服務,可引起大量事故(每天約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我的服務才不會消失。

同樣由於某些原因,服務無法在啟動時啟動。

如果您返回START_REDELIVER_INTENTSTART_STICKY和相關標志,則Android框架將使服務保持活動狀態(或為您重新創建)。

doc中對START_REDELIVER_INTENT的描述:

onStartCommand(Intent, int, int)返回的常量:如果此服務的進程在啟動時被殺死(從onStartCommand(Intent, int, int))返回之后onStartCommand(Intent, int, int)) ,則它將安排重新啟動,並計划最后一次交付的Intent通過onStartCommand(Intent, int, int)重新傳遞給它。 在服務調用stopSelf(int)並將其提供給onStartCommand(Intent, int, int)的開始ID之前,將一直計划該Intent進行重新交付

因此,我建議使用Android的框架行為來實現,方法是通過返回START_REDELIVER_INTENT使服務由於某種原因被殺死后再次運行,而不調用Service.stopSelf()Context.stopService()

但是,如果您需要使用廣播機制來執行此操作,則可以只返回START_NOT_STICKY以防止系統為您執行此操作。 並且系統不會重新創建服務,這將導致多次調用onStartCommand()並導致崩潰。

有關onStartCommand()更多詳細信息,請參考文檔

更新:

如上所述,我現在要返回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)停止通知。

想法是:如果服務因為在后台運行而沒有通知而被破壞,我將淡入通知,如果服務再次穩定,則將再次刪除該通知。

有沒有可能實現這一目標?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM