Location abfrage in einem Service

B

Braesident

Ambitioniertes Mitglied
1
Mein Versuch GPS Daten aus einem Service heraus zu laden schlägt leider immer fehl.
Code:
Fehler beim GPS auslesen: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
Fehler beim NET auslesen: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
dieser Betrifft die Zeilen:
lmGPS.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0,llGPS);
lmNET.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0,llNET);
in folgendem Code

Code:
public class MessagingService extends FirebaseMessagingService {

    String locationAdress = "";
    SharedPreferences sp;

    @Override
    public void onMessageReceived( RemoteMessage remoteMessage ) {

        super.onMessageReceived(remoteMessage )

        sp = PreferenceManager.getDefaultSharedPreferences(MessagingService.this);
        String action = sharedPreferences.getString(
                getString( R.string.preference_action_key ) ,
                getString( R.string.preference_action_default));

        if ( remoteMessage.getFrom().equals( meineMsgID ) ){


            if ( action.equals("send") ){

                LocationManager lmGPS = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
                LocationListener llGPS = new myGPSLocationListener();

                try{

                    lmGPS.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0,llGPS);

                } catch (Exception e){ Log.e( "FB_MSG_SRV" , "Fehler beim GPS auslesen: " + e); }

                if ( locationAdress == "" ){

                    LocationManager     lmNET = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
                    LocationListener    llNET = new myNETLocationListener();

                    try{

                        lmNET.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0,llNET);

                    } catch (Exception e){ Log.e( "FB_MSG_SRV" , "Fehler beim NET auslesen: " + e); }

                }

                sendToServer( locationAdress );

            }
        }
    }

    class myGPSLocationListener implements LocationListener{

        @Override
        public void onLocationChanged(Location location) {

            if(location != null){

                double  pLat = location.getLatitude();
                double  pLong = location.getLongitude();

                if ( "" + pLat != "" && "" + pLong != "" ){

                    int     grad = (int) pLat;
                    double  tmp = (pLat - grad) * 60;
                    int     min = (int) tmp;
                    double  sek = (tmp - min) * 60;
                    locationAdress = Integer.toString(grad) + "°"
                            + Integer.toString(min) + "'"
                            + Double.toString(roundOn(sek,3))
                            + "\"" + iif( grad >= 0 , "N" , "S" );


                    grad = (int) pLong;
                    tmp = (pLong - grad) * 60;
                    min = (int) tmp;
                    sek = (tmp - min) * 60;
                    locationAdress = locationAdress + " "
                            + Integer.toString(grad)
                            + "°" + Integer.toString(min) + "'"
                            + Double.toString(roundOn(sek,3))
                            + "\"" + iif( grad >= 0 , "E" , "W" );

                }


            }
        }

        @Override
        public void onProviderDisabled(String provider) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

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

        }
    }

    class myNETLocationListener implements LocationListener{

        @Override
        public void onLocationChanged(Location location) {

            if(location != null){

                double  pLat = location.getLatitude();
                double  pLong = location.getLongitude();


                int     grad = (int) pLat;
                double  tmp = (pLat - grad) * 60;
                int     min = (int) tmp;
                double  sek = (tmp - min) * 60;
                locationAdress = Integer.toString(grad) + "°" + Integer.toString(min) + "'" + Double.toString(roundOn(sek,3)) + "\"" + iif( grad >= 0 , "N" , "S" );


                grad = (int) pLong;
                tmp = (pLong - grad) * 60;
                min = (int) tmp;
                sek = (tmp - min) * 60;
                locationAdress = locationAdress + "+" + Integer.toString(grad) + "°" + Integer.toString(min) + "'" + Double.toString(roundOn(sek,3)) + "\"" + iif( grad >= 0 , "E" , "W" );


            }
        }

        @Override
        public void onProviderDisabled(String provider) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

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

        }
    }

    public String iif( boolean frage , String p1 , String p2 ){

        if (frage=true)
            return p1;

        return p2;
    }

    public double roundOn( double zahl , int n ){

        double  faktor = 1;

        for ( int i = 1 ; i <= n ; i++ ){
            faktor = faktor * 10;
        }

        return Math.round(zahl*faktor)/faktor;
    }

    public void sendToServer( final String text ){

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

                try {

                    // ...

                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

Wie muss die Abfrage sonst gestaltet werden?
 
Hallo,

vielleicht hilft dir dieser weg weiter.

HINWEIS : in einem einmal gestarteten Service wirst du keinen Erfolg haben, wenn du kontinuierlich
etwas haben willst (loop) .. du musst mit alarmgetriggerten Service arbeiten.
Ausserdem ist das kontinuierliche Abfragen in einem Service NICHT zulässig

Dieses Beispiel ist alarmgetriggert


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

    private static final String TAG = Globals.TAG_BASE + "LOWPOWER_GPSLOG";

    private boolean currentlyProcessingLocation = false;
    private LocationRequest locationRequest;
    private GoogleApiClient googleApiClient;

    private CountDownTimer p_CountDownTimer;
    private static int       SAVE_LAST_VALUE = 0;
    private static SharedPreferences settings;


    @Override
    ////////////////////////////////////////////////////////////////////////////////////////////////
    public void onCreate() {
        super.onCreate();




        p_CountDownTimer = new CountDownTimer(30000, 1000)
        {

            public void onTick(long millisUntilFinished) {}

            public void onFinish() {
                MyLog.d(TAG, "CANCELED - no GPS data");
                stopLocationUpdates();
                stopSelf();
            }
        }.start();
    }

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

        settings = getBaseContext().getSharedPreferences(Globals.APP_PREFS, Context.MODE_PRIVATE);

        // if we are currently trying to get a location and the alarm manager has called this again,
        // no need to start processing a new location.
        if (!currentlyProcessingLocation)
        {
            //  Auch  weitermachen , wenn das  device  in den Sleep Modus  geht
            PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
            PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"TRACKYCAR_GPSLOW");
            wakeLock.acquire();

            currentlyProcessingLocation = true;
            SAVE_LAST_VALUE = intent.getIntExtra("SAVE",0);
            startTracking();
        }

        return START_NOT_STICKY;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    private void startTracking()
    {
        //MyLog.d(TAG, "startTracking");

        GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
        int result = googleAPI.isGooglePlayServicesAvailable(this);
        if (result == ConnectionResult.SUCCESS)
//            if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS)
        {

            googleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();

            if (!googleApiClient.isConnected() || !googleApiClient.isConnecting())
            {
                googleApiClient.connect();
            }
        } else
        {
            MyLog.d(TAG, "unable to connect to google play services.");
        }
    }


    @Override
    ////////////////////////////////////////////////////////////////////////////////////////////////
    public void onDestroy() {
        super.onDestroy();
    }

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

    @Override
    ////////////////////////////////////////////////////////////////////////////////////////////////
    public void onLocationChanged(Location location)
    {
        if (location != null)
        {
            // we have our desired accuracy of 500 meters so lets quit this service,
            // onDestroy will be called and stop our location uodates
            if (location.getAccuracy() < 200.0f)
            {
                stopLocationUpdates();
                //sendLocationDataToWebsite(location);
                p_CountDownTimer.cancel();
                // umrechnung meter/per/sec  in km/h
                double _speed   = (location.getSpeed() / 1000.0d) / (1.0 / 3600.0);
                double _lat     = location.getLatitude();
                double _lon     = location.getLongitude();
                double _course  = location.getBearing();

                if (SAVE_LAST_VALUE == 1)
                {
                    MyLog.d(TAG, "LOCATION SAVED : " + _lat + ", " + _lon + " ac: " + location.getAccuracy() + " dir: " + _course + " sp: " + _speed);
                    SharedPreferences.Editor editor = settings.edit();
                    editor.putFloat("pref_last_position_lat",(float) location.getLatitude());
                    editor.putFloat("pref_last_position_lon",(float) location.getLongitude());
                    editor.apply();
                }
                else
                {

                    float last_lat  = settings.getFloat("pref_last_position_lat", 0.0F);
                    float last_lon  = settings.getFloat("pref_last_position_lon", 0.0F);
                    float _distance =  distFrom((float)_lat,(float)_lon,(float)last_lat,(float)last_lon);
                    MyLog.d(TAG, "LOCATION CURRENT : " + _lat + ", " + _lon + " ac: " + location.getAccuracy() + " dir: " + _course + " sp: " + _speed+ " distance: " + _distance);

                    Globals.POSITION_ALARM_LON        = _lon;
                    Globals.POSITION_ALARM_LAT        = _lat;
                    Globals.POSITION_ALARM_SPEED      = _speed;
                    Globals.POSITION_ALARM_DIST       = (double) _distance;

                    if (_speed    > 3.0   ) OnlineControl.sendAlarm(OnlineControl.ALARMTYPE_SPEED);
                    if (_distance > 2000.0) OnlineControl.sendAlarm(OnlineControl.ALARMTYPE_POSITION);


                }

                stopSelf();
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    private void stopLocationUpdates() {
        if (googleApiClient != null && googleApiClient.isConnected()) {
            googleApiClient.disconnect();
        }
    }

    /**
     * Called by Location Services when the request to connect the
     * client finishes successfully. At this point, you can
     * request the current location or start periodic updates
     */
    @Override
    ////////////////////////////////////////////////////////////////////////////////////////////////
    public void onConnected(Bundle bundle)
    {
        //MyLog.d(TAG, "onConnected");

        locationRequest = LocationRequest.create();
        locationRequest.setInterval(2000); // milliseconds
        locationRequest.setFastestInterval(2000); // the fastest rate in milliseconds at which your app can handle location updates
        locationRequest.setExpirationDuration(20 * 1000); // Abbruch  nach 20  sekunden
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        try {
            LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
        } catch (SecurityException e) {

        }
    }

    @Override
    ////////////////////////////////////////////////////////////////////////////////////////////////
    public void onConnectionFailed(ConnectionResult connectionResult)
    {
        MyLog.d(TAG, "onConnectionFailed");
        stopLocationUpdates();
        p_CountDownTimer.cancel();
        stopSelf();
    }

    @Override
    ////////////////////////////////////////////////////////////////////////////////////////////////
    public void onConnectionSuspended(int i)
    {
        MyLog.d(TAG, "GoogleApiClient connection has been suspend");
    }

    ////////////////////////////////////////////////////////////////////////////////////////////
    private static float distFrom(float lat1, float lng1, float lat2, float lng2)
    {
        double earthRadius = 6371000; //meters
        double dLat = Math.toRadians(lat2-lat1);
        double dLng = Math.toRadians(lng2-lng1);
        double a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *  Math.sin(dLng/2) * Math.sin(dLng/2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        float dist = (float) (earthRadius * c);

        return dist;
    }

}



P.S die Aufrufe wie du sie machst sind deprecated, bitte GoogleAPiClient verwenden
lg
Stefan
 
Zuletzt bearbeitet:
Hi Stefan, erstmal danke dafür. Werd mir dein Beispiel und GoogleAPiClient gleich zu Hause nochmal anschauen.

Ich wollt die Daten eigentlich nicht kontinuierlich abrufen, sondern nur bei Abfrage durch eine eingehende Msg.
Oder bleibt der Listener einmal aufgerufen aktiv?
 
Hallo Braesident,

Wenn ich es richtig verstanden habe , funktioniert ja dein Message-Service bereits
Dann brauchst du nur noch GoogleApiClient zu implementieren.

Beispiel haste ja oben

lg
Stefan
 
swa00 schrieb:
Hallo,

HINWEIS : in einem einmal gestarteten Service wirst du keinen Erfolg haben, wenn du kontinuierlich
etwas haben willst (loop) .. du musst mit alarmgetriggerten Service arbeiten.
Ausserdem ist das kontinuierliche Abfragen in einem Service NICHT zulässig

Hallo Stefan,

Danke für die Info.
Wo hast Du denn Hinweis mit den "kontinuierliche Abfragen" in der Doku gefunden?
 
Hallo Markus,

Ich wollte es auch nicht zuerst glauben - bin aber allerdings selbst opfer geworden :)

Bekannt ist, dass Android im Stande ist , jederzeit ohne Info den Service zu beenden und dann ist Schluss - es sei denn man setzt ihn foreground.
Auch die Verwendung von PowerManager.PARTIAL_WAKE_LOCK ( CPU) hilf nichts , ab 4.1 macht Android irgendwann mal zu, wenn das Device in Ruhe gelassen wird.

Man kann zwar im Service eine zeitlang in einem Async o.ä. arbeiten - sollte aber immer dazu wissen , dass irgendwann mal Schluss ist.
Soweit ich festgestellt habe , sogar schneller , als wenn eine normale Activity im background ist.

Stackoverflow ist leider voll von diesem "Probem" ..

Neben dieser Erkenntnis habe ich auch festgestellt, dass es stark ROM-Image abhängig ist.
Ich vermute mal - natürlich ohne beweis - dass Aufgrund des angestrebten Akku-Durchhalte-Vermögens da
hier oder da "unnütze" Dinge eher abgeschossen werden ( aus Sicht des Roms und Hersteller)


Seitdem ich Alarmgetriggert arbeite , ist Ruhe im Karton.

Ein schönes & triviales Beispiel
How To Keep An Android Service Running – Digital Stuff

Nachteil : Die Zeitglieder des requests sollten ein paar sekunden auseinanderliegen (Pi mal Daumen)
Wichtig : stopself() innerhalb des Service

Soweit ich gelesen habe , soll das bei Nougat sogar noch verschärfter stattfinden - warten wir mal ab

lg
Stefan
 
Zuletzt bearbeitet:
Hi Stefan, ich bin gerade dabei dein Beispiel umzusetzen. Allerdings ist die onStartCommand im FirebaseMessagingService eine 'final method' - kann der Inhalt wie im Beispiel oben in die onCreate oder macht das keinen Sinn?
 
Moin ,

ich gehe mal davon aus , dass es keinen unterschied macht , wann du das Ganze initialisierst .
Schau halt zu , dass du die Resourcen wieder frei gibst , sonst bekommst du ein memory lag.

P.S mit FireBase habe ich leider keine Erfahrungen ......


lg
Stefan
 
Zuletzt bearbeitet:
Hi Stefan,

ein ähnliches Problem hatte ich auch mal. Es gibt eine Möglichkeit für kontinuierliche Abfragen in einem Service.
Ein "normaler" Service läuft im MainThread. Sprich er muss sich die Rechenzeit mit allen andern Vorgänge im MainThread teilen. Sprich es kann zu ANR kommen.

Es gibt aber eine Möglichkeit, dieses Problem zu umgehen. Man kann einen Service in eine eigenen Prozess auslagern (Dieser hat dann eine eigene PID).
Processes and Threads | Android Developers

Das ganze ist aber nicht ganz unproblematisch. Bei Zugriffen auf Datenbanken, SharedPreferences, Settings usw. kann die Integrität der gespeicherten Daten verletzt werden. Sprich die vorhandenen Daten können dabei geschrottet werden. D.h. man muss die Daten sperren (Lock, Transaktionssicherheit, usw), damit keinen Mist passiert..
 
Guten Morgen Markus,

ich Danke Dir.
Welches funktionierendes Alternativ-Verfahren hast du denn schlussendlich angewendet ?
Das würde mich interessieren - man möchte ja nicht Auslernen :)

Hättest du einen Code snippet ??

lg
Stefan
 
Zuletzt bearbeitet:
Ich habe den Service als Prozess ausgelagert (im Android Manifest). Die Information tausche ich normalerweise über Brodcast Receiver aus. Daten schreibe ich normalerweise in eine SQLite Datenbank. (In de Datenbank benutze ich Primär und Fremdschlüssel. Zusätzlich Transaktionssicherheit und Contains.) Mit SharedPreferences habe ich sehr schlechte Erfahrung gemacht. Normalerweise läuft meine komplette Internetkommunikation im Service.
 
@Markus

Nur dass ich verstehe : Mag ich aber einen service bauen , der beim Booting ausgeführt bekomme, dann würde das obige bespiel nicht gehen , da
die App einmalig ( mit layoutgeraffel) gestartet werden müsste (Process-start) , richtig ?
 
Nein, du kannst den Service direkt nach dem Boot starten, ohne die App selber starten zu müssen.
Das heißt, du brauchst einen BroadcastReceiver, der auf das Intent ACTION_BOOT_COMPLETED hört. Intent | Android Developers
Daneben muss die App die Permission RECEIVE_BOOT_COMPLETED haben. Manifest.permission | Android Developers

Der Service ist dann unabhängig vom der GUI, und läuft komplett im Hintergrund.
 

Ähnliche Themen

B
Antworten
3
Aufrufe
1.305
swa00
swa00
W
  • WuDiDong
Antworten
3
Aufrufe
765
jogimuc
J
Zurück
Oben Unten