Як завжди запускати службу у фоновому режимі?


76

Зараз я створюю програму, подібну до вбудованої програми для SMS.

Що мені потрібно:

  • послуга, яка завжди працює у фоновому режимі
  • кожні 5 хв. служба перевіряє поточне місцезнаходження пристрою та викликає веб-службу
  • якщо певні критерії дотримані, служба повинна генерувати сповіщення (як і додаток SMS)
  • коли натискається сповіщення, користувач переходить до програми (як і додаток для SMS)
  • після встановлення програми слід запустити службу
  • при перезавантаженні пристрою послугу слід запустити

Те, що я пробував:
- запуск звичайного сервісу, який працював чудово, доки Android не вб’є службу,
- використання AlarmManager, щоб зробити 5 хв. інтервальний дзвінок до служби. Але я не зміг зробити цю роботу.

Відповіді:


34

послуга, яка завжди працює у фоновому режимі

Як ви виявили, це неможливо в реальному сенсі цього терміну. Це також поганий дизайн .

кожні 5 хв. служба перевіряє поточне місцезнаходження пристрою та викликає веб-службу

Використовуйте AlarmManager.

за допомогою AlarmManager зробіть 5 хв. інтервальний дзвінок до служби. Але я не зміг зробити цю роботу.

Ось зразок проекту, який показує, як ним користуватися, разом із використанням, WakefulIntentServiceщоб ви не спали, намагаючись виконати всю роботу з веб-послугами.

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


я використав цей зразок проекту, і я поклав журнал і чекаю 10 хвилин, нічого не працює? мені потрібно зателефонувати до будь-якої служби, щоб її запустити?
Самір Мангролія

2
@SamirMangroliya: Якщо ви подивитесь на дату відповіді, то помітите, що їй більше 3 років. Цей приклад був написаний для Android 2.x, і вам потрібно буде перезавантажити пристрій / емулятор, щоб запустити будильники. Тепер для Android 3.1+ навіть цього недостатньо - вам потрібно запустити діяльність, перш ніж завантажувальний ресивер стане ефективним. Я б порекомендував вам перейти на github.com/commonsguy/cw-omnibus/tree/master/AlarmManager/..., що є тим самим базовим проектом, але більш сучасним. Запустіть його та переконайтеся, що активність виконується (показано а Toast), і будильники почнуться.
CommonsWare

привіт, якщо я зателефоную до PollReceiver.scheduleAlarms (це); в основній діяльності, а коли я закриваю програму, то перезавантажую (припустимо, за одну годину я відкриваю програму більше 15 разів), то створює будильник щоразу протягом 5 хвилин?
Самір Мангролія

1
@SamirMangroliya: Планування будильника на еквівалент PendingIntentскасує будь-який існуючий сигнал тривоги щодо цього PendingIntent. Крім цього, будь ласка, майте на увазі, що це приклад з книги, і вона навмисно не намагається розглянути всі сценарії.
CommonsWare

Я знайшов наступний допис, який стверджує, що це можливо: blogmobile.itude.com/2012/02/20/… . Чи працює такий підхід?
Mark Vincze

26

Один із моїх додатків робить щось дуже подібне. Щоб збудити послугу після певного періоду, я рекомендуюpostDelayed()

Майте поле обробника:

private final Handler handler = new Handler();

та оновлення Runnable

private final Runnable refresher = new Runnable() {
  public void run() {
    // some action
  }
};

Ви можете запускати свої сповіщення в режимі запуску.

На побудові служби та після кожного виконання її запускайте так:

handler.postDelayed(refresher, /* some delay in ms*/);

У onDestroy()видалити публікацію

handler.removeCallbacks(refresher);

Щоб запустити послугу під час завантаження, вам потрібен автозапуск. Це стосується вашого маніфесту

<receiver android:name="com.example.ServiceAutoStarter">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
  </receiver>

і ServiceAutoStarterвиглядає так:

public class ServiceAutoStarter extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    context.startService(new Intent(context, UpdateService.class));
  }
}

Зупинити ОС від вбивства служби досить складно. Крім того, у вашому додатку може RuntimeExceptionстатися помилка і збій, або ваша логіка може зупинитися.

У моєму випадку це, здається, допомагало завжди оновлювати послугу на екрані за допомогою BroadcastReceiver. Отже, якщо ланцюжок оновлень зупиниться, вона буде воскрешена, коли користувач користується своїм телефоном.

У сервісі:

private BroadcastReceiver screenOnReceiver; 

До ваших послуг onCreate()

screenOnReceiver = new BroadcastReceiver() {

  @Override
  public void onReceive(Context context, Intent intent) {
    // Some action
  }
};

registerReceiver(screenOnReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));

Тоді незареєстрований вашої служби на onDestroy()з

unregisterReceiver(screenOnReceiver);

2
Зазвичай мені подобаються ваші відповіді, але ця ... не дуже. :-( Наприклад, ви пропонуєте службі зареєструвати одержувача для воскресіння після вбивства. Одержувач буде вбитий разом із службою; отже, це не повинно мати ефекту. Більше того, вся концепція вічної послуги жахлива на Android і саме тому користувачі відбиваються за допомогою вбивць завдань або екрану запущених служб у додатку Налаштування. Дуже мало випадків, коли потрібна справді вічна послуга - переважної більшості випадків AlarmManagerбуде достатньо.
CommonsWare

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

3

Ви можете зробити це за допомогою простої реалізації:

public class LocationTrace extends Service implements LocationListener{

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
    private static final int TWO_MINUTES = 100 * 10;

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 10; // 30 seconds

    private Context context;

    double latitude;
    double longitude;

    Location location = null;
    boolean isGPSEnabled = false;
    boolean isNetworkEnabled = false;
    protected LocationManager locationManager;


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        this.context = this;
        get_current_location();
//      Toast.makeText(context, "Lat"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
        return START_STICKY;
    }


    @Override
    public void onLocationChanged(Location location) {
        if((location != null) && (location.getLatitude() != 0) && (location.getLongitude() != 0)){

            latitude = location.getLatitude();
            longitude = location.getLongitude();

            if (!Utils.getuserid(context).equalsIgnoreCase("")) {
                Double[] arr = { location.getLatitude(), location.getLongitude() };

               // DO ASYNCTASK
            }
        }

    }


    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    /*
    *  Get Current Location
    */
    public Location get_current_location(){

        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

        isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

        isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        if(!isGPSEnabled && !isNetworkEnabled){



        }else{
            if (isGPSEnabled) {

                if (location == null) {
                    locationManager.requestLocationUpdates(
                            LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                    if (locationManager != null) {
                        location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
        //                  Toast.makeText(context, "Latgps"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            }
            if (isNetworkEnabled) {

                locationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER,
                        MIN_TIME_BW_UPDATES,
                        MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                if (locationManager != null) {

                    if (location != null) {
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
        //              Toast.makeText(context, "Latgps1"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
                    }
                }
            }
        }

        return location;
    }


    public double getLatitude() {
        if(location != null){
            latitude = location.getLatitude();
        }
        return latitude;
    }

    public double getLongitude() {
         if(location != null){
             longitude = location.getLongitude();
         }

        return longitude;
    }


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

    @Override
    public void onDestroy() {
        if(locationManager != null){
            locationManager.removeUpdates(this);
        }
        super.onDestroy();
    }

}

Ви можете розпочати обслуговування з цього:

/*--Start Service--*/
startService(new Intent(Splash.this, LocationTrace.class));

У маніфесті:

 <service android:name=".LocationTrace">
            <intent-filter android:priority="1000">
                <action android:name="android.location.PROVIDERS_CHANGED"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
  </service>

1

за допомогою цих трьох кроків ви можете прокидатися кожні 5 хвилин більшість пристроїв Android:

1. встановіть альтернативний AlarmManager для різних API:

AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(getApplicationContext(), OwnReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 0, i, 0);

if (Build.VERSION.SDK_INT >= 23) {
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}
else if (Build.VERSION.SDK_INT >= 19) {
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}

2. побудуйте власний статичний BroadcastReceiver:

public static class OwnReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

       //do all of your jobs here

        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent i = new Intent(context, OwnReceiver.class);
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);

        if (Build.VERSION.SDK_INT >= 23) {
            am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
        }
        else if (Build.VERSION.SDK_INT >= 19) {
            am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
        } else {
            am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
        }
    }
}

3. додати <receiver>до AndroidManifest.xml:

<receiver android:name=".OwnReceiver"  />

1
Статичні приймачі не можна ініціалізувати в Маніфесті, як це, скоріше, ви повинні використовувати <приймач android: name = "className $ OwnReceiver" />
Psycho

0

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

@Override
        public void onTaskRemoved(Intent rootIntent) {
            Log.d(TAG, "onTaskRemoved: removed");
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(System.currentTimeMillis() + 10000);
((AlarmManager) getSystemService(Context.ALARM_SERVICE)).setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), PendingIntent.getService(getApplicationContext(), 0, new Intent(getApplicationContext(), RegisterReceiverService.class), 0));
            super.onTaskRemoved(rootIntent);
        }

Отже, як тільки ви вб'єте програму, служба буде запущена через 10 секунд. Примітка: Якщо ви хочете скористатися

AlarmManager.ELAPSED_REALTIME_WAKEUP

мінімальний час, через який ви зможете перезапустити послугу, буде 15 хвилин.

Пам'ятайте, вам також потрібно запустити службу при перезавантаженні за допомогою BroadcastReceiver.

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