Zuverlässige GPS abfragen

S

SlayNox

Ambitioniertes Mitglied
15
Hallo liebes Forum,
mein bisherigen suchen ergaben leider keine treffer, deshalb schreibe ich ein neues Thema.

Ich benötige eine Zuverlässige GPS abfrage. Ich bin in einem Abschleppdienst tätig, indem die Fahrer meine App auf dem Handy haben. Diese wird unter anderem für die Auftragsverwaltung und noch ein paar mehr sachen verwendet. Momentan beziehen wir unsere Standortabfragen der Fahrer/Fahrzeuge über das eingebaute TomTom System. Dies soll sich aber in Zukunft ändern. Ich habe mich lange an der GPS abfrage veruscht und bis zu Version 8.0.0 hat es auch relativ zuverlässig funktioniert. Nun jedoch habe ich Probleme.
Ziel ist es die Standortabfragen in regelmäßigen Abständen auf einen Webserver zu laden. Dies muss zuverlässig funktionieren. Villeicht kann mir der ein oder andere Tipps geben, dies zu gewährleisten.

Mein aktueller Code sieht wie folgt aus:

Startbefehl:

Code:
private void  startGPSService(){
context.startForegroundService(new Intent(context, GPSTracker.class));
}

GPS Tracker Klasse:

Code:
public class GPSTracker extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    IBinder mBinder = new LocalBinder();

    private GoogleApiClient mGoogleApiClient;
    private PowerManager.WakeLock mWakeLock;
    private LocationRequest mLocationRequest;
    // Flag that indicates if a request is underway.
    private boolean mInProgress;
    private static Location mLocation;
    private Boolean servicesAvailable = false;
    private static Context mContext;

    public class LocalBinder extends Binder {
        public GPSTracker getServerInstance() {
            return GPSTracker.this;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(2,new Notification());
        Log.i("GPSTracker", "Service gestartet");

        mContext = getApplicationContext();
        mInProgress = false;
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(GPSTracker_Parameter.UPDATE_INTERVAL);
        mLocationRequest.setFastestInterval(GPSTracker_Parameter.FASTEST_INTERVAL);

        servicesAvailable = servicesConnected();

        setUpLocationClientIfNeeded();
    }

    protected synchronized void buildGoogleApiClient() {
        this.mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    private boolean servicesConnected() {
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (ConnectionResult.SUCCESS == resultCode) {
            return true;
        } else {
            return false;
        }
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
        if (this.mWakeLock == null) { //**Added this
            this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
        }
        if (!this.mWakeLock.isHeld()) { //**Added this
            this.mWakeLock.acquire();
        }
        if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
            return START_STICKY;

        setUpLocationClientIfNeeded();
        if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
            mInProgress = true;
            mGoogleApiClient.connect();
        }
        return START_STICKY;
    }

    private void setUpLocationClientIfNeeded() {
        if (mGoogleApiClient == null)
            buildGoogleApiClient();
    }

    @Override
    public void onLocationChanged(Location location) {
        mLocation = location;
        createLocationFile();
    }

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

    public String getTime() {
        SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return mDateFormat.format(new Date());
    }


    @Override
    public void onDestroy() {
        this.mInProgress = false;

        if (this.servicesAvailable && this.mGoogleApiClient != null) {
            this.mGoogleApiClient.unregisterConnectionCallbacks(this);
            this.mGoogleApiClient.unregisterConnectionFailedListener(this);
            this.mGoogleApiClient.disconnect();
            this.mGoogleApiClient = null;
        }

        if (this.mWakeLock != null) {
            this.mWakeLock.release();
            this.mWakeLock = null;
        }

        Log.i("GPSTracker", "Service wurde gestoppt");

        super.onDestroy();
    }

    @Override
    public void onConnected(Bundle bundle) {

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient, mLocationRequest, this);
    }

    @Override
    public void onConnectionSuspended(int i) {
        mInProgress = false;
        mGoogleApiClient = null;
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        mInProgress = false;
        if (connectionResult.hasResolution()) {
            // If no resolution is available, display an error dialog
        } else {

        }
    }

    private static List<Address> getGeocodeAdress(Location location) {
        Geocoder geocoder = null;
        List<Address> addresses = null;
        try {
            geocoder = new Geocoder(mContext, Locale.getDefault());
            addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return addresses;
    }

    public static double getLatitude() {
        return mLocation.getLatitude();
    }

    public static double getLongitude() {
        return mLocation.getLongitude();
    }

    public static String getLocationAddress() {
        String result = "n/A";
        if (mLocation != null) {
            result = getGeocodeAdress(mLocation).get(0).getAddressLine(0);
        }
        return result;
    }

    public static String getLocationCity() {
        String result = "n/A";
        if (mLocation != null) {
            result = getGeocodeAdress(mLocation).get(0).getLocality();
        }
        return result;
    }

    public static String getLocationState() {
        String result = "n/A";
        if (mLocation != null) {
            result = getGeocodeAdress(mLocation).get(0).getAdminArea();
        }
        return result;
    }

    public static String getLocationCountry() {
        String result = "n/A";
        if (mLocation != null) {
            result = getGeocodeAdress(mLocation).get(0).getCountryName();
        }
        return result;
    }

    public static String getLocationPostalCode() {
        String result = "n/A";
        if (mLocation != null) {
            result = getGeocodeAdress(mLocation).get(0).getPostalCode();
        }
        return result;
    }

    public static String getLocationStreet() {
        String result = "n/A";
        if (mLocation != null) {
            result = getGeocodeAdress(mLocation).get(0).getThoroughfare();
        }
        return result;
    }

    public static String getLocationStreetNumber() {
        String result = "n/A";
        if (mLocation != null) {
            result = getGeocodeAdress(mLocation).get(0).getSubThoroughfare();
        }
        return result;
    }

    public static String getLocationCountryCode() {
        String result = "n/A";
        if (mLocation != null) {
            result = getGeocodeAdress(mLocation).get(0).getLocale().getCountry();
        }
        return result;
    }

    public static String getCustomAdressStringShort() {
        return getLocationStreet() + " " + getLocationStreetNumber() + ", " + getLocationPostalCode() + " " + getLocationCity();
    }

    public static String getCustomAdressString() {
        return getLocationStreet() + " " + getLocationStreetNumber() + ", " + getLocationPostalCode() + " " + getLocationCity() + ", " + getLocationState() + ", " + getLocationCountry() + ", " + getLocationCountryCode();
    }


HTTP Upload:
(Bitte hier vom "UploadImage" nicht irritieren lassen.

Code:
public class HTTP_GPS_Upload {
    private static boolean inUploadProgress = false;
    private static Context context;

    public HTTP_GPS_Upload(Context context) {
        this.context = context;
        if (!inUploadProgress) {
            uploadImage();
        }
    }

    public void uploadImage() {
        if (!Connectivity.isConnected(context)) {
            Log.e("HTTP_GPS_Upload", "Es besteht keine Internetverbindung");
            return;
        }

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {


                inUploadProgress = true;
                String filepath = Parameter.FilePath.EXTERNAL_STORAGE_GPS + User.getIMEI(context) + ".xml";

                File file = new File(filepath);

                if (!file.exists()) {
                    return;
                }

                String requestUrl = new Parameter.ConnectionUrl().GET_CURRENT_HTTPS_SERVER_ADRESS_GPS_UPLOAD(context);

                OkHttpClient.Builder client = new HTTP_Trust_Client().getUnsafeOkHttpClient().newBuilder();


                File f = new File(filepath);

                RequestBody file_body = RequestBody.create(MediaType.parse(filepath), f);

                RequestBody request_body = new MultipartBody.Builder()
                        .setType(MultipartBody.FORM)
                        .addFormDataPart("type", filepath)
                        .addFormDataPart("uploaded_file", User.getIMEI(context) + ".xml", file_body)
                        .build();

                Request request = new Request.Builder()
                        .addHeader("Authorization", Credentials.basic(new Parameter.ConnectionUrl().GET_CURRENT_HTTPS_SERVER_ADRESS_USERNAME(context),
                                new Parameter.ConnectionUrl().GET_CURRENT_HTTPS_SERVER_ADRESS_PASSWORD(context)))
                        .url(requestUrl)
                        .post(request_body)
                        .build();

                OkHttpClient requestClient = client.build();

                try {
                    Response response = requestClient.newCall(request).execute();
                    if (!response.isSuccessful()) {
                        Log.e("HTTP_GPS_Upload", "Fehler beim upload der Standortdaten");
                        inUploadProgress = false;
                        Log.e("RESPONSE", response.body().string());
                    } else {
                        inUploadProgress = false;
                        //Log.e("HTTP_GPS_Upload", "Standortdaten hochgeladen");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }


Ich hoffe mir kann jemand helfen wie meine GPS abfragen (selbstverändlich wird die Internetanbindung außer Acht gelassen) zuverlässig ankommen

Liebe Grüße
 
Hallo Slay,

bevor ich deinen Code genauer anschaue ...

Deine Frage ist nicht eindeutig : Was bedeutet 8.0.0 ? Oreo ?
Was ist denn nicht zuverlässig ?

Vielleicht solltest du mal erklären , wo es Unregelmäßigkeiten gibt .
Service ? Koordinaten ? Übertragung ?


Was mir schon mal auffällt : Du gehst immer von einer connection zum Server aus .
Sicherheitsrelevante Dinge wie Puffer, CRC Check , Abbruch sehe ich nicht .
 
Hay,
entschuldigung, ja ich rede von Oreo. Also der Zeitpunkt an dem die BackgroundServices limitiert wurden.

Das ziel ist es, das wenn ein Fahrer den Auftrag startet, GPS Daten zu dieser "Fahrt" gesammelt werden. Bis zu dem Zeitpunkt an dem der Auftrag beendet wird. Leider kommen die GPS Daten aber nicht immer regelmäßig an. (Pause von 15 - 20 min zwichen einzelnen Uploads. Sonst in der Regel alle 3- 5 min.) Es gibt auch Fahrten zu denen gar keine Daten gesammt werden. GPS ist bei jedem Fahrer Aktiv.
 
leider kommen die GPS Daten aber nicht immer regelmäßig an

Wie wäre es denn mit einem alarmgetriggerten Service ?

Alle x Minuten werden die bis dahin local gesammelten Daten übertragen .
Basis wäre eine SQLite DB
 
Aber besteht bis dahin nicht das selbe problem? meines wissens nach werden Internet etc nach dem doze mode abgeschaltet. nun könnte ich also auch keine Daten übertragen. Bitte verbessere mich
 
Gegenfrage : Warum benötigst du denn die Daten in der Firma im quasi "Echtzeitverfahren" .

P.S. Die Rechte für den Hintergrundservice kannst du z.b. bei Samsung devices setzen - Default sind die entzogen .
(Powermanagerment)
 
Da wir in Zukunft planen eine Karte mit allen Einsatzmitteln anzuzeigen. Gelegentlich melden sich auch Auftraggeber die den aktuellen Standort des Einsatzmittels wissen möchten. Aus diesem Grund ist dies wichtig für uns.

Meine Vermutung ist das der Hintergrund Service abgeschaltet wird und sich nicht mehr von selbst startet. Somit wird HTTP nicht getriggert und es kommt auch nichts an.

Ja das weiß ich aber wie sieht es mit anderen Geräten aus, ich denke nicht dass sich das alles via Code abwickeln lässt.
Wir nutzen zwar Hauptsächlich Samsung Geräte, doch konnte das in Zukunft auch anderes aussehen. Man steckt halt nicht drin, und andere Abschleppdienste müssen ja auch nicht zwangsläufig Samsung Geräte verwenden. Leider :1f606:
 
Meine Vermutung ist das der Hintergrund Service abgeschaltet wird und sich nicht mehr von selbst startet

Das ist seit Gedenken von Android so und hat nichts mit Oreo zu tun.
(Siehe auch GarbageCollector und Lifecycle)

Deshalb :

A) Auf alle Fälle alarmgetriggert - Auch bei Samsung wird sich das mit entsprechenden Rechte abschalten
Das ist eine Eigenart von Android und Device / Versions unabhängig. (s.o.)

B) Du kannst für die Notfälle einen ForegroundService erstellen.

C) Du kannst aber auch die Daten von den Devices mittels FCM Triggerung pollen.
(Würde ich so auch tun)
 
Zu A).
Das würde bedeuten ich erstelle einen Alarm der im selben Intervall das triggern übernimmt. Frage hier: Gibt es wichtige Parameter beim Alarm die ich setzen muss, damit es zuverlässig funktioniert?

Zu B) Den Service würde ich dann kommplett wegfallen lassen, da ich mich auf A) verlasse :]

Zu C) Der FCM weckt das gerät dann auf. Gibt es auch hier wichtige Parameter zu setzen?
 
A) Nein , der AlarmManager beendet den Service, wartet 5000ms und startet ihn dann neu .
Nennt sich schlichtweg Heartbeat.
Bsp : How To Keep An Android Service Running – Digital Stuff

Alle 10 Min dürfte reichen

B) Ich würde immer einen Service verwenden.

C) Du kannst auch ein Silent-Push durchführen ...
FCM dient nur dazu , deinen Push an den Server zu triggern , mehr nicht
(Alle 30 Min z.b. oder bei Bedarf)
[doublepost=1544697334,1544696546][/doublepost]Nachtrag :


Deine Technik ansich ist ein wenig unproduktiv in Punkto Datenvolumen.

Alleine die Tatsache , dass du einen http Header von min 1500 Bytes hast , verbrät dir unnötige Resourcen.
Wenn schon ASCII ,dann würde ich niemals xml format nehmen (als Datei) sondern JSON.

Tipp : Sammle Daten und schicke sie als Block. Am besten noch als CRC32 /Binär.
ASCII ist hier m.E. der ungünstigste Weg
 
Zuletzt bearbeitet:
Okay vielen Dank für die Tipps.
Die HeartBeat Technik finde ich sehr gut. Nur leider will das Beispiel bei mir schon nicht funktionieren. Ich habe die onStart() gegen onStartCommand() getauscht, da diese veraltet ist. Aber leider habe ich damit kein Erfolg.
 
Zumindest funktioniert die Vorgehensweise bei mir in etlichen Projekten .....
Anpassen musst du sie natürlich an die entsprechenden API's

Es ging mir ja nur darum dir aufzuzeigen , was es für Möglichkeiten gibt
 
Okay dann danke ich dir für deine Hilfe.

Hier kriegt man wirklich immer gut geholfen :D
 
Hallo
ich finde das Beispiel von saw00 nicht schlecht. Nur wird es unter Oreo ,oder sogar schon früher, so nicht richtig laufen.

Der Alarmmanager im Beispiel ist auf eine wiederholungs- rate von 1 sek eingestellt.

"alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000, piHeartBeatService);"

Das ist zu schnell und geht auch mit den neueren Android Versionen nicht mehr, ich bekomme da nur ca. 1 min hin seit Android 5 oder 6
Alarm Repeat in Android 8


Glaube auch das sich auch etwas geändert hat wenn ein Service einen Thread startet. Oder war das beim Resiver schaue da nochmal genau nach.
Background Execution Limits | Android Developers


Auch wird bei Oreo der Alarmmanager mit Repeating nicht immer richtig Im Hintergrund ausgeführt. Deshalb starte ich den Alarm immer wieder neu im Service.
Bei einen einzelnen Alarm habe ich das noch nicht beobachtet.

Ich Starte einen Alarmmanager der einen Service aufruft der wieder einen neuen Alarm startet und der Service beendet sich selbst. Abbruch kannste ja über ein PutExtra was du dem Intent mitgibst einbauen.

Preparing for Android 0: The Death of Background Services
Android Oreo Background Execution Limits – AndroidPub
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: swa00
Ja, das ist ab Oreo gar nicht mehr so einfach ständig im Hintergrund zu laufen. Am besten funktioniert es eigentlich mit einem Foreground Service (also eine dauerhafte Notification) plus Battery Optimization ausschalten. Optimize for Doze and App Standby  |  Android Developers
Damit läuft meine App eigentlich ganz gut. Ansonsten zusätzlich über JobScheduler (sollte man statt AlarmManager verwenden) den Service immer mal wieder neustarten. Die Kombination aus diesem funktioniert für mich eignetlich zu 99%.
 
  • Danke
Reaktionen: swa00 und jogimuc
Oh ich habe gar nicht mehr mit Antworten gerechnet. Vielen dank ich schaue mir das alles mal an. Die machen es einem wirklich schwer :1f605:
[doublepost=1546873115,1546872829][/doublepost]Ich habe auch schon daran gedacht mittels Room alle GPS abfragen in eine Datenbank zu schreiben und diese dann beim Wakeup wegzuschreiben. Vermutlich wird das aber nicht viel bringen da ich der Meinung bin das der GPS Service abgeschaltet wird. Daher wird nach einiger Zeit nicht viel in der Datenbank drinstehen. Außerdem soll es ja möglichst ein Live-System darstellen.
 
Hi hast du es denn schon mal so probiert wie ich gesagt habe. Einen alarmmanager ner der keine Wiederholung hat. Der dann einen Service startet und der Service startet wieder einen alarmmanager.
Fer alarmmanager wird eigentlich auch unter den neuen Versionen recht zuverlässig ausgeführt. Nur wenn die einen alarmmanager mit automatischer Wiederholung machst gibt es propleme.
Der Service solte sich natürlich selber beenden damit der alarmintent ihn wieder starten kann.

Ansonsten die Methode von deek ist auch eine Möglichkeit.

Wenn dein Service nicht läuft wie soll da was in die DB geschrieben werden.
 
Hay,
nein das habe ich noch nicht probiert. Das werde ich leider erst morgen in Angriff nehmen können. Das werde ich auf jeden Fall testen.
Ich halte euch auf dem laufenden :)
[doublepost=1546957573,1546957287][/doublepost]Ach noch was, muss ich dem AlarmManger irgendwelche besonderen Parameter übergeben?Da gab es doch mal RTC_WAKEUP oder so ähnlich. Also das der AlarmManager das Gerät auch so weckt, dass es Netzwerkanfragen durchführen kann oder ist das hiermit nicht möglich?
 
>Ach noch was, muss ich dem AlarmManger irgendwelche besonderen Parameter übergeben?Da gab es doch mal RTC_WAKEUP oder so ähnlich

Besondere Parameter musst du nicht übergeben. Eben halt die Zeit in ms wann der nächste Alarm ausgelöst werden soll. Dazu holst du dir die aktuelle Systemzeit und addierst die Zeit in ms dazu in der er auslösen soll.

> Also das der AlarmManager das Gerät auch so weckt, dass es Netzwerkanfragen durchführen kann oder ist das hiermit nicht möglich?

Der AlarmManager beinhaltet einen PendingIntent und dieser einen Intent der deinen Service aufruft. Was du dann in den Service machst ist deine Sache.

Wenn Ich mich recht erinnere reicht es dir wenn der Service ca. jede min einmal aufgerufen wird du die Pos speichert. Du müsstest eben dafür sorgen dass der Service nicht zu lange läuft. So das er sich beendet hat bevor der nächste Alarm kommt. Wenn das GPS eben nach einer bestimmten Zeit nicht verfügbar ist eben abbrechen.
Ich habe das bis jetzt nur mit sehr kurzen Service Laufzeiten gemacht.

Der Service in etwa so

Code:
public class Hintergrundservice extends Service {

    private static PendingIntent mPendingIntent;

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

            if( intent.hasExtra("Start")) {
                // Start aus App
                Log.v("Test", "HintergundService  Start aus Activity");
               
                // Deine GPS Abfrage
               
                startAlarmManager();
                stopSelf();
                return super.onStartCommand(intent, flags, startId);

            }

            if(intent.hasExtra("LÖSCHEN")){
                Log.v("Test", "HintergundService  Alarm Löschen");

                AlarmManager mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
                mAlarmManager.cancel(mPendingIntent);
                stopSelf();
                return super.onStartCommand(intent, flags, startId);
            }
           
        }
       
        stopSelf();

       return super.onStartCommand(intent, flags, startId);
    }
 
    void startAlarmManager(  ){
        Date aktuell = new Date();
        AlarmManager mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent mIntent = new Intent(this, Hintergrundservice.class);
        mIntent.putExtra("Start","Ein");
        mPendingIntent = PendingIntent.getService(this, 2, mIntent, 0);
        // neuen Alarm setseten  1 min nach acktueller Zeit.
        mAlarmManager.set(AlarmManager.RTC_WAKEUP, aktuell.getTime()+1000*60*1, mPendingIntent);
    }


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

// In der Activity

void startIntentHinderrund(){
        Intent sIntent = new Intent(MainActivity.this, Hintergrundservice.class);
        sIntent.putExtra("Start", "Ein");
        startService(sIntent);
    }
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: SlayNox
Das ist ein tolles Beispiel, vielen lieben Dank. Ich werde es morgen früh gleich umsetzen und Rückmeldung geben!
 

Ähnliche Themen

B
Antworten
3
Aufrufe
1.309
swa00
swa00
H
Antworten
2
Aufrufe
933
swa00
swa00
S
  • SlayNox
Antworten
1
Aufrufe
635
swa00
swa00
Zurück
Oben Unten