Служба Android зупиняється, коли додаток закрито


78

Я запускаю послугу з моєї основної діяльності на Android наступним чином:

final Context context = base.getApplicationContext();
final Intent intent = new Intent(context, MyService.class);
startService(intent);

Коли я закриваю сторінку активності, виводячи її зі списку останніх програм, служба припиняє роботу та через деякий час перезапускається. Я не можу використовувати постійні служби зі сповіщеннями через мої вимоги до програми. Як я можу змусити службу НЕ перезапуститись чи вимкнутись і продовжувати працювати при виході з програми?



@kiran Проблема, з якою я стикаюся, полягає в тому, що служба перезапускається, коли діяльність закривається. Я шукаю спосіб, щоб служба постійно працювала (без перезапуску після завершення діяльності).
Бам,

1
Я відчуваю, що ми не можемо цього досягти. За низьких ресурсів ваша служба буде вбита. Найкраще, на що ми могли сподіватися, - це перезапуск. А на версії 4.4+ вбивство вбивством навіть не перезапустить службу. Прочитайте цю тему
Кіран,

Подумайте добре, чи дійсно вам потрібна ваша послуга буквально постійно: це погано для енергії та споживання пам'яті.
Poolie

Відповіді:


48

Я в тій же ситуації, дотепер я дізнався, коли додаток закрито, службу закривають ще й тому, що вони знаходяться в одному потоці, тому служба повинна бути в іншому потоці, щоб не закривати її, розгляньте це і подивіться на підтримку роботи служби за допомогою диспетчера будильників на прикладі http://www.vogella.com/articles/AndroidServices/article.html, таким чином, ваша служба не відображатиметься в сповіщенні.

нарешті, після всіх досліджень, які я startForeground()провів, я усвідомлюю, що найкращим вибором для довготривалої послуги є те , що вона зроблена саме для цього, і система насправді добре працює з вашою послугою.


10
@Bam У мене саме така проблема. Однак моя послуга - це послуга на перший план, і вона все одно вбивається, коли моя діяльність закрита. Я змінив аргумент ініціалізації наміру з контексту getApplicationContext () на getBaseContext (), і це вирішило проблему.
JayB

1
чи можу я використовувати getBaseContext () у pendingIntent?
Kairi San

1
замість використання notificationManager.notify(intent.getIntExtra("notification_Id", 0), builder.build());я повинен використовувати startForeground(intent.getIntExtra("notification_Id, 0), builder.build());? Я використовую NotificationCompat.Builder
Kairi San

startForeground () потрібна api 26 чи є рішення?
Midhilaj,

13

змусить вас обслуговуватись таким чином у вашому Mainifest

 <service
            android:name=".sys.service.youservice"
            android:exported="true"
        android:process=":ServiceProcess" />

тоді ваша служба буде працювати на іншому процесі з назвою ServiceProcess


якщо ви хочете, щоб ваша послуга ніколи не вмирала:

  1. onStartCommand () повертає START_STICKY

  2. onDestroy () -> запустити себе

  3. створити службу Deamon

  4. jin -> створити процес Native Deamon, ви можете знайти кілька проектів з відкритим кодом на github

  5. startForeground (), є спосіб запуститиForeground без сповіщення, загугніть його


12
Цілком впевнений, що стартувати в onDestroy буде розглядатися як боротьба з системою Android, що є поганою практикою.
Flare Cat

12

Це може вам допомогти. Можливо, я помиляюся, але мені здається, що це пов'язано з поверненням START_STICKYу ваш onStartCommand()метод. Ви можете уникнути повторного виклику служби, повернувшись START_NOT_STICKYнатомість.


4
Насправді служба перестає працювати, коли я виходжу або закриваю програму (а потім перезапускається). Я не хочу, щоб служба зупиняла / зупиняла, коли я виходжу / закриваю програму; Я хочу, щоб це продовжувало працювати. Я вважаю, що рішення має щось спільне із запуском служби в окремому процесі, але я насправді не впевнений.
Бам,

Працює прекрасна артекс, подяка
Manikandan K

8

Послуги іноді бувають досить складними.

Коли ви запускаєте послугу з якоїсь діяльності (або вашого процесу), служба, по суті, працює на тому ж процесі.

цитування з приміток розробника

Найбільша плутанина щодо класу Service насправді пов’язана з тим, чим він не є:

Послуга - це не окремий процес. Сам об’єкт Служби не означає, що він працює у власному процесі; якщо не вказано інше, він запускається в тому ж процесі, що і програма, частиною якої він є.

Послуга - це не нитка. Сам по собі спосіб не обробляти основний потік (щоб уникнути помилок програми, що не відповідають).

Отже, що це означає, якщо користувач відведе додаток від останніх завдань, він видалить ваш процес (сюди входять всі ваші дії тощо). Тепер візьмемо три сценарії.

По-перше, коли служба не має попереднього сповіщення.

У цьому випадку ваш процес буде вбито разом із вашою послугою.

По-друге, коли служба має попереднє сповіщення

У цьому випадку послугу не вбивають, як і процес

Третій сценарій Якщо служба не має попереднього сповіщення, вона все одно може продовжувати працювати, якщо програму закрито. Ми можемо зробити це, змусивши службу працювати в іншому процесі. (Однак я чув, як деякі люди говорять, що це може не спрацювати. Ви залишили вам спробувати це самостійно )

Ви можете створити службу в окремому процесі, включивши в свій маніфест атрибут нижче.

android: process = ": yourService"

або

android: process = "yourService" Назва процесу повинна починатися з нижнього регістру.

цитування з приміток розробника

Якщо ім'я, призначене цьому атрибуту, починається з двокрапки (':'), новий процес, приватний для програми, створюється, коли це потрібно, і служба запускається в цьому процесі. Якщо ім'я процесу починається з малого символу, служба буде запускатися в глобальному процесі з цим іменем за умови, що вона має на це дозвіл. Це дозволяє компонентам різних програм спільно використовувати процес, зменшуючи використання ресурсів.

це те, що я зібрав, якщо хтось є експертом, будь ласка, виправте мене, якщо я помиляюся :)



4

Основна проблема полягає в неможливості запустити службу, коли додаток закрито, ОС Android ( у деяких ОС ) вб’є службу з оптимізації ресурсів. Якщо ви не можете перезапустити службу, то зателефонуйте менеджеру тривоги, щоб запустити приймач так, Ось весь код, Цей код буде підтримувати вашу службу.

Маніфест - це,

         <service
            android:name=".BackgroundService"
            android:description="@string/app_name"
            android:enabled="true"
            android:label="Notification" />
        <receiver android:name="AlarmReceiver">
            <intent-filter>
                <action android:name="REFRESH_THIS" />
            </intent-filter>
        </receiver>

IN Main Activty запустити ясла тривоги таким чином,

String alarm = Context.ALARM_SERVICE;
        AlarmManager am = (AlarmManager) getSystemService(alarm);

        Intent intent = new Intent("REFRESH_THIS");
        PendingIntent pi = PendingIntent.getBroadcast(this, 123456789, intent, 0);

        int type = AlarmManager.RTC_WAKEUP;
        long interval = 1000 * 50;

        am.setInexactRepeating(type, System.currentTimeMillis(), interval, pi);

це буде називати ресивер, а ресивер є,

public class AlarmReceiver extends BroadcastReceiver {
    Context context;

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;

        System.out.println("Alarma Reciver Called");

        if (isMyServiceRunning(this.context, BackgroundService.class)) {
            System.out.println("alredy running no need to start again");
        } else {
            Intent background = new Intent(context, BackgroundService.class);
            context.startService(background);
        }
    }

    public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);

        if (services != null) {
            for (int i = 0; i < services.size(); i++) {
                if ((serviceClass.getName()).equals(services.get(i).service.getClassName()) && services.get(i).pid != 0) {
                    return true;
                }
            }
        }
        return false;
    }
}

І цей ресивер Alaram дзвонить один раз, коли додаток для Android відкривається і коли додаток закривається. ТАКОЖ послуга така,

public class BackgroundService extends Service {
    private String LOG_TAG = null;

    @Override
    public void onCreate() {
        super.onCreate();
        LOG_TAG = "app_name";
        Log.i(LOG_TAG, "service created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(LOG_TAG, "In onStartCommand");
        //ur actual code
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Wont be called as service is not bound
        Log.i(LOG_TAG, "In onBind");
        return null;
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Log.i(LOG_TAG, "In onTaskRemoved");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(LOG_TAG, "In onDestroyed");
    }
}

2

спробуйте це, це збереже роботу служби у фоновому режимі.

BackServices.class

public class BackServices extends Service{

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      // Let it continue running until it is stopped.
      Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
      return START_STICKY;
   }
   @Override
   public void onDestroy() {
      super.onDestroy();
      Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
   }
}

у вашому MainActivity onCreate опустіть цей рядок коду

startService(new Intent(getBaseContext(), BackServices.class));

Тепер служба буде працювати у фоновому режимі.


7
Проблема полягає в стилі: коли я вбиваю програму з меню нещодавно, служба перезапускається (onStartCommand знову викликається). Єдине, що мені допомогло, - це зробити послугу на перший план.
Бам

1

Використання одного і того ж процесу для послуги та активності та START_STICKY або START_REDELIVER_INTENT у службі - це єдиний спосіб мати можливість перезапустити службу при перезапуску програми, що трапляється, коли користувач, наприклад, закриває програму, а також коли система приймає рішення закрити його з міркувань оптимізації. Ви НЕ МОЖЕТЕ мати послугу, яка працюватиме постійно без будь-яких перерв. Це за задумом, смартфони не створені для запуску безперервних процесів протягом тривалого періоду часу. Це пов’язано з тим, що час автономної роботи є найвищим пріоритетом. Вам потрібно розробити свою послугу, щоб вона могла зупинятися в будь-який момент.


1

Ви повинні додати цей код у свій клас Service, щоб він обробляв випадки, коли ваш процес вбивається

 @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);
    }

0

Чому б не використовувати IntentService?

IntentService відкриває новий потік, крім основного потоку, і працює там, таким чином закриття програми не вплине на це

Зауважте, що IntentService запускає onHandleIntent (), і коли служба завершиться, перевірте, чи відповідає вона вашим потребам. http://developer.android.com/reference/android/app/IntentService.html


2
Насправді моєю вимогою було мати постійно працюючий фоновий сервіс. Початок служби як Foreground Service вирішив це для мене.
Бам

0

Найкращим рішенням є використання адаптера синхронізації в Android для запуску служби. Створіть адаптер синхронізації та запустіть службу виклику їх .. всередині методу onPerformSync. для створення облікового запису синхронізації перейдіть за цим посиланням https://developer.android.com/training/sync-adapters/index.html

Чому SyncAdapter? Відповідь: Тому що раніше ви раніше запускали службу, використовуючи контекст вашого додатка. так що, коли процес вашого додатка вбивається (коли ви видаляєте його з диспетчера завдань або ОС вбиває через брак ресурсів), у цей час ваша служба також буде видалена. SyncAdapter не буде працювати в потоці програми .. тому, якщо ви зателефонуєте всередині нього .. послуга більше не буде видалена .., якщо ви не напишете код для її видалення.


0
<service android:name=".Service2"
            android:process="@string/app_name"
            android:exported="true"
            android:isolatedProcess="true"
            />

Заявіть про це у своєму маніфесті. Дайте користувацькому імені ваш процес і зробіть цей процес ізольованим та експортованим.


0

Запустити службу намірів буде простіше. Послуга у створенні потоку в програмі, але вона все ще в додатку.


-5

Просто перевизначте метод onDestroy у першій видимій діяльності, наприклад, після сплеску у вас є домашня сторінка, і під час перенаправлення з сплеску на домашню сторінку ви вже закінчили сплеш. так надіньте знищувати на домашній сторінці. і зупинити обслуговування таким методом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.