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 официальная документация.

чтобы начать прослушивание обновлений местоположения

  1. подключиться к GoogleClient С помощью LocationServices.API API
  2. построить свой LocationRequest согласно вашим потребностям (см. док)
  3. вызов requestLocationUpdates() использование PendingIntent версия

перестать слушать

  1. подключиться к GoogleClient С помощью LocationServices.API API
  2. вызов 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% работает.

Я надеюсь, что это поможет по крайней мере кому-нибудь в будущем.