Heartbeat mit dem AlarmManager hält Service nicht am Leben

  • Antworten:24
  • OffenNicht stickyNicht beantwortet
  • Forum-Beiträge: 22

07.09.2018, 08:32:38 via Website

Hey,
ich hatte hier vor ein paar Wochen schon mal eine Frage gepostet, wie ich GPS updates im Hintergrund permanent erhalte. Mir wurde die Antwort gegeben einen Service mit einem Heartbeat zu verwenden. Danke für die Hilfe nochmal.
Jetzt habe ich das Problem, dass mein Service trotzdem ab und zu gekillt wird.
Ich habe jetzt zwei troubleshooting Ansätze:

Entweder die App crasht nach ein paar stunden und ich krieg nicht mit wieso. In diesem Kontext würde mich interessieren, ob ich die Logfiles auf meinem Gerät speichern kann und später auslesen? Ich habe nur dieses eine Gerät und kann es nicht die ganze Zeit am Rechner hängen lassen :)

Der zweite Ansatz ist, dass etwas mit meinem Heartbeat nicht stimmt. Hierfür poste ich hier mal meinen Code:
Die MainActivity:

> public class MainActivity extends AppCompatActivity {

//initializing heartbeat
static AlarmManager alarmManager;

@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //checking if permission to use gps location is given
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 10);
        return;
    }else{
        Toast.makeText(MainActivity.this,"Permission to use GPS is given!", Toast.LENGTH_SHORT).show();
    }

    Log.e("Alarmmanager", "Starting Alarmmanager");
    if (alarmManager == null && !isMyServiceRunning(BackroundService.class)) {
        alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(this, AlarmReceive.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 900000, pendingIntent);
    }
}

Der wichtigste Part ist der Unterste mit dem AlarmManager.
Dann die AlarmReceive klasse:

public class AlarmReceive extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
    Intent background = new Intent(context, BackroundService.class);
    context.stopService(background);
    context.startService(background);
}

}

Und zu guter Letzt der BackroundService:

 @SuppressLint("MissingPermission")
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {

    //code for letting the service run even if app is not used
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // For foreground service
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

        // Creating channel for notification
        String id = BackroundService.class.getSimpleName();
        String name = BackroundService.class.getSimpleName();
        NotificationChannel notificationChannel = new NotificationChannel(id,
                name, NotificationManager.IMPORTANCE_NONE);
        NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        service.createNotificationChannel(notificationChannel);

        // Foreground notification
        Notification notification = new Notification.Builder(this, id)
                .setContentTitle(getText(R.string.app_name))
                .setContentText("KSL is protecting you!")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .setTicker("Ticker text")
                .build();

        startForeground(9, notification);
    }

    locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
    locationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            //when the location changed
            //here is where the magic is happening
        }
        @Override
        public void onStatusChanged(String s, int i, Bundle bundle) {
        }
        @Override
        public void onProviderEnabled(String s) {
        }
        @Override
        public void onProviderDisabled(String s) {
            Toast.makeText(BackroundService.this, "gps is turned off!", Toast.LENGTH_SHORT).show();
        }
    };
    requestingGpsUpdates();
    return START_STICKY;
}

public void requestingGpsUpdates(){
    powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
    //Handler to delay
    new Handler().postDelayed(new Runnable() {
        @SuppressLint("MissingPermission")
        @Override
        public void run() {
        if (powerManager.isScreenOn()){
            Log.d("Requesting:","GpsLocation");
            //asking for a new update
            if (!updatesRunning){
                locationManager.requestLocationUpdates("gps", 500, 0, locationListener);
                updatesRunning = true;
                Log.e("Updates Requested", ""+updatesRunning);
            }
        }else{
            //if screen is turned off updates will be removed
            if (updatesRunning){
                locationManager.removeUpdates(locationListener);
                updatesRunning = false;
                Log.e("Updates Requested", ""+updatesRunning);
            }
        }
        //the method calling itself for a loop
            if (!isDestroy){
                requestingGpsUpdates();
            }else {
            Log.e("Service", "get's destroyed!");
            }
        }
    },500);
}

Könntet ihr mir vielleicht dabei helfen? Ich habe schon viel gesucht, und keine Lösung bekommen.

— geändert am 07.09.2018, 08:35:22

Diskutiere mit!
  • Forum-Beiträge: 2.902

07.09.2018, 08:49:58 via Website

Hallo,

Grundsätzlich ist das was du entwickelst sehr zeitkritisch .

Um das richtig und vernünftig auf Langzeit zu debuggen und auch Fehler zu erkennen kommst du mit einem Gerät definitv nicht hin . Ich könnte zumindest so nicht arbeiten .

Desweitern (und ich bin deinen Code nicht ganz durchgegangen) ist mir aufgefallen , dass du beim Neustart des Services dem System überhaupt keine Luft lässt

context.stopService(background);
context.startService(background);

Das Ganze machst Du im millisekunden Bereich. - Das muss schief gehen .

Dann sehe ich nirgendwo bei dir einen Ansatz von try/catch - das A&O bei der Entwicklung.

Und zuletzt kannst du dir dann noch ACRA oder Firebase implementieren - Dann wird es auch einfacher mit dem Debuggen

https://github.com/ACRA/acra
https://firebase.google.com/docs/crash/

BTW : Trotz meiner zahlreichen Hinweise, benutzt du trotzdem 500ms Interval und keine Accelerator.
Mehr als Dich mit der Nase draufzustupsen kann man leider nicht :-)

— geändert am 07.09.2018, 09:33:28

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 22

07.09.2018, 10:02:40 via Website

Ok, ich habe die AlarmReceive aktualisiert:

 public void onReceive(final Context context, Intent intent) {
    Log.e("Service_call_"  , "You are in AlarmReceive class.");
    final Intent background = new Intent(context, BackroundService.class);
    Log.e("AlarmReceive ","testing called broadcast called");
    try{
        context.stopService(background);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                context.startService(background);
            }
        },100);
    }catch (Exception e){
    }
}

Jetzt werde ich mal schauen, ob das besser läuft.

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.902

07.09.2018, 10:15:36 via Website

100 ms ?? ist jetzt nicht dein Ernst ...

Min 2000-3000 , wenn nicht mehr

P.S Kann es sein , dass du das ganze System nicht verstanden hast ?

— geändert am 07.09.2018, 10:16:30

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 22

07.09.2018, 10:19:46 via Website

Und hierzu:

BTW : Trotz meiner zahlreichen Hinweise, benutzt du trotzdem 500ms Interval und keine Accelerator.
Mehr als Dich mit der Nase draufzustupsen kann man leider nicht

Ich wollte mir das Prinzip mit dem Accelometer mal anschauen. Es ist jedoch sehr komplex mit dem sensor eine fortlaufende Geschwindigkeit (und keine Beschleunigung) zu messen. Ich habe dazu ein paar Beispiele gefunden und war heillos überfordert. ich werde mich da langsam rantasten.

Zu dem 500ms Intervall:
Ich brauche eine sehr schnelle Reaktion. Deswegen der kurze Intervall. Ich habe es immerhin geschafft, dass nur dann Location Updates gemacht werden, wenn ich sie brauche. Also wenn der Bildschirm an ist. Bis jetzt hat das System im Alltagsgebrauch noch nicht gemeckert, dass zu viel Akku verbraucht wird.

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 22

07.09.2018, 10:20:58 via Website

P.S Kann es sein , dass du das ganze System nicht verstanden hast ?

Kann gut sein :)
Ich mach das als Hobby. Ich hab nur in meiner Ausbildung ein bisschen Java gelernt und hab keine Ahnung, wie Android als System funktioniert.

— geändert am 07.09.2018, 10:24:39

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.902

07.09.2018, 10:45:24 via Website

Na ja , ich bin ein wenig verwundert :

Auf der einen Seite fragst du nach, wie es geht - Dann setzt Du es doch nicht so um (mit dem Kopf durch die Wand) und schlägst dann hier wiederum mit einem Leiden auf :-)

Und dann muss man noch diskutieren ? Warum ? :-)

Zu dem 500ms Intervall:
Ich brauche eine sehr schnelle Reaktion. Deswegen der kurze Intervall. Ich habe es immerhin geschafft, dass nur dann Location Updates gemacht werden, wenn ich sie brauche. Also wenn der Bildschirm an ist. Bis jetzt hat das System im Alltagsgebrauch noch nicht gemeckert, dass zu viel Akku verbraucht wird.

Das hat nichts mit dem Akku zu tun , sondern mit dem System ansich.
Es meckert , weil Du für deinen Zweck die falsche Technik verwendest.

Nochmal : Wenn du deinen Zweck vernünftig umsetzen magst , musst du zuerst die Sensoren auswerten , dann GPS im 2000ms Takt (MINDESTENS ! )

Dann bitte schön Alles mit try/catches abfangen und vor allem nicht wie wild durch die Gegend pollen.

Kleine Anmerkung : Grundsätzlich solltest du wissen , dass Android NICHT für dein Vorhaben konzipiert ist. Du befindest dich auf einem "Workaround" Pfad für Eingefleischte.

UND : Was denkst du wohl gibt es die verbauten Beschleunigungs-Sensoren ?
Um festzustellen , dass das Teil auf den Boden gefallen ist ? :-)

— geändert am 07.09.2018, 10:47:12

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 483

07.09.2018, 10:48:39 via Website

Hi warum stopst du eigentlich den Service es solte doch reichen ihn nur nochmal zu starten aufzurufen um ihn am Leben zu halten.
Zweitens warum machst du nicht gleich alles im resiever? Wie so erst noch einen Service starten?

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.902

07.09.2018, 10:52:03 via Website

@Jokel

.... weil er sich ein paar threads vorher beschwert hatte , dass Android ihm im Langzeittest den Service abgeschossen hat. (Was normal ist)

Deshalb macht er jetzt nen Heartbeat mit dem Alarmmanger.
Und damit er sich sicher sein kann ,schiesst er den Service vorher nochmal manuell ab.

Allerdings will er dann den wieder in 0ms-100ms zum Leben erwecken - ein Turbo-Smartphone :-)

— geändert am 07.09.2018, 12:54:52

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 22

07.09.2018, 17:32:53 via Website

ich hab die pause zwischen stop und start des Services auf 2000ms erhöt.
Ich hab mir das mit dem Accelerometer mal angeschaut und da steckt ziemlich hohe mathematik dthahinter, die ich nicht verstehe. Zumindest noch nicht. Gerade macht mir die Gravitation schwer zu schaffen. Ich habe auch noch keine Idee, wie ich aus den werten (x,y und z achse) eine Geschwindigkeit berechnen soll, die z.B. Bewegungen in der Hand (gestikullieren etc.) ignoriert, aber Laufen erfasst.
Deswegen hatte ich mich ursprünglisch für gps entschieden. Das interessiert es nicht, wie sehr du mit deinem smartphone rumfuchtelst.

— geändert am 07.09.2018, 18:07:00

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 22

09.09.2018, 11:15:54 via Website

Da ich die methode mit dem Accelerometer erstmal nicht verstehe (werde mich weiter damit befassen) habe ich nun zwei dinge getan:
1. Ich habe ein Criteria objekt programmiert, dass die genauigkeit meiner GPS updates auf hoch setzt.
2. Ich habe den GPS Update Intervall auf 2000ms gesetzt.

Es funktioniert alles noch einwandfrei und die App reagiert schnell (innerhalb von 2s). Jedoch wurde der Service, wieder nach einiger Zeit, wiederholte male gekillt. Dabei ist mir etwas aufgefallen. Der Service wurde immer bei Ausgeschalteten Bildschirm gekillt. Das kann ja eig dann nicht mit der Systemauslastung zu tun haben, da da ja nur ein loop läuft, der eig nichts macht, außer zu checken ob der Bildschirm wieder an ist. Oder nimmt Android diesen Loop zum anlass den Service zu killen?
Gibt es eine Möglichkeit anders zu erfahren, ob der Bildschirm angeschaltet wird. Über einen Broadcast Receiver oder so?

hier ist nochmal der code des Services:

public void requestingGpsUpdates(){
    powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);

    //criteria to make sure high accuracy is granted
    final Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    criteria.setPowerRequirement(Criteria.POWER_LOW);
    criteria.setSpeedAccuracy(Criteria.ACCURACY_FINE);
    criteria.setAltitudeRequired(false);
    criteria.setSpeedRequired(true);
    criteria.setCostAllowed(false);
    criteria.setBearingRequired(false);
    //API level 9 and up
    criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
    criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);

    //Handler to delay
    new Handler().postDelayed(new Runnable() {
        @SuppressLint("MissingPermission")
        @Override
        public void run() {
        if (powerManager.isScreenOn()){
            Log.d("Requesting:","GpsLocation");
            //asking for a new update
            if (!updatesRunning){
                locationManager.requestLocationUpdates(2000, 0, criteria, locationListener, null);
                updatesRunning = true;
                Log.e("Updates Requested", ""+updatesRunning);
            }
        }else{
            //if screen is turned off updates will be removed
            if (updatesRunning){
                locationManager.removeUpdates(locationListener);
                updatesRunning = false;
                Log.e("Updates Requested", ""+updatesRunning);
            }
        }
        //the method calling itself for a loop
            if (!isDestroy){
                requestingGpsUpdates();
            }else {
            Log.e("Service", "get's destroyed!");
            }
        }
    },2000);
}

Noch eine andere Frage:
Ich bin ja ein ziemlicher Grünschnabel, was programmierung an geht. Wo sollte ich eurer meinung nach überall mit try/catch absichern?
Sollte ich das einfach überall machen?

— geändert am 09.09.2018, 11:19:03

Hilfreich?
Diskutiere mit!
Pascal P.
  • Mod
  • Blogger
  • Forum-Beiträge: 10.170

09.09.2018, 11:28:34 via Website

Ja das geht mit Broadcasts...
https://stackoverflow.com/questions/9477922/android-broadcast-receiver-for-screen-on-and-screen-off

und try&catch überall wo es notwendig ist, das musst du selber einschätzen, spätestens dann wenn du sonst die Methode mit "throws ...Exception" dekarieren musst.
Zudem lasse die Catch Blöcke nie leer, der Fehler muss ja behandelt werden...

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.902

09.09.2018, 11:54:07 via Website

Gibt es eine Möglichkeit anders zu erfahren, ob der Bildschirm angeschaltet wird.

Neben der von Pascal vorgeschlagenen Variante solltest du darauf auch achten , dass dein Smartphone ansich den Process killt .
Bei Samsung - Devices neuere Generation muss man explizit im Powermanagermant auch die Apps angeben , die nicht gekillt werden soll.

Wenn das nicht ausgeschaltet ist , nutzt dir auch kein Broadcast etwas

— geändert am 09.09.2018, 12:12:40

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
Pascal P.
  • Mod
  • Blogger
  • Forum-Beiträge: 10.170

09.09.2018, 11:57:10 via Website

@stefan: Man kann doch aber den Broadcst immer registrieren und abfangen auch wenn die App nicht läuft.
Wie funktioniert sonst der Broadcast nach einem Boot vorgang? Da läuft die App ja auch nicht.

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.902

09.09.2018, 12:06:13 via Website

Richtig : Wenn aber das Powermanagement des Smartphone die App abschiesst, dann kommt auch kein broadcast mehr - also auch nicht das vom SCREEN_OFF.

Nennt sich bei meinem Teil (S8) "App-Energieüberwachung" - da kommt nix mehr , was du nur ansatzweise abfangen könntest . Habe mir schon den Wolf gesucht :-)

— geändert am 09.09.2018, 12:06:54

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 22

11.09.2018, 11:02:56 via Website

Ich glaube ich geb auf :D Der Service immer noch nach sechs stunden gekillt.
Habe jetzt mit BroadcastReceivern für SCREEN_ON bzw. OFF gearbeitet und den Update-intervall auf 2000ms gesetzt. Ich hab auch die Einstellungen bezüglich der Systemauslastung geändert.
Wie funktioniert das bei email oder messenger Programmen, die permanent im Hintergrund laufen. Die kriegen das ja auch hin am leben zu bleiben....

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.902

11.09.2018, 11:07:16 via Website

Wie funktioniert das bei email oder messenger Programmen, die permanent im Hintergrund laufen. Die kriegen das ja auch hin am leben zu bleiben....

Die bauen auf dem FCM Service auf - Völlig andere Technik
https://firebase.google.com/docs/cloud-messaging/

Wir hatten Dir Eingangs schon versucht mitzuteilen , dass Android nicht für dein Vorhanden konzipiert ist. Das soll nicht heissen , dass es nicht geht , aber da muss man sich schon viel anstrengen und eine gehörige Portion Geduld und Erfahrung mitbringen .

— geändert am 11.09.2018, 11:27:27

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
Pascal P.
  • Mod
  • Blogger
  • Forum-Beiträge: 10.170

11.09.2018, 12:29:50 via App

Gab es nicht noch den Trick mit der ständigen Benachrichtigung damit der Service aktiv bleibt?
AutomateIt macht das so, damit dessen Regeln greifen können.

Edit: Vielleicht ist es für dein Vorhaben besser einen Microcontroller mit GPS zu Programmieren und diesen dann per USB/Wifi mit einer App kommunizieren lassen.
Damit hast du die zeitkritischen Dinge ausgelagert und kannst die erfassten Daten später vom Controller lesen. Aber probier es erstmal mit der Notification

— geändert am 11.09.2018, 12:33:07

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.902

11.09.2018, 12:32:12 via Website

Gab es nicht noch den Trick mit der ständigen Benachrichtigung damit der Service aktiv bleibt?

Ist mir auch beim Schreiben oben in den Sinn gekommen - eigentlich recht pfiffige Idee und trivial dazu :-)

— geändert am 11.09.2018, 12:35:02

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 22

11.09.2018, 12:41:51 via Website

Wie funktioniert das dann mit den ständigen benachrichtigungen?

Hilfreich?
Diskutiere mit!

Empfohlene Artikel