web-dev-qa-db-fra.com

Comment toujours exécuter un service en arrière-plan?

Je suis en train de créer une application similaire à l'application intégrée SMS.

Ce dont j'ai besoin: 

  • un service qui fonctionne toujours en arrière-plan 
  • toutes les 5 min. le service vérifie l'emplacement actuel de l'appareil et appelle un service Web 
  • si certains critères sont remplis, le service doit générer une notification (tout comme l'application SMS) 
  • lorsque l'utilisateur clique sur la notification, l'utilisateur est amené à l'application (tout comme l'application SMS) 
  • lorsque l'application est installée, le service doit être démarré 
  • lorsque le périphérique est redémarré, le service doit être démarré 

Ce que j'ai essayé
- exécuter un service régulier qui fonctionnait parfaitement jusqu'à ce que Android tue le service
- en utilisant AlarmManager pour faire les 5 min. appel d'intervalle à un service. Mais je n'ai pas pu faire ce travail.

72
Thomas H

un service toujours actif l'arrière-plan

C'est impossible dans le vrai sens du terme, comme vous l'avez découvert. C'est aussi mauvais design .

toutes les 5 min. le service vérifie le emplacement actuel de l'appareil et appelle un service web

Utilisez AlarmManager.

en utilisant AlarmManager, faites le 5 min. appel d'intervalle à un service. Mais je n'a pas pu faire ce travail.

Voici un exemple de projet montrant comment en utiliser un, ainsi que/ WakefulIntentService afin que vous restiez éveillé tout en essayant de faire tout le travail du Web.

Si vous avez toujours des problèmes avec cela, posez une nouvelle question sur les choses spécifiques que vous rencontrez avec AlarmManager qui vous causent du chagrin.

33
CommonsWare

Une de mes applications fait quelque chose de très similaire. Pour réactiver le service après une période donnée, je recommande postDelayed()

Avoir un champ de gestionnaire:

private final Handler handler = new Handler();

et un recyclage Runnable

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

Vous pouvez déclencher vos notifications dans le runnable.

Lors de la construction du service, et après chaque exécution, démarrez comme suit:

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

Dans onDestroy() supprimer le message

handler.removeCallbacks(refresher);

Pour démarrer le service au démarrage, vous avez besoin d'un démarreur automatique. Cela va dans votre manifeste

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

et la ServiceAutoStarter ressemble à ceci:

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

Empêcher le système d'exploitation de tuer le service est délicat. De plus, votre application peut avoir une valeur RuntimeException et se bloquer, ou votre logique peut rester bloquée. 

Dans mon cas, il a semblé utile de toujours actualiser le service à l'écran avec une variable BroadcastReceiver. Donc, si la chaîne de mises à jour est bloquée, elle sera ressuscitée à mesure que l'utilisateur utilise son téléphone.

Dans le service:

private BroadcastReceiver screenOnReceiver; 

Dans votre service onCreate()

screenOnReceiver = new BroadcastReceiver() {

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

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

Puis annulez l'enregistrement de votre service sur onDestroy() avec

unregisterReceiver(screenOnReceiver);
26
Jim Blackler

Vous pouvez le faire par une implémentation simple:

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

}

Vous pouvez commencer le service par ceci:

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

En manifeste:

 <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>
3
Manoj Srivastava

par ces trois étapes, vous pouvez réveiller toutes les 5 minutes la plupart des appareils Android:

1. Définissez votre AlarmManager alternatif pour différentes 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. Construisez votre propre BroadcastReceiver statique:

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. Ajoutez <receiver> à AndroidManifest.xml:

<receiver Android:name=".OwnReceiver"  />
0
MHSFisher