In diesem Beitrag aus unseren Archiven erklären wir, wie man einen eigenen Uber- oder Lyft-Klon erstellt und dabei PubNub zur Verfolgung der Fahrzeugposition verwendet. Auch wenn einige der Codeschnipsel auf ältere Bibliotheksversionen verweisen, ist alles in diesem Artikel für das heutige Publikum geeignet
Die Verfolgung des Echtzeit-Standortes eines anderen Geräts ist die Grundlage vieler beliebter mobiler, standortbezogener Apps, insbesondere wenn es um
wie Lyft und Uber. Der Aufbau dieser Funktionalität ist eine Herausforderung und kann mühsam sein, aber wir werden es mit einem einfachen Publish/Subscribe-Modell zwischen Fahrern und Fahrgästen vereinfachen, das von PubNub unterstützt wird.
Tutorial-Übersicht
Dervollständige Quellcode für dieses Tutorial ist hier verfügbar.
In diesem Tutorial zeigen wir Ihnen, wie Sie diesen häufigen Anwendungsfall in einer Android-Anwendung implementieren können. In der Beispielanwendung, die wir erstellen werden, wird der Benutzer zunächst gefragt, ob er der Fahrer oder der Beifahrer ist. Wenn ein Benutzer ein Fahrer ist, wird sein aktueller Standort in einem Kanal veröffentlicht und alle 5 Sekunden (in diesem Beispiel) oder so oft, wie es Ihre Anwendung erfordert, aktualisiert.
Ist ein Benutzer ein Beifahrer, abonniert er denselben Kanal, um Aktualisierungen des aktuellen Standorts seines Fahrers zu erhalten. Dieses Veröffentlichungs-/Abonnementmodell ist in der folgenden Abbildung dargestellt.
Google Maps API und PubNub-Einrichtung
Zunächst müssen wir unsere Gradle-Abhängigkeiten schreiben, um das PubNub Android SDK und die Google Play Services für die Kartenfunktionalität zu nutzen. Wir werden auch
com.fasterxml.jackson
Bibliotheken implementieren, um einige grundlegende JSON-Parsing von PubNub-Nachrichten zu vervollständigen .
Geben Sie in der build.gradle-Datei für das App-Modul unter dependencies den Codeblock des folgenden Befehls ein, um sicherzustellen, dass wir alles haben, was wir brauchen. Stellen Sie sicher, dass Google Play Services über den Android SDK Manager im Tools-Teil des Menüs installiert ist, damit wir die erforderlichen Bibliotheken implementieren können.
implementation group: 'com.pubnub', name: 'pubnub-gson', version: '4.12.0'
implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation "com.google.android.gms:play-services-location:15.0.1"
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.2'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.2'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.2'
Jetzt, da wir die notwendigen Abhängigkeiten haben, müssen wir sicherstellen, dass wir die notwendigen Berechtigungen in unserer Android Manifest-Datei haben. Mit der Erlaubnis, auf den Netzwerkstatus zuzugreifen und eine Internetverbindung herzustellen, kann der Benutzer nun die Verbindung zur PubNub-API herstellen. Wir werden die Standortberechtigung später verwenden, wenn wir den Standort des Fahrers abfragen.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Um PubNub in Ihrer Android-App einzurichten, müssen Sie eine PubNub-App im PubNub Admin Dashboard erstellen (kostenlos). Nach der Erstellung der App werden Ihnen ein Abonnement- und ein Veröffentlichungsschlüssel zugewiesen. Im folgenden Teil werden wir diese Anmeldeinformationen in unserer MainActivity-Klasse verwenden, um die Verbindung herzustellen.
Nun müssen wir unseren Google Maps API-Schlüssel einrichten, damit wir ihn in unserer Anwendung verwenden können. Rufen Sie dazu die Google Developer API-Konsole auf. Hier erstellen wir ein neues Projekt und generieren dann einen API-Schlüssel. Jetzt, da wir unseren API-Schlüssel haben, können wir den folgenden Code schreiben, um unsere Google Maps-API einzurichten und sie mit unserem generierten API-Schlüssel zu konfigurieren.
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value=”ENTER_KEY_HERE" />
Hauptaktivität
Die Klasse MainActivity wird drei wichtige Funktionen haben:
- Sie leitet den Benutzer zu seiner jeweiligen Schnittstelle, je nachdem, ob er Fahrer oder Beifahrer ist.
- Herstellen einer Verbindung zu PubNub für alle Benutzer der App
- Überprüfung der Zugriffsrechte für den Standort
Zunächst wollen wir unsere Nutzer in Fahrer und Beifahrer unterteilen und ihnen ihre jeweilige Benutzeroberfläche zur Verfügung stellen. Dazu erstellen wir eine Klasse DriverActivity und eine Klasse PassengerActivity, die wir in Teil 2 und Teil 3 ausführlich besprechen werden.
In MainActivity werden wir eine einfache, auf Schaltflächen basierende Schnittstelle erstellen, die den Benutzer auffordert, der App seine Rolle als Fahrer oder Beifahrer mitzuteilen. Mithilfe der Intent-Klasse von Android können wir von der MainActivity-Klasse in die DriverActivity-Klasse oder von der MainActivity-Klasse in die PassengerActivity-Klasse übergehen, je nachdem, welche Schaltfläche gedrückt wird.
Die Schnittstelle wird wie folgt aussehen:
driverButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, DriverActivity.class));
}
});
passengerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, PassengerActivity.class));
}
});
Als Nächstes erstellen wir mit unseren PubNub-Anmeldeinformationen aus dem Admin Dashboard eine
PNConfiguration
-Instanz, mit der wir eine
PubNub
-Instanz erstellen können ,die unsere App und PubNub verbindet. Diese PubNub-Instanz in der Klasse MainActivity wird öffentlich und statisch sein, damit sie sowohl von der DriverActivity als auch von der PassengerActivity aus zugänglich ist. Wir werden die untenstehende Methode in unserer
onCreate()
Methode der MainActivityaufrufen , damit PubNub für alle Benutzer unserer App richtig initialisiert wird. Dies alles ist im folgenden Code dargestellt.
private void initPubnub() {
PNConfiguration pnConfiguration = new PNConfiguration();
pnConfiguration.setSubscribeKey(Constants.PUBNUB_SUBSCRIBE_KEY);
pnConfiguration.setPublishKey(Constants.PUBNUB_PUBLISH_KEY);
pnConfiguration.setSecure(true);
pubnub = new PubNub(pnConfiguration);
}
Zum Schluss müssen wir sicherstellen, dass unser Benutzer seine Standortdienste für unsere App aktiviert hat. Dies kann mit folgendem Code geschehen. Wenn unser Benutzer keine Erlaubnis für den Zugriff auf den Fein- oder Grobstandort erteilt, wird unsere App diese Erlaubnis anfordern.
public void checkPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
) {//Can add more as per requirement
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
123);
}
}
Fahrer-Aktivität
Die einzige Aufgabe der Klasse DriverActivity besteht darin, den Standort des Fahrers zu ermitteln und diese Daten in einem bestimmten Zeitintervall im PubNub-Kanal zu veröffentlichen.
Schauen wir uns zunächst an, wie man eine Standortabfrage durchführt. Wir werden die Klasse
FusedLocationProviderClient
aus der Google Location API verwenden , umStandortaktualisierungen vom Fahrer anzufordern. Außerdem verwenden wir die Klasse
LocationRequest
, umwichtige Parameter unserer Standortanforderung festzulegen, z. B. die Priorität, den kleinsten Abstand und das Zeitintervall .
Für unser Beispiel verwenden wir ein Zeitintervall von 5000 Millisekunden, so dass der Standort alle 5 Sekunden aktualisiert wird, und eine kleinste Verschiebung von 10 Metern, um sicherzustellen, dass der Standort nur dann aktualisiert wird, wenn eine Änderung von mindestens 10 Metern vorliegt. Schließlich soll die Standortabfrage das GPS des Mobilgeräts verwenden, um eine hochgenaue Standortbestimmung zu ermöglichen. Diese hohe Genauigkeit ist wichtig, damit der Fahrgast eine detaillierte Anzeige seines Fahrers (dargestellt als Fahrzeugsymbol) sehen kann, der sich über die Karte zum Zielort bewegt.
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(5000);
locationRequest.setFastestInterval(5000);
locationRequest.setSmallestDisplacement(10);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
Nachdem wir nun unsere Parameter für die Standortanforderung definiert haben, werden wir dieses LocationRequest-Objekt an die Anforderung unseres FusedLocationProviderClient-Objekts für Standortaktualisierungen weitergeben.
mFusedLocationClient.requestLocationUpdates(locationRequest, new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
Location location = locationResult.getLastLocation();
...
Mit dem Standort, den wir von unserer Anfrage erhalten, müssen wir ihn in eine LinkedHashMap umwandeln, damit er in das gewünschte Format einer Standortnachricht im PubNub-Kanal passt. Die LinkedHashMap hat zwei Schlüssel "lat" und "lng" und die entsprechenden Werte für diese Schlüssel, beide als Strings.
Jetzt, da unsere Nachricht im Format einer LinkedHashMap vorliegt, können wir sie im PubNub-Kanal veröffentlichen. Dies wird im folgenden Code gezeigt. Beachten Sie, dass wir
MainActivity.pubnub
verwenden müssen, um die PubNub-Instanz zu erhalten, die wir zuvor in der onCreate-Methode der Main Activity erstellt haben. Wir wollen nicht mehrere Verbindungen aufbauen, indem wir PubNub erneut initialisieren. Stattdessen verwenden wir einfach die Verbindung, die wir bereits in der Klasse MainActivity erstellt haben.
MainActivity.pubnub.publish()
.message(message)
.channel(Constants.PUBNUB_CHANNEL_NAME)
.async(new PNCallback<PNPublishResult>() {
@Override
public void onResponse(PNPublishResult result, PNStatus status) {
// handle publish result, status always present, result if successful
// status.isError() to see if error happened
if (!status.isError()) {
System.out.println("pub timetoken: " + result.getTimetoken());
}
System.out.println("pub status code: " + status.getStatusCode());
}
});
Passenger-Aktivität
In unserer Klasse PassengerActivity wollen wir eine MapView erstellen, um den Standort des Fahrers anzuzeigen, der gerade aktualisiert wird. Um das MapView-UI-Element zu unserer PassengerActivity hinzuzufügen, müssen wir den folgenden Code in die entsprechende layout.xml-Datei für diese Activity aufnehmen.
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mapwithmarker.MapsMarkerActivity" />
Anschließend instanziieren Sie das Objekt
MapFragment
unter Verwendung der in der Layout-Datei definierten ID. In unserem Beispielcode haben wir "map" als ID verwendet. Wir werden auch unsere Methode
onMapReady
ein richten, indem wir PassengerActivity
onMapReadyCallback
implementieren lassen. Dann übergeben wir
PassengerActivity.this
als Parameter an die Methode
getMapAsync
unseres MapFragment-Objekts . Dies ermöglicht uns die Einrichtung unserer onMapReady-Callback-Methode für unser MapFragment-Objekt.
mMapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mMapFragment.getMapAsync(PassengerActivity.this);
Nun wollen wir den Standortkanal des Fahrers abonnieren, damit wir Aktualisierungen des aktuellen Standorts des Fahrers erhalten. Dies muss geschehen, sobald die Karte bereit ist, also werden wir diesen Code in unsere Callback-Methode aufnehmen. Außerdem müssen wir einen Listener mit einem
SubscribeCallback
hinzufügen, damit unsere Anwendung weiß, wie sie beim Empfang einer Nachricht reagieren soll.
Wie in den PubNub-Android-Dokumenten beschrieben, müssen wir dies in der Reihenfolge tun, in der wir zuerst den Listener hinzufügen und dann die PubNub-Instanz für den Kanal abonnieren. Sobald wir eine Nachricht erhalten, wollen wir die JSON-Ausgabe in eine LinkedHashMap konvertieren, die wir leicht parsen können, um den Längen- und Breitengrad zu erhalten. Um die JSON-Ausgabe in eine LinkedHashMap zu konvertieren, müssen wir die Klasse
JsonUtil
importieren , die die Methode
fromJson()
enthält , die uns dies ermöglicht. Diese Klasse ist im vollständigen Quellcode dargestellt. Sobald wir den Standort des Fahrers haben, müssen wir die Benutzeroberfläche aktualisieren, indem wir unsere Methode
updateUI()
aufrufen und den neuen Standort als Parameter übergeben .
MainActivity.pubnub.addListener(new SubscribeCallback() {
@Override
public void status(PubNub pub, PNStatus status) {
}
@Override
public void message(PubNub pub, final PNMessageResult message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
Map<String, String> newLocation = JsonUtil.fromJson(message.getMessage().toString(), LinkedHashMap.class);
updateUI(newLocation);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
@Override
public void presence(PubNub pub, PNPresenceEventResult presence) {
}
});
MainActivity.pubnub.subscribe()
.channels(Arrays.asList(Constants.PUBNUB_CHANNEL_NAME)) // subscribe to channels
.execute();
Unsere updateUI-Methode hat zwei Fälle, auf die sie treffen wird.
Der erste Fall ist, dass es noch keine Markierung für den Fahrer gibt. In diesem Fall instanziieren wir das Objekt
Marker
und setzen seine Position auf den ersten getrackten Standort des Fahrers. Außerdem müssen wir die Karte vergrößern, um eine Straßenansicht des Fahrzeugs des Fahrers zu erhalten.
Der zweite Fall ist, dass es bereits eine Markierung für den Fahrer gibt. In diesem Fall müssen wir das Markierungsobjekt nicht erneut instanziieren. In diesem Fall müssen wir das Marker-Objekt nicht neu instanziieren, sondern wir verschieben einfach die Position des Markers auf der Karte an seine neue Position. Dies geschieht durch den Aufruf der Methode
animateCar()
. Wir müssen auch sicherstellen, dass wir die Kamera der MapView an die neue Position verschieben, wenn sie sich außerhalb der Grenzen der Kamera befindet. Auf diese Weise befindet sich der Marker des Fahrers immer innerhalb der Grenzen der MapView. Dies wird im folgenden Code gezeigt.
private void updateUI(Map<String, String> newLoc) {
LatLng newLocation = new LatLng(Double.valueOf(newLoc.get("lat")), Double.valueOf(newLoc.get("lng")));
if (driverMarker != null) {
animateCar(newLocation);
boolean contains = mGoogleMap.getProjection()
.getVisibleRegion()
.latLngBounds
.contains(newLocation);
if (!contains) {
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLng(newLocation));
}
} else {
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
newLocation, 15.5f));
driverMarker = mGoogleMap.addMarker(new MarkerOptions().position(newLocation).
icon(BitmapDescriptorFactory.fromResource(R.drawable.car)));
}
}
In unserer animateCar-Methode müssen wir unseren Auto-Marker in einer sanften, linienförmigen Weise an seine neue Position bewegen. Die Animation muss 5 Sekunden dauern, da unsere App alle 5 Sekunden eine Aktualisierung der Position des Fahrers erhält. Auf diese Weise wird jedes Mal, wenn die Animation vorbei ist, die neue Position aktualisiert, um die nächste Animation zu starten. Auf diese Weise minimieren wir die Verzögerung bei der Animation des Autos.
Wir werden auch die
LatLngInterpolator
Schnittstelle unten. Dies ermöglicht uns die Implementierung der Methode
interpolate()
implementieren, die eine neue LatLng-Koordinate zurückgibt, die einen Bruchteil des Weges zur endgültigen newLocation darstellt. Dieser Bruchteil wird mit der Methode
getAnimatedFraction()
. Wir rufen diese Methode von einer
ValueAnimator
Instanz auf, die wir von der
onAnimationUpdate()
Callback-Methode erhalten. Dieser Anteil steigt stetig an, so dass er nach 5 Sekunden 1 ist (bis zum neuen Standort). Diese Animation des Autos wird in den folgenden Codeschnipseln gezeigt.
private void animateCar(final LatLng destination) {
final LatLng startPosition = driverMarker.getPosition();
final LatLng endPosition = new LatLng(destination.latitude, destination.longitude);
final LatLngInterpolator latLngInterpolator = new LatLngInterpolator.LinearFixed();
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(5000); // duration 5 seconds
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
try {
float v = animation.getAnimatedFraction();
LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, endPosition);
driverMarker.setPosition(newPosition);
} catch (Exception ex) {
}
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
valueAnimator.start();
}
private interface LatLngInterpolator {
LatLng interpolate(float fraction, LatLng a, LatLng b);
class LinearFixed implements LatLngInterpolator {
@Override
public LatLng interpolate(float fraction, LatLng a, LatLng b) {
double lat = (b.latitude - a.latitude) * fraction + a.latitude;
double lngDelta = b.longitude - a.longitude;
if (Math.abs(lngDelta) > 180) {
lngDelta -= Math.signum(lngDelta) * 360;
}
double lng = lngDelta * fraction + a.longitude;
return new LatLng(lat, lng);
}
}
}
Weitere Verbesserungen
Wenn Sie dieses Beispiel weiterentwickeln möchten, könnten Sie z. B. eine Funktion hinzufügen, bei der sich der Marker dreht, wenn sich das Auto dreht. Diese Funktion würde erfordern, dass wir ein Schlüssel-Wert-Paar zu unserer Nachrichtenstruktur hinzufügen. Wir müssten den Schlüssel "Peilung" und den Wert einfügen. Dann würden wir einfach die Markierung in Abhängigkeit von der Peilung des Fahrers drehen.
Herzlichen Glückwunsch!
Nun, da wir die MainActivity, DriverActivity und PassengerActivity erstellt haben, haben wir erfolgreich das Publish/Subscribe-Modell erstellt, das notwendig ist, um eine einfache Lyft/Uber Android-Demo zu erstellen. Klicken Sie hier für den vollständigen Quellcode.
Wenn der eingebettete Inhalt auf dieser Seite nicht verfügbar ist, kann er auch unter https://www.youtube.com/embed/kQ6EzqoQu5A angesehen werden.
Weitere Ressourcen
Wie kann PubNub Ihnen helfen?
Dieser Artikel wurde ursprünglich auf PubNub.com veröffentlicht.
Unsere Plattform unterstützt Entwickler bei der Erstellung, Bereitstellung und Verwaltung von Echtzeit-Interaktivität für Webanwendungen, mobile Anwendungen und IoT-Geräte.
Die Grundlage unserer Plattform ist das größte und am besten skalierbare Echtzeit-Edge-Messaging-Netzwerk der Branche. Mit über 15 Points-of-Presence weltweit, die 800 Millionen monatlich aktive Nutzer unterstützen, und einer Zuverlässigkeit von 99,999 % müssen Sie sich keine Sorgen über Ausfälle, Gleichzeitigkeitsgrenzen oder Latenzprobleme aufgrund von Verkehrsspitzen machen.
PubNub erleben
Sehen Sie sich die Live Tour an, um in weniger als 5 Minuten die grundlegenden Konzepte hinter jeder PubNub-gestützten App zu verstehen
Einrichten
Melden Sie sich für einen PubNub-Account an und erhalten Sie sofort kostenlosen Zugang zu den PubNub-Schlüsseln
Beginnen Sie
Mit den PubNub-Dokumenten können Sie sofort loslegen, unabhängig von Ihrem Anwendungsfall oder SDK
Top comments (0)