Android: сохранить сервис, когда приложение убито

Я хочу сохранить IntentService в фоновом режиме, даже когда приложение будет убито. И "убитым" я имею в виду нажатие на кнопку "домой" в течение долгого времени → видеть все запущенные приложения → удалять мое приложение в сторону → приложение убито ИЛИ нажать кнопку "назад" в течение длительного времени → приложение убито

Мой код выглядит следующим образом. В моей MainActivity:

Intent intent = new Intent(this, MyService.class);
this.startService(intent);

В моем MyService:

public class MyService extends IntentService {

@Override
protected void onHandleIntent(Intent intent) {
    System.out.println("MyService started");
    run();
}

private void run() {
    while (true){
        System.out.println("MyService still running");
        doSomething();
        waitSomeTime();
    }
}

}

Я вижу, что служба запускается, когда приложение открыто. Он по-прежнему работает, когда я скрою приложение через домашнюю кнопку. Он все еще работает, когда я закрываю приложение с помощью кнопки "Назад". Но он прекратится, если я убью его, как упоминалось выше. Как это решить?

Ответ 1

Если ваша Служба запущена вашим приложением, то на самом деле ваша служба работает в основном процессе. поэтому, когда приложение будет убито, сервис также будет остановлен. Итак, что вы можете сделать, отправьте трансляцию из метода onTaskRemoved вашей службы следующим образом:

 Intent intent = new Intent("com.android.ServiceStopped");
 sendBroadcast(intent);

и иметь широковещательный приемник, который снова запустит службу. Я попробовал. сервис перезапускается от всех видов убийств.

Ответ 2

Все ответы кажутся правильными, поэтому я продолжу и дам полный ответ здесь.

Во-первых, самый простой способ сделать то, что вы пытаетесь сделать, это запустить Broadcast в Android, когда приложение убивается вручную, и определить пользовательский BroadcastReceiver для запуска перезапуска службы после этого.

Теперь давайте перейдем к коду.


Создайте свой сервис в YourService.java

Обратите внимание на onCreate(), где мы onCreate() службу переднего плана по- разному для версий Build больше, чем Android Oreo. Это из-за строгих политик уведомления, введенных недавно, когда мы должны определить наш собственный канал уведомлений для их правильного отображения.

this.sendBroadcast(broadcastIntent); в onDestroy() есть оператор, который асинхронно отправляет широковещательную onDestroy() с именем действия "restartservice". Мы будем использовать это позже как триггер для перезапуска нашего сервиса.

Здесь мы определили простую задачу Timer, которая печатает значение счетчика каждую 1 секунду в Log, увеличивая при этом каждый раз, когда печатает.

public class YourService extends Service {
public int counter=0;

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
            startMyOwnForeground();
        else
            startForeground(1, new Notification());
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private void startMyOwnForeground()
    {
        String NOTIFICATION_CHANNEL_ID = "example.permanence";
        String channelName = "Background Service";
        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        assert manager != null;
        manager.createNotificationChannel(chan);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(2, notification);
    }


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


    @Override
    public void onDestroy() {
        super.onDestroy();
        stoptimertask();

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("restartservice");
        broadcastIntent.setClass(this, Restarter.class);
        this.sendBroadcast(broadcastIntent);
    }



    private Timer timer;
    private TimerTask timerTask;
    public void startTimer() {
        timer = new Timer();
        timerTask = new TimerTask() {
            public void run() {
                Log.i("Count", "=========  "+ (counter++));
            }
        };
        timer.schedule(timerTask, 1000, 1000); //
    }

    public void stoptimertask() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

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

Создайте широковещательный приемник для ответа на ваши определенные трансляции в Restarter.java

Вещание с именем действия "restartservice" которое вы только что определили в YourService.java, теперь должно вызвать метод, который перезапустит ваш сервис. Это делается с помощью BroadcastReceiver в Android.

Мы переопределяем встроенный метод onRecieve() в BroadcastReceiver чтобы добавить оператор, который перезапустит сервис. startService() не будет работать должным образом в Android Oreo 8.1 и выше, так как строгие фоновые политики вскоре прекратят работу службы после перезапуска после закрытия приложения. Поэтому мы используем startForegroundService() для более высоких версий и показываем непрерывное уведомление, чтобы поддерживать работу службы.

public class Restarter extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("Broadcast Listened", "Service tried to stop");
        Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(new Intent(context, YourService.class));
        } else {
            context.startService(new Intent(context, YourService.class));
        }
    }
}

Определите свой MainActivity.java для вызова службы при запуске приложения.

Здесь мы определяем отдельный isMyServiceRunning() для проверки текущего состояния фоновой службы. Если служба не запущена, мы запускаем ее с помощью startService().

Поскольку приложение уже запущено на переднем плане, нам не нужно запускать службу как службу переднего плана, чтобы предотвратить ее прекращение.

Обратите внимание, что в onDestroy() мы специально вызываем stopService(), поэтому наш переопределенный метод вызывается. Если бы это не было сделано, то служба была бы onDestroy() автоматически после того, как приложение было убито, без вызова нашего модифицированного onDestroy() в YourService.java

public class MainActivity extends AppCompatActivity {
    Intent mServiceIntent;
    private YourService mYourService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mYourService = new YourService();
        mServiceIntent = new Intent(this, mYourService.getClass());
        if (!isMyServiceRunning(mYourService.getClass())) {
            startService(mServiceIntent);
        }
    }

    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                Log.i ("Service status", "Running");
                return true;
            }
        }
        Log.i ("Service status", "Not running");
        return false;
    }


    @Override
    protected void onDestroy() {
        stopService(mServiceIntent);
        super.onDestroy();
    }
}

Наконец зарегистрируйте их в своем AndroidManifest.xml

Все вышеперечисленные три класса должны быть отдельно зарегистрированы в AndroidManifest.xml.

Обратите внимание, что мы определяем intent-filter с именем действия как "restartservice" Restarter.java "restartservice" где Restarter.java зарегистрирован как receiver. Это гарантирует, что наш пользовательский BroadcastReciever вызывается всякий раз, когда система встречает широковещательную рассылку с заданным именем действия.

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <receiver
        android:name="Restarter"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="restartservice" />
        </intent-filter>
    </receiver>

    <activity android:name="MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        android:name="YourService"
        android:enabled="true" >
    </service>
</application>

Это должно теперь перезапустить ваш сервис снова, если приложение было убито из диспетчера задач. Эта служба будет продолжать работать в фоновом режиме, пока пользователь не Force Stop приложение из настроек приложения.

ОБНОВЛЕНИЕ: Престижность Dr.jacky для указания на это. Вышеупомянутый способ будет работать только в том случае, если onDestroy() службы, что может быть не так в определенные моменты времени, о которых я не знал. Благодарю.

Ответ 3

внутри команды onstart put START_STICKY... Эта служба не будет убивать, если она не выполняет слишком много задач, а ядро ​​хочет ее убить...

@Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("LocalService", "Received start id " + startId + ": " + intent);
            // We want this service to continue running until it is explicitly
            // stopped, so return sticky.
            return START_STICKY;
        }

Ответ 4

Причиной этого является то, что вы пытаетесь использовать IntentService. Вот строка из Документов API

IntentService выполняет следующие действия:

Остановка службы после того, как все начальные запросы были обработаны, поэтому вы никогда не нужно называть stopSelf().

Таким образом, если вы хотите, чтобы ваша служба выполнялась неограниченно, я предлагаю вам расширить класс службы. Однако это не гарантирует, что ваш сервис будет работать бесконечно. У вашего сервиса все равно будет шанс быть убитым ядром в состоянии низкой памяти, если он имеет низкий приоритет. Итак, у вас есть два варианта:
1) Держите его на переднем плане, вызвав метод startForeground().
2) Перезапустите службу, если она будет убита.  Вот часть примера из документов, где они говорят о перезапуске службы после ее смерти.

 public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the 
      // start ID so we know which request we're stopping when we finish the job 
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart 
      return START_STICKY;
  }  

Ответ 5

В вашем сервисе добавьте следующий код.

@Override
public void onTaskRemoved(Intent rootIntent){
    Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
    restartServiceIntent.setPackage(getPackageName());

    PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
    AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarmService.set(
    AlarmManager.ELAPSED_REALTIME,
    SystemClock.elapsedRealtime() + 1000,
    restartServicePendingIntent);

    super.onTaskRemoved(rootIntent);
 }

Ответ 6

Вы можете использовать android:stopWithTask="false" в манифесте, как показано ниже. Это означает, что даже если пользователь убивает приложение, удалив его из списка задач, ваша служба не остановится.

 <service android:name=".service.StickyService"
                  android:stopWithTask="false"/>

Ответ 7


vvcv iiiiiiiii я i я i я i я i

я я

III