Android-Сервис не перезапускается в lollipop
в моем приложении я использую службу на основе местоположения в фоновом режиме. Поэтому мне нужно перезапустить службу, когда она будет уничтожена.
но я получил это сообщение в logcat
ложная смерть для ProcessRecord{320afaf6 20614:com.с.crm: my_odoo_gps_service / u0a391}, curProc для 20614: null
мои услуги onTaskRemoved
@Override
public void onTaskRemoved(Intent rootIntent) {
System.out.println("onTaskRemoved called");
Intent restartServiceIntent = new Intent(App.getAppContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent =
PendingIntent.getService(App.getAppContext(), 1, restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService =
(AlarmManager) App.getAppContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
}
моя служба onDestroy
@Override
public void onDestroy() {
System.out.println("destroy service");
super.onDestroy();
wakeLock.release();
}
моя служба onStartCommand
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
I не знаю, в чем ошибка. Я искал как в google , так и в stackoverflow. Все они ссылаются сервис.START_STICKY. но я уже использовал его.
тот же перезапуск службы работает в KitKat, но с некоторой задержкой(~5 минут).
любая помощь приветствуется.
7 ответов
вы можете перезапустить его с помощью BroadcasteReceiver
который обрабатывает трансляцию, отправленную из onDestroy()
службы.
для этого:
StickyService.java
public class StickyService extends Service
{
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
sendBroadcast(new Intent("IWillStartAuto"));
}
@Override
public void onDestroy() {
super.onDestroy();
sendBroadcast(new Intent("IWillStartAuto"));
}
}
RestartServiceReceiver.java
public class RestartServiceReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context.getApplicationContext(), StickyService.class));
}
}
объявить компоненты в манифесте:
<service android:name=".StickyService" >
</service>
<receiver android:name=".RestartServiceReceiver" >
<intent-filter>
<action android:name="IWillStartAuto" >
</action>
</intent-filter>
</receiver>
надеюсь, это поможет вам.
код onTaskRemoved
предотвращает запуск системы killProcess
команды. Задержка на Kitkat
вызвано использованием alarmService.set
, которая составляет неточный из API 19. Использовать setExact.
если у вас service
что вы хотите сохранить в живых, рекомендуется прикрепить notification
и это foreground
. Таким образом, вероятность того, что его убьют, будет снижена.
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;
import android.support.v7.app.NotificationCompat;
import java.io.File;
import java.io.IOException;
import activity.MainActivity;
import activity.R;
import fragment.MainFragment;
public class MyService extends Service {
public static final int NOTIFICATION_CODE = 1;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(NOTIFICATION_CODE, getNotification());
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
stopForeground(true);
super.onDestroy();
}
@Override
public boolean stopService(Intent name) {
return super.stopService(name);
}
/**
* Create and return a simple notification.
*/
private Notification getNotification() {
Notification notification;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setColor(getResources()
.getColor(R.color.material_deep_teal_500))
.setAutoCancel(true);
notification = builder.build();
notification.flags = Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTO_CANCEL;
return notification;
}
}
вы можете изменить этот код, чтобы удовлетворить ваши потребности, но это базовая структура для запуска службы переднего плана. Который перезапускается, если его убивают.
Как вы проверяете issocketalive, что сокет подключен или нет ? если создается исключение sockettimeoutexception, попробуйте установить getinputstream и getoutputstream. другая проблема, которая может быть socket не закрыта должным образом. Поэтому, если возможно, поместите свой код сокета здесь
это работает для меня
добавьте этот атрибут в android: allowBackup= "false" в файле манифеста в теге приложения.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="false"
tools:replace="android:allowBackup">
</application>
</manifest>
идея иметь службу всегда работает в фоновом режиме в Android просто неправильно 99% раз.
система должна "выключение" CPU, и переключиться на низкий профиль использования батареи.
вы говорите, что у вас есть служба на основе местоположения. Я предполагаю, что вы используете Google Play Services FusedLocationProvider
, если не вы должны.
на FusedLocationProvider
позволяет зарегистрироваться для изменения местоположения с помощью PendingIntent
. Смысл вашего сервисы не должны работать все время, им просто нужно зарегистрироваться для изменения местоположения, а затем реагировать, когда новое местоположение приходит и делает свои вещи.
посмотреть FusedLocationProviderApi
официальная документация.
чтобы начать прослушивание обновлений местоположения
- подключиться к
GoogleClient
С помощьюLocationServices.API
API - построить свой
LocationRequest
согласно вашим потребностям (см. док) - вызов
requestLocationUpdates()
использованиеPendingIntent
версия
перестать слушать
- подключиться к
GoogleClient
С помощьюLocationServices.API
API - вызов
removeLocationUpdates()
используя то же самоеPendingIntent
ваш PendingIntent может запустить другую службу для обработки нового местоположения.
например, делая это из службы:
public void startMonitoringLocation(Context context) {
GoogleApiClient client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.build()
ConnectionResult connectionResult = mApiClient.blockingConnect();
if (connectionResult.isSuccess()) {
LocationServices.FusedLocationApi
.requestLocationUpdates(client, buildLocationRequest(), buildPendingIntent(context));
} else {
handleConnectionFailed(context);
}
}
после этого обслуживание может немедленно остановить.
при первом запуске этого кода произойдет сбой. Этот подключение к клиенту google обычно требует от пользователя выполнения некоторых действий. The ConnectionResult.hasResolution()
метод вернет true, если это так. В противном случае причина в чем-то другом и вы не можете оправиться от этого. То есть единственное, что вы можете сделать, это сообщить пользователю, что функция не будет работать или иметь хороший резерв.
на ConnectionResult.getResolution()
дать вам PendingIntent
вам нужно использовать Activity
и startIntentSenderForResult()
метод Activity
чтобы разрешить это намерение. Таким образом, вы создадите Notification
начиная ваш Activity
чтобы решить эту проблему, и в конце концов вызовите свой Service
снова.
обычно я просто начинаю Activity
стремятся сделать всю работу. Это намного проще, но вы не хотите называть connectBlocking()
в нем. Проверьте этой о том, как это сделать.
вы можете спросить, почему не запрашивать обновления местоположения непосредственно в Activity
. Это на самом деле прекрасно, если вам не нужен монитор местоположения для автоматического запуска с устройства, даже если пользователь этого не сделал явно открыл приложение.
<receiver android:name=".BootCompletedBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
таким образом, вы можете просто запустить службу для подключения и запросить обновления местоположения при перезагрузке устройства.
пример того, как вы можете создать свой запрос местоположения:
public LocationRequest buildLocationRequest() {
LocationRequest locRequest = LocationRequest.create();
// Use high accuracy
locRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// how often do you need to check for the location
// (this is an indication, it's not exact)
locRequest.setInterval(REQUIRED_INTERVAL_SEC * 1000);
// if others services requires the location more often
// you can still receive those updates, if you do not want
// too many consider setting this lower limit
locRequest.setFastestInterval(FASTEST_INTERVAL_SEC * 1000);
// do you care if the user moved 1 meter? or if he move 50? 1000?
// this is, again, an indication
locRequest.setSmallestDisplacement(SMALLEST_DISPLACEMENT_METERS);
return locRequest;
}
и Ваше ожидающее намерение:
public PendingIntent buildPendingIntent(Context context) {
Intent intent = new Intent(context, LocationUpdateHandlerService.class);
intent.setAction(ACTION_LOCATION_UPDATE);
intent.setPackage(context.getPackageName());
return PendingIntent.getService(context, REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
код LocationUpdateHandlerService
может быть IntentService
если вам нужно сделать работу в фоновом режиме:
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(FusedLocationProviderApi.KEY_LOCATION_CHANGED)) {
Location location = extras.getParcelable(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
handleLocationChanged(location);
} else {
Log.w(TAG, "Didn't receive any location update in the receiver");
}
}
}
но также может быть трансляция или что-нибудь, что вам подходит.
наконец-то я добился с помощью Evernote JobService
ссылка Github -https://github.com/evernote/android-job
Шаг 1: добавьте зависимость evernote jobservice
implementation 'com.evernote:android-job:1.3.0-alpha03'
Шаг 2: Создайте DemoJobCreator.класс java
public class DemoJobCreator implements JobCreator {
@Override
@Nullable
public Job create(@NonNull String tag) {
switch (tag) {
case DemoSyncJob.TAG:
return new DemoSyncJob();
default:
return null;
}
}
}
Шаг 3: Создайте DemoSyncJob.класс java
public class DemoSyncJob extends Job {
public static final String TAG = ">>>> job_demo_tag";
@Override
@NonNull
protected Result onRunJob(Params params) {
// run your job here
Log.d(TAG, "onRunJob: ");
if(!isMyServiceRunning(this.getContext(), TestService.class)){
Intent intent=new Intent(context,TestService.class);
context.startService(intent);
}
scheduleJob();
return Job.Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(DemoSyncJob.TAG)
.setExecutionWindow(2_000L, 2_000L)
//.setPeriodic(900000) -> recommended. but it will work after 15 min (if you used this no need scheduleJob(); inside onRunJob();)
.build()
.schedule();
}
public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
try {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
}catch (Exception e){
Log.e(TAG, "isMyServiceRunning: ",e );
}
return false;
}
}
Шаг 4: в файле приложения (если он недоступен создайте его) и добавьте следующую строку в onCreate()
JobManager.create(this).addJobCreator(new DemoJobCreator());
Шаг 5: Наконец, запустите JobService в своей деятельности
DemoSyncJob.scheduleJob();
эта служба JobService будет проверять запущенную службу или нет (каждые 2 секунды), если служба не запущена, она перезапустит службу.
отказ от ответственности: это может быть неправильное решение. Но он 100% работает.
Я надеюсь, что это поможет по крайней мере кому-нибудь в будущем.