Как мы можем предотвратить уничтожение Сервиса ОС?
Я использую Service
в моем приложении, и оно должно работать, пока мое приложение не будет удалено, но проблема в том, что оно будет убито ОС.
Как мы можем предотвратить его уничтожение ОС? Или, если он будет убит, мы сможем перезапустить этот сервис программно?
11 ответов
Вы можете запустить сервис на переднем плане, используя startForeground ().
Сервис переднего плана - это сервис, который считается тем, о чем пользователь активно осведомлен, и, следовательно, не является кандидатом в систему для уничтожения при нехватке памяти.
Но имейте в виду, что служба переднего плана должна предоставлять уведомление для строки состояния (см. Здесь), и что уведомление не может быть отклонено, если служба не остановлена или не удалена с переднего плана.
Примечание. Это по-прежнему не гарантирует, что служба не будет уничтожена в условиях крайне низкого уровня памяти. Это только снижает вероятность быть убитым.
Недавно я озадачился той же проблемой, что и ваша. Но сейчас я нашел хорошее решение. Прежде всего, вы должны знать, что, даже если ваш сервис был убит ОС, метод onCreate вашего сервиса будет вызываться ОС в ближайшее время. Так что вы можете сделать что-то с помощью метода onCreate, например:
@Override
public void onCreate() {
Log.d(LOGTAG, "NotificationService.onCreate()...");
//start this service from another class
ServiceManager.startService();
}
@Override
public void onStart(Intent intent, int startId) {
Log.d(LOGTAG, "onStart()...");
//some code of your service starting,such as establish a connection,create a TimerTask or something else
}
содержимое "ServiceManager.startService()":
public static void startService() {
Log.i(LOGTAG, "ServiceManager.startSerivce()...");
Intent intent = new Intent(NotificationService.class.getName());
context.startService(intent);
}
Тем не менее, это решение доступно только для ситуации, когда GC убивает ваш сервис. Иногда наш сервис может быть убит пользователем с помощью Program Manager. В этой ситуации ваши запросы будут убиты, а ваш сервис никогда не будет повторно создан. Таким образом, ваш сервис не может быть перезапущен. Но хорошая новость в том, что когда PM убьет ваш сервис, он вызовет ваш метод onDestroy. Так что мы можем что-то сделать с этим методом.
@Override
public void onDestroy() {
Intent in = new Intent();
in.setAction("YouWillNeverKillMe");
sendBroadcast(in);
Log.d(LOGTAG, "onDestroy()...");
}
Строка "YouWillNeverKillMe" является настраиваемым действием. Самое важное в этом методе - не добавлять код перед отправкой трансляции. Поскольку система не будет ждать завершения onDestroy(), вы должны отослать трансляцию как можно скорее. Затем зарегистрируйте получателя в manifast.xml:
<receiver android:name=".app.ServiceDestroyReceiver" >
<intent-filter>
<action android:name="YouWillNeverKillMe" >
</action>
</intent-filter>
</receiver>
Наконец, создайте BroadcastReceiver и запустите службу в методе onReceive:
@Override
public void onReceive(Context context, Intent intent) {
Log.d(LOGTAG, "ServeiceDestroy onReceive...");
Log.d(LOGTAG, "action:" + intent.getAction());
Log.d(LOGTAG, "ServeiceDestroy auto start service...");
ServiceManager.startService();
}
Надеюсь, что это будет полезно для вас, и извините за мой плохой письменный английский.
Метод переопределения onStartCommand()
в вашем классе обслуживания и просто вернуть START_STICKY
(как предложено "Это не пусто"). Это все, что вам нужно. Если процесс, выполняющий вашу службу, будет убит (например, из-за нехватки памяти), система Android перезапустит его автоматически (обычно с некоторой задержкой, например, 5 секунд).
Не использовать onStart()
больше как предложено в другом ответе, это устарело.
Использование
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//**Your code **
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
ref Документация жизненного цикла Сервиса.
Изменить добавленный метод.
Насколько я знаю, onDestroy()
будет вызываться только тогда, когда служба явно остановлена (Force Stop). Но этот метод не будет вызываться в случае, если служба будет убита ОС / смахивает список недавних приложений. В этих случаях другой обработчик событий с именем onTaskRemoved(Intent)
вызывается. Это связано с дефектом в Android 4.3-4.4 по ссылке здесь. Попробуйте использовать следующий код:-
public void onTaskRemoved(Intent intent){
super.onTaskRemoved(intent);
Intent intent=new Intent(this,this.getClass());
startService(intent);
}
Я нашел другое решение проблемы, которое гарантирует, что ваша служба будет всегда жива. В моем случае эта схема решает также проблему с FileObserver, который прекращает работу через некоторое время.
- Используйте действие (StartServicesActivity), чтобы запустить службу (FileObserverService) в качестве службы Foreground.
- Используйте класс BroadcastReceiver (в примере CommonReceiver), чтобы перезапустить службу в некоторых особых ситуациях и в случае, если она была убита.
Я использовал этот код в своем приложении "Автоматически отправлять фотографии по электронной почте" https://play.google.com/store/apps/details?id=com.alexpap.EmailPicturesFree
Вот класс CommonReceiver.
public class CommonReceiver extends BroadcastReceiver {
public void onReceive(Context paramContext, Intent paramIntent)
{
paramContext.startService(new Intent(paramContext, FileObserverService.class));
}
}
Вот его определение в AndroidManifest.xml непосредственно перед закрытием тега приложения.
<receiver android:name="com.alexpap.services.CommonReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter>
</receiver>
Запустите службу в деятельности StartServicesActivity.
Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
StartServicesActivity.this.startService(iFileObserver);
Вот метод onStartCommand() службы.
public int onStartCommand(Intent intent, int flags, int startId) {
int res = super.onStartCommand(intent, flags, startId);
/*** Put your code here ***/
startServiceForeground(intent, flags, startId);
return Service.START_STICKY;
}
public int startServiceForeground(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, StartServicesActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("File Observer Service")
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(300, notification);
return START_STICKY;
}
Я протестировал этот код с помощью приложения Task Killer, и каждый раз, когда служба была убита, она почти сразу же перезапускалась (выполняет onStartCommand()). Он также перезагружается при каждом включении телефона и после перезагрузки. Я использую этот код в своем приложении, которое отправляет каждое изображение, которое вы делаете с помощью телефона, по электронной почте, чтобы предварительно определить список писем. Отправка электронной почты и список получаемых писем задаются в другом действии и хранятся в общих настройках. Я сделал около 100 фотографий за несколько часов, и все они были отправлены правильно для получения электронных писем.
@Override
public void onDestroy() {
super.onDestroy();
startService(new Intent(this, YourService.class));
}
напишите код выше в вашем сервисе, и ваш сервис никогда не остановится, даже если пользователь захочет его уничтожить или он хочет убить его, он никогда не убьет, пока ваше приложение не будет удалено с вашего устройства
Вы можете попытаться запустить службу повторно, например, каждые 5 секунд. Таким образом, когда ваш сервис работает, он будет выполнять onStartCommand() каждые 5 секунд. Я проверил эту схему, и она очень надежна, но, к сожалению, она немного увеличивает накладные расходы телефона. Вот код в вашей деятельности, где вы запускаете службу.
Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
PendingIntent pendingIntentFileObserver = PendingIntent.getService(StartServicesActivity.this, 0, iFileObserver, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
Date now = new Date();
//start every 5 seconds
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTime(), 5*1000, pendingIntentFileObserver);
А вот onStartCommand() службы.
//class variable
public static boolean isStarted = false;
public int onStartCommand(Intent intent, int flags, int startId) {
int res = super.onStartCommand(intent, flags, startId);
//check if your service is already started
if (isStarted){ //yes - do nothing
return Service.START_STICKY;
} else { //no
isStarted = true;
}
/**** the rest of your code ***/
return Service.START_STICKY;
}
Сначала создайте сервис в другом процессе, и напишите вещатель, который работает в рекурсии во временных интервалах
protected CountDownTimer rebootService = new CountDownTimer(9000, 9000) {
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
sendBroadcast(reboot);
this.start();
Log.d(TAG, "rebootService sending PREVENT AUTOREBOT broadcast");
}
};
После этого зарегистрируйте получателя широковещательной передачи в основном процессе также с рекурсией таймера, которая запускается после получения первой широковещательной передачи от службы.
protected static class ServiceAutoRebooter extends BroadcastReceiver {
private static ServiceAutoRebooter instance = null;
private RebootTimer rebootTimer = null;
private static ServiceAutoRebooter getInstance() {
if (instance == null) {
instance = new ServiceAutoRebooter();
}
return instance;
}
public class RebootTimer extends CountDownTimer {
private Context _context;
private Intent _service;
public RebootTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
_context.startService(_service);
this.cancel();
Log.d(TAG, "Service AutoRebooted");
}
}
@Override
public void onReceive(Context context, Intent intent) {
if (rebootTimer == null) {
Log.d(TAG, "rebootTimer == null");
rebootTimer = new RebootTimer(10000, 10000);
rebootTimer._context = context;
Intent service = new Intent(context, SomeService.class);
rebootTimer._service = service;
rebootTimer.start();
} else {
rebootTimer.cancel();
rebootTimer.start();
Log.d(TAG, "rebootTimer is restarted");
}
}
}
Служба будет автоматически перезагружена, если время в RebootTimer (основной процесс) истечет, а это означает, что широковещательная передача "ПРЕДОТВРАТ АВТОРЕБОТ" от службы не наступила
Я нашел решение.... поздний ответ, но я хотел ответить...
мы можем отправить трансляцию в ondestroy службы и создать получателя, который получает трансляцию и снова запускает службу... когда она уничтожается по каким-либо причинам...
Пожалуйста, попробуйте следующее:
final Messenger mMessenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
default:
super.handleMessage(msg);
}
}
}
@Override
public void onCreate() {
super.onCreate();
makeServiceForeground();
}
@Override
public IBinder onBind(Intent arg0) {
return mMessenger.getBinder();
}
private void makeServiceForeground() {
IActivityManager am = ActivityManagerNative.getDefault();
try {
am.setProcessForeground(onBind(null), android.os.Process.myPid(), true);
} catch (RemoteException e) {
Log.e("", "cant set to foreground" + e.toString());
}
}
также необходимо добавить в manifest.xml
<uses-permission android:name="android.permission.SET_PROCESS_LIMIT"/>