Wie führe ich einen Dienst immer im Hintergrund aus?

76

Ich bin gerade dabei, eine App zu erstellen, die der integrierten SMS-App ähnelt.

Was ich brauche:

  • Ein Dienst, der immer im Hintergrund ausgeführt wird
  • alle 5 min. Der Dienst überprüft den aktuellen Standort des Geräts und ruft einen Webdienst auf
  • Wenn bestimmte Kriterien erfüllt sind, sollte der Dienst eine Benachrichtigung generieren (genau wie die SMS-App).
  • Wenn Sie auf die Benachrichtigung klicken, wird der Benutzer zur App weitergeleitet (genau wie bei der SMS-App).
  • Wenn die App installiert ist, sollte der Dienst gestartet werden
  • Wenn das Gerät neu gestartet wird, sollte der Dienst gestartet werden

Was ich versucht habe:
- Ausführen eines regulären Dienstes
, der einwandfrei funktioniert hat, bis Android den Dienst beendet - Verwenden des AlarmManagers, um die 5 Minuten zu erstellen. Intervallaufruf an einen Dienst. Aber ich konnte diese Arbeit nicht machen.

Thomas H.
quelle

Antworten:

34

Ein Dienst, der immer im Hintergrund ausgeführt wird

Dies ist im wahrsten Sinne des Wortes nicht möglich , wie Sie festgestellt haben. Es ist auch schlechtes Design .

alle 5 min. Der Dienst überprüft den aktuellen Standort des Geräts und ruft einen Webdienst auf

Verwenden Sie AlarmManager.

Mit dem AlarmManager machen Sie die 5 min. Intervallaufruf an einen Dienst. Aber ich konnte diese Arbeit nicht machen.

Hier ist ein Beispielprojekt, das zeigt, wie man eines zusammen mit dem verwendetWakefulIntentService damit Sie wach bleiben, während Sie versuchen, den gesamten Webdienst zu erledigen.

Wenn Sie weiterhin Probleme damit haben, stellen Sie eine neue Frage zu den spezifischen Dingen, mit AlarmManagerdenen Sie konfrontiert sind und die Ihnen Kummer bereiten.

CommonsWare
quelle
Ich habe dieses Beispielprojekt verwendet und ich habe ein Protokoll erstellt und 10 Minuten gewartet. Nichts ist Arbeit? Ich muss irgendetwas Service anrufen, um es zu starten?
Samir Mangroliya
2
@SamirMangroliya: Wenn Sie sich das Datum auf der Antwort ansehen, werden Sie feststellen, dass es über 3 Jahre alt ist. Dieses Beispiel wurde für Android 2.x geschrieben, und Sie müssten das Gerät / den Emulator neu starten, damit die Alarme gestartet werden. Für Android 3.1+ ist selbst das nicht ausreichend - Sie müssen eine Aktivität ausführen, bevor der On-Boot-Empfänger wirksam wird. Ich würde empfehlen, dass Sie zu github.com/commonsguy/cw-omnibus/tree/master/AlarmManager/… wechseln. Dies ist das gleiche Basisprojekt, aber aktueller. Führen Sie es aus und stellen Sie sicher, dass die Aktivität ausgeführt wird (zeigt a Toast) und die Alarme beginnen.
CommonsWare
Hallo, wenn ich PollReceiver.scheduleAlarms (this) aufrufe; In der Hauptaktivität und wenn ich die App schließe und dann neu starte (Angenommen, ich öffne die Anwendung in einer Stunde mehr als 15 Mal), wird dann jedes Mal für 5 Minuten ein Alarm erstellt?
Samir Mangroliya
1
@SamirMangroliya: Wenn Sie einen Alarm für ein Äquivalent planen, PendingIntentwird jeder vorhandene Alarm dafür gelöscht PendingIntent. Beachten Sie darüber hinaus, dass dies ein Beispiel aus einem Buch ist und absichtlich nicht versucht wird, alle Szenarien anzusprechen.
CommonsWare
Ich habe den folgenden Beitrag gefunden, der besagt, dass dies möglich ist: blogmobile.itude.com/2012/02/20/… . Funktioniert dieser Ansatz?
Mark Vincze
26

Eine meiner Apps macht etwas sehr ähnliches. Um den Service nach einer bestimmten Zeit zu aktivieren, empfehle ichpostDelayed()

Haben Sie ein Handlerfeld:

private final Handler handler = new Handler();

und eine Auffrischung Runnable

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

Sie können Ihre Benachrichtigungen im Runnable auslösen.

Beim Serviceaufbau und nach jeder Ausführung wie folgt beginnen:

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

In onDestroy()entfernen Sie den Beitrag

handler.removeCallbacks(refresher);

Um den Dienst beim Booten zu starten, benötigen Sie einen Autostarter. Dies geht in Ihrem Manifest

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

und das ServiceAutoStartersieht so aus:

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

Es ist schwierig, das Betriebssystem daran zu hindern, den Dienst zu beenden. Auch Ihre Bewerbung kann eine habenRuntimeException und abstürzen, oder Ihre Logik kann ins Stocken geraten.

In meinem Fall schien es hilfreich zu sein, den Dienst auf dem Bildschirm immer mit einem zu aktualisieren BroadcastReceiver. Wenn die Aktualisierungskette ins Stocken gerät, wird sie wiederbelebt, wenn der Benutzer sein Telefon verwendet.

Im Dienste:

private BroadcastReceiver screenOnReceiver; 

In Ihrem Dienst onCreate()

screenOnReceiver = new BroadcastReceiver() {

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

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

Dann heben Sie die Registrierung Ihres Dienstes onDestroy()mit auf

unregisterReceiver(screenOnReceiver);
Jim Blackler
quelle
2
Normalerweise mag ich deine Antworten, aber diese ... nicht so sehr. :-( Sie schlagen beispielsweise vor, dass der Dienst einen Empfänger registriert, um sich selbst wiederzubeleben, sobald er getötet wurde. Der Empfänger wird zusammen mit dem Dienst getötet, daher sollte dies keine Auswirkung haben. Darüber hinaus ist das gesamte Konzept eines ewigen Dienstes schrecklich unter Android und ist der Grund, warum Benutzer mit Task-Killern oder dem Bildschirm "Laufende Dienste" in der Einstellungen-App zurückschlagen. Es gibt nur sehr wenige Fälle, in denen ein wirklich ewiger Dienst benötigt wird - die überwiegende Mehrheit der Zeit AlarmManagerwird ausreichen.
CommonsWare
4
Dank CWare hätte ich wahrscheinlich klarer machen sollen, dass der Resurrector vor Logikfehlern und den vielen Dingen schützen soll, die die Kette von Ereignissen blockieren können, die den Dienst aufwecken, da das, was dem Herunterfahren des Dienstes durch das System zugeschrieben wird, oft etwas anderes sein kann. Ich werde mich mit dem Ansatz des Alarmmanagers befassen. Ich erinnere mich vage, dass ich dies vor einiger Zeit versucht habe und nicht in der Lage war, es zum Laufen zu bringen.
Jim Blackler
3

Sie können dies durch eine einfache Implementierung tun:

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

}

Sie können den Service folgendermaßen starten:

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

Im Manifest:

 <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>
Manoj Srivastava
quelle
1

Mit diesen drei Schritten können Sie die meisten Android-Geräte alle 5 Minuten aktivieren:

1. Stellen Sie Ihren alternativen AlarmManager für verschiedene APIs ein:

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. Erstellen Sie Ihren eigenen statischen 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. hinzufügen <receiver>zu AndroidManifest.xml:

<receiver android:name=".OwnReceiver"  />
MHSFisher
quelle
1
Statische Empfänger können im Manifest nicht so initialisiert werden, stattdessen sollten Sie <Empfänger android: name = "className $ OwnReceiver" />
Psycho
0

Wenn Sie möchten, dass Ihr Dienst ausgeführt wird, bedeutet dies meiner Meinung nach immer, dass er nicht gestoppt wird, wenn die App beendet wird. Wenn Ihre App ausgeführt wird oder sich im Hintergrund befindet, wird Ihr Dienst trotzdem ausgeführt. Wenn Sie die App beenden, während ein Dienst ausgeführt wird, wird die Funktion onTaskRemoved ausgelöst.

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

Sobald Sie die App beendet haben, wird der Dienst nach 10 Sekunden gestartet. Hinweis: Falls Sie verwenden möchten

AlarmManager.ELAPSED_REALTIME_WAKEUP

Die Mindestzeit, nach der Sie den Dienst neu starten können, beträgt 15 Minuten.

Denken Sie daran, dass Sie den Dienst auch beim Neustart mit BroadcastReceiver starten müssen.

Siddhartho Dhar
quelle