GPS
Das GPS (Global Positioning System) kann die aktuelle Position mit der Hilfe von Satelliten bestimmen. GPS liefert die Werte Longitude, Latitude. Außerdem die Höhe in Metern und die Zeit als koordinierte Weltzeit (UTC) in Millisekunden seit 1.1.1970.Aus mehreren Messwerten kann Android die Geschwindigkeit in m/s und die Ausrichtung (bearing) errechnen.
Für die Bestimmung werden mindestens vier Satelliten benötigt. Android kann eine etwas ungenauere Position allerdings auch aus den Informationen des Netzwerkproviders empfangen.
Permissions
Der Zugriff auf den Standort ist eine "dangerous permission". Die Berechtigung für den Zugriff muss nicht nur im Manifest verankert, sondern explizit beim Benutzer erfragt werden.Eine detaillierte Beschreibung erfolgt an anderer Stelle.
Berechtigung im Manifest
Der Zugriff auf die Location muss im Manifest deklariert werden. Dazu wird ein Eintrag mit uses-permission angelegt.
... <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>ACCESS_FINE_LOCATION steht für GPS. Die gröbere Auflösung kommt vom Mobilfunkanbieter und wird mit ACCESS_COARSE_LOCATION angesprochen.
Permission prüfen
Damit diese Frage nicht dauernd gestellt wird, kann zunächst mit checkSelfPermission geprüft werden, ob die Erlaubnis nicht schon zuvor gegeben wurde.if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {Neben FINE muss auch COARSE abgefragt werden. Daraus ergibt sich:
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // bitte den Anwender um Erlaubnis ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 123); }
Permissions erfragen
Gibt es keine Permission, muss der Benutzer gefragt werden, bevor auf GPS zugegriffen werden darf. Das erfolgt durch den Aufruf von requestPermissions:ActivityCompat.requestPermissions(MainActivity.this, new String[] { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 123);Der zweite Parameter ist ein String-Array, das alle angeforderten Permissions aufführt. Der dritte Parameter ist eine Zahl, an der Android diese Anfrage im Callback wieder erkennt.
Antwort auf den Dialog
Der Dialog blockiert nicht die App. Stattdessen wird die Antwort des Anwenders ein Ereignis auslösen, dass durch die Methode onRequestPermissionsResult gefangen wird. Erst dort ist man sicher, dass man den GPS-Listener in Betrieb nehmen kann.@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { // Requests können für mehrere Ressourcen eingetroffen sein. // Prüfe erst einmal, ob es mein Request-Code ist if (requestCode==myRequestCode) { if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Zugriff erlaubt, dennoch noch einmal fragen if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { // Starte die GPS-Anfragen (siehe unten) locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, locationListener); } } else { // Zugriff verboten } } else { super.onRequestPermissionsResult(requestCode, permissions,grantResults); } }
LocationManager und LocationListener
Anschließend kann mit dem Sammeln der GPS-Daten begonnen werden. Dazu schaltet der LocationManager den LocationListener scharf.Der LocationManager ist ein Service von Android. Er muss zunächst initialisiert werden.
private LocationManager locationManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... locationManager = (LocationManager) this.getSystemService( Context.LOCATION_SERVICE);Damit ist der Service erreichbar. Die eigentlichen GPS-Ereignisse werden wie alle anderen Ereignisse unter Android durch einen Listener gefangen, der in diesem Fall LocationListener heißt.
Wie immer bei Listenern gibt es mehrere Möglichkeiten. Die Activity-Klasse kann den LocationListener implementieren oder man legt eine Instanz von LocationListener an, was dazu führt, dass man auch dessen abstrakte Methoden sofort implementieren muss.
Die Verbindung zwischen dem Location-Manager und dem Location-Listener schlägt die Methode requestLocationUpdates. Der letzte Parameter ist der Listener. Falls die Activity-Klasse LocationListener implementiert, ist dieser this.
requestLocationUpdates(LocationManager.GPS_PROVIDER, minTime, minDist, listener)Das Abschalten der Updates erfolgt mit removeUpdates.
locationManager.removeUpdates(locationListener);
Ist GPS eingeschaltet?
Feststellen, ob Location überhaupt eingeschaltet ist (ab Android 9):if ( ! locationManager.isLocationEnabled() ) { // GPS ist ausgeschaltetAlternative:
if (manager.isProviderEnabled(Context.LOCATION_SERVICE)) {
Das Interface LocationListener
Der LocationListener ist ein Interface für den Empfang aller Ereignisse rund um eine Location. Sie erzwingt die folgenden Methoden:- void onLocationChanged(Location loc): Wird bei einem erkannten Standortwechsel aufgerufen.
- void onStatusChanged(String provider, int status, Bundle bundle)
- void onProviderEnabled(@NonNull String provider)
- void onProviderDisabled(@NonNull String provider)
Die Klasse Location
Die Location trägt den gefundenen Ort. Diesen erfährt das Programm in der Methode onLocationChanged des LocationListeners als Parameter. Hier erfährt man alle Informationen über den gefundenen Standort.- double getLatitude(): x-Richtung, positiv in Richtung Osten
- double getLongitude(): y-Richtung, positiv in Richtung Norden
- double getAltitude(): liefert die Höhe in m. Mit Vorsicht zu genießen.
- float getBearing(): liefert die Ausrichtung, die das GPS-System aus den letzten Bewegungen ermittelt.
- distanceTo(Location dest): liefert Entfernung in m zu anderer Location.
- void reset()
@Override public void onLocationChanged(Location location) { String str = "Long: " + location.getLongitude() + " Lat: " + location.getLatitude() + " Höhe: " + location.getAltitude() + " Genauigkeit: " + location.getAccuracy() + " Zeitstempel: " + location.getTime(); tv.setText(str); }
Activity
Das folgende Programm aktiviert das GPS und löst alle Sekunde bei 10 Metern Positionsunterschied ein onLocationChanged aus. Diese Updates werden bei onPause gestoppt, um keinen zu hohen Akkuverbrauch zu haben.Auch die anderen Ereignisse des LocationListeners werden gefangen und angezeigt.
package de.willemer.gpstester; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.os.Build; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; public class MainActivity extends AppCompatActivity implements LocationListener { final String TAG = "GPStest"; LocationManager lm; TextView tv; @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tvGps); lm = (LocationManager) getSystemService(LOCATION_SERVICE); } @Override protected void onResume() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 10, this); } super.onResume(); } @Override protected void onPause() { // GPS braucht sehr viel Akku, also stoppen, wenn nicht gebraucht lm.removeUpdates(this); super.onPause(); } @Override protected void onStop() { finish(); super.onStop(); } @Override public void onLocationChanged(Location location) { tv.setText(location.toString()); double north = location.getLatitude(); double east = location.getLongitude(); } @Override public void onStatusChanged(String s, int status, Bundle bundle) { String msg = "Status changed"; switch (status) { case LocationProvider.OUT_OF_SERVICE: msg = "Status: Außer Betrieb"; break; case LocationProvider.TEMPORARILY_UNAVAILABLE: msg = "Status: derzeit nicht verfügbar"; break; case LocationProvider.AVAILABLE: msg = "Status: Verfügbar"; break; } Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); } @Override public void onProviderEnabled(String s) { Toast.makeText(this, "GPS Aktiviert", Toast.LENGTH_SHORT).show(); } @Override public void onProviderDisabled(String s) { // Settings aufrufen, wenn kein Provider vorhanden Intent intent = new Intent( android.provider.Settings. ACTION_LOCATION_SOURCE_SETTINGS); startActivity(intent); } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { // Requests können für mehrere Ressourcen eingetroffen sein. // Prüfe erst einmal, ob es mein Request-Code ist if (requestCode==myRequestCode) { if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Zugriff erlaubt, dennoch noch einmal fragen if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { // Starte die GPS-Anfragen (siehe unten) locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, locationListener); } } else { // Zugriff verboten Toast.makeText( this, "App benötigt GPS Permission!\nDie App wird beendet", Toast.LENGTH_LONG).show(); this.finish(); } } else { super.onRequestPermissionsResult(requestCode, permissions,grantResults); } } }Wird nur eine grobe Auflösung benötigt, wird statt der Konstante GPS_PROVIDER nun NETWORK_PROVIDER eingesetzt. Auf einem Emulator funktioniert das nur, wenn dieser die Google API enthält.
Der Bildschirm entspricht dem Hello-World. Allerdings wurde dem Label der Name tv verpasst, damit es aus der Activity heraus mit Ausgaben versorgt werden kann.
Eine Landkarte mit OpenStreetMap
Der Dienst BigMap kann eine Landkarte der Umgebung als PNG-Datei erzeugen.In die Landkarte mit Scrollrad oder über die Plus- und Minussymbole hineingehen. Durch Draggen der Maus kann die Position verschoben werden.
Ist der gewünschte Ausschnitt gefunden, dann Submit klicken.
- Im folgenden Dialog wird die Größe feinjustiert.
- Wichtig ist die Angabe, in welchem Bereich sich die Karte befindet
(Bbox is x.xxxxxx, yy.yyyyyy, x.xxxxxx, yy.yyyyyy). x ist Longitude, y ist Latitude.
Am besten darüber streichen, Strg-C.
- In die Karte mit der rechten Maustaste und Grafik speichern.
- Im Dateidialog Strg-V und dann daraus einen hübschen Dateinamen bauen.
- Abspeichern