Java >> Java tutorial >  >> Java

Android:GPS-positions- og placeringsstrategier

Bemærk:Dette blogindlæg vedligeholdes ikke længere, fordi det er forældet. Eventuelle spørgsmål og kommentarer vil ikke blive besvaret.

Har du nogensinde spekuleret på, hvor nemt eller svært det er at implementere GPS-sporing til din Android-applikation?
Ved du, hvilke udfordringer der er ved at balancere mellem den bedste nøjagtighed, strømforbrug og registreringshastighed, når du anskaffer en placering?

Det kan være svært at få en nøjagtig brugerplacering på en mobilenhed. Generelt er der tre bekymringer, der vil udfordre dig mest:

  1. Flere placeringskilder   – Der er mere end én udbyder, hvorfra lokationen erhverves (GPS, WI-FI og Cell-ID), og de varierer alle meget i nøjagtighed, strømforbrug og hastighed.
  2. Brugerbevægelse  – På grund af brugerbevægelser skal lokaliseringsdata opdateres med et rimeligt tidsinterval.
  3. Varierende nøjagtighed – Placeringsestimater fra forskellige udbydere må ikke være med samme nøjagtighed. Placeringen fra den nyeste udbyder kan være væsentligt mindre nøjagtig end estimeringen fra en ældre udbyder.

At have disse tre punkter i tankerne og omhyggeligt at vælge og optimere dine valg kan gøre en kæmpe forskel i en god eller dårlig brugeroplevelse, når det kommer til GPS-sporing.

BEMÆRK:Android 4.4.2 (API 19) bruges.

Lytter efter placeringsopdateringer

Lad os starte med et simpelt eksempel, hvor vi henter LocationManager-systemtjenesten og tildeler LocationListener til den for at håndtere placeringsopdateringer. LocationManager-tjenesten giver applikationer mulighed for at modtage periodiske opdateringer af mobilenhedens geografiske placering. LocationManager-klassen startes ikke direkte, den hentes fra systemtjenester. Lad os tage et kort kig på kildekoden her:

// Don't initialize location manager, retrieve it from system services.
LocationManager locationManager = (LocationManager) this
        .getSystemService(Context.LOCATION_SERVICE);
 
LocationListener locationListener = new LocationListener() {
 
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }
 
    @Override
    public void onProviderEnabled(String provider) {
        Toast.makeText(MainActivity.this,
                "Provider enabled: " + provider, Toast.LENGTH_SHORT)
                .show();
    }
 
    @Override
    public void onProviderDisabled(String provider) {
        Toast.makeText(MainActivity.this,
                "Provider disabled: " + provider, Toast.LENGTH_SHORT)
                .show();
    }
 
    @Override
    public void onLocationChanged(Location location) {
        // Do work with new location. Implementation of this method will be covered later.
        doWorkWithNewLocation(location);
    }
};
 
long minTime = 5 * 1000; // Minimum time interval for update in seconds, i.e. 5 seconds.
long minDistance = 10; // Minimum distance change for update in meters, i.e. 10 meters.
 
// Assign LocationListener to LocationManager in order to receive location updates.
// Acquiring provider that is used for location updates will also be covered later.
// Instead of LocationListener, PendingIntent can be assigned, also instead of 
// provider name, criteria can be used, but we won't use those approaches now.
locationManager.requestLocationUpdates(getProviderName(), minTime,
        minDistance, locationListener);

// Initialiser ikke lokationsadministratoren, hent den fra systemtjenester.LocationManager locationManager =(LocationManager) denne .getSystemService(Context.LOCATION_SERVICE); LocationListener locationListener =new LocationListener() { @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public void onProviderEnabled(String provider) { Toast.makeText(MainActivity.this, "Provider enable" , Toast.LENGTH_SHORT) .show(); } @Override public void onProviderDisabled(String provider) { Toast.makeText(MainActivity.this, "Provider disabled:" + provider, Toast.LENGTH_SHORT) .show(); } @Override public void onLocationChanged(Location location) {// Arbejd med ny placering. Implementering af denne metode vil blive dækket senere. doWorkWithNewLocation(placering); }}; lang minTid =5 * 1000; // Minimum tidsinterval for opdatering i sekunder, dvs. 5 sekunder. lang minDistance =10; // Minimum afstandsændring for opdatering i meter, dvs. 10 meter. // Tildel LocationListener til LocationManager for at modtage lokationsopdateringer.// Anskaffelse af udbyder, der bruges til lokationsopdateringer, vil også blive dækket senere.// I stedet for LocationListener kan PendingIntent tildeles, også i stedet for // udbydernavn kan kriterier bruges, men vi vil ikke bruge disse metoder nu.locationManager.requestLocationUpdates(getProviderName(), minTime, minDistance, locationListener);

TIP: Når du kører denne kode, vil du se meddelelserne "Provider aktiveret" og "Provider deaktiveret", når du modtager placeringsopdateringer fra udbydere.

Du har måske bemærket, at vi har tildelt en minimumstid (minTime ) og minimumsafstand (minDistance ) for at requestLocationUpdates sammen med LocationListener til LocationManager ?

Det virker måske ikke så vigtigt for dig, men et omhyggeligt valg af disse værdier kan gøre en forskel.

  1. minTid – Klogt valg af værdi for minTime vil hjælpe dig med at reducere strømforbruget. Det, du skal huske på, er, at den forløbne tid mellem placeringsopdateringer aldrig vil være mindre end minTime , men det kan være større, fordi det er påvirket af implementeringen af ​​hver given udbyder og det opdateringsinterval, der anmodes om af andre kørende programmer.
  2. minDistance – I modsætning til minTime denne parameter kan slås fra ved at indstille dens værdi til 0. Men hvis minDistance er indstillet til en værdi større end 0, vil lokationsudbyderen kun sende opdateringer til din applikation, hvis lokationen er ændret i det mindste med en given afstand. Denne parameter er ikke en stor strømbesparer som minTime , men det skal dog huskes.

Disse to parametre fungerer i AND forhold, så for at modtage placeringsopdatering skal begge deres betingelser være opfyldt (dvs. der er gået mere end 5 sekunder, og afstandsændringen er større end 10 meter ).

Udbyder navn, der også skal angives for requestLocationUpdates vil blive dækket senere, når vi gennemgår den del af at lave vores bedste udbydervalgskriterier.

Implementering af LocationListener giver os mulighed for at have nogle oplysninger om tilstanden af ​​brugt udbyder. Vi kan overvåge udbyderstatus (onStatusChanged ) som giver os oplysninger, hvis udbyderen ikke er i stand til at hente en placering, eller er blevet tilgængelig efter en periode med inaktivitet, og vi kan også overvåge, hvornår en given udbyder er deaktiveret (onProviderDisabled ) eller aktiveret (onProviderEnabled ). Den vigtigste metode til LocationListener for os er onLocationChanged metode, hvor placering hentes, vil vi gennemgå det senere, når vi beskriver placeringsstrategier. Lad os gennemgå det grundlæggende først.

Hvad er en placering?

Placering er dataklasse, der repræsenterer geografisk placering. Det har mange egenskaber, der kan være nyttige, men lad os fokusere på de vigtigste:

  • Udbyder – Navnet på den udbyder, der genererede placeringsrettelsen. Dette kan bruges til at vurdere nøjagtigheden og pålideligheden af ​​placering.
  • Nøjagtighed – Estimeret nøjagtighed af given placering i meter. Det er defineret som nøjagtighed med 68 % tillid. Det betyder, at hvis du tegner en cirkel i midten af ​​bredde- og længdegraden af ​​en given placering, har cirklen repræsenteret af denne parameter en sandsynlighed for 68 % at den indeholder den nøjagtige placering af enheden. Husk, at nøjagtighedsniveauet afhænger meget af arten af ​​den brugte udbyder, og det kan variere betydeligt.
  • Tid – Repræsenterer UTC-tiden i millisekunder, når den givne placeringsfix er blevet genereret. Hvis man antager, at ældre placeringer har en meget større sandsynlighed for at være falske end nye, kan det føre til "udviklerfælder", fordi placeringsrettelser fra 10 sekunder siden stadig kan have en bedre nøjagtighed end nyere placeringer, der kommer fra en meget mere nøjagtig udbyder.
  • BreddegradLængdegrad og Højde er parametre, der definerer modtaget geografisk placering.

Vælg din udbyder

For at begynde at gøre din placeringsbevidste applikation, er det bedst at starte med at vælge den ønskede udbyder, der passer bedst til dine behov. Android API var en god måde at hjælpe dig med at overvinde dit dilemma. De gav os kriterierne som er en pæn klasse til at opbygge kriterierne for, hvilken lokationsudbyder er valgt. Du skal tænke på kriterier som en måde ikke at filtrere eksisterende udbydere fra, men som et listerækkefølgekriterie for lokationsudbydere. Udbydere kan bestilles i henhold til nøjagtighed, strømforbrug, evne til at rapportere højde, hastighed og pejling og pengeomkostninger. Ja, i de fleste tilfælde behøver du kun et godt valg, så tag et kig på kildekoden nedenfor:

/**
 * Get provider name.
 * @return Name of best suiting provider.
 * */
String getProviderName() {
    LocationManager locationManager = (LocationManager) this
            .getSystemService(Context.LOCATION_SERVICE);
 
    Criteria criteria = new Criteria();
    criteria.setPowerRequirement(Criteria.POWER_LOW); // Chose your desired power consumption level.
    criteria.setAccuracy(Criteria.ACCURACY_FINE); // Choose your accuracy requirement.
    criteria.setSpeedRequired(true); // Chose if speed for first location fix is required.
    criteria.setAltitudeRequired(false); // Choose if you use altitude.
    criteria.setBearingRequired(false); // Choose if you use bearing.
    criteria.setCostAllowed(false); // Choose if this provider can waste money :-)
 
    // Provide your criteria and flag enabledOnly that tells
    // LocationManager only to return active providers.
    return locationManager.getBestProvider(criteria, true);
}

/** * Hent udbyderens navn. * @return Navn på bedst egnede udbyder. * */String getProviderName() { LocationManager locationManager =(LocationManager) denne .getSystemService(Context.LOCATION_SERVICE); Kriteriekriterier =new Criteria(); criteria.setPowerRequirement(Criteria.POWER_LOW); // Vælg dit ønskede strømforbrugsniveau. criteria.setAccuracy(Criteria.ACCURACY_FINE); // Vælg dit nøjagtighedskrav. criteria.setSpeedRequired(true); // Vælg, hvis hastighed for første placeringsfix er påkrævet. criteria.setAltitudeRequired(false); // Vælg om du bruger højde. criteria.setBearingRequired(false); // Vælg om du bruger pejling. criteria.setCostAllowed(false); // Vælg, om denne udbyder kan spilde penge :-) // Angiv dine kriterier og flag aktiveret Kun der fortæller // LocationManager kun at returnere aktive udbydere. return locationManager.getBestProvider(kriterier, sand);}

Der er en fin artikel her som tager udbyderens historie mere detaljeret, men for at gøre en lang historie kort, her er de tilgængelige udbydere med fordele og ulemper for hver enkelt.

Brugertilladelser

For at din applikation kan få adgang til placeringstjenester, skal du lære den at anmode om tilladelser, som brugeren skal bekræfte ved installation af applikationen.

Heldigvis kræver placeringstjenester ikke mange tilladelser, der kan skræmme en bruger til paranoia, der er kun to tilladelsesgrupper af interesse:

  1. ACCESS_COARSE_LOCATION – inkluderer kun tilladelse til NETWORK_PROVIDER.
  2. ACCESS_FINE_LOCATION – inkluderer tilladelse både til NETWORK_PROVIDER og GPS_PROVIDER.
<manifest ... >
    <!-- Use one of these permissions that suit your needs most. -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>

Placeringsstrategier

For den bedste brugeroplevelse og laveste strømforbrug bør der udtænkes en god placeringsstrategi. Betydningen af ​​udtrykket placeringsstrategi er dybest set den måde, du beslutter dig for at implementere, når placeringsopkøbet er færdig, hvilken udbyder du bruger, og hvor længe har du tænkt dig at blive ved med at bruge den. Der er et par regler, du måske vil følge:

  • Når du anskaffer den første placeringsfix, skal du bruge den hurtigste udbyder. Netværk eller passiv udbyder giver den hurtigste placeringsfix, selvom de også er mest upålidelige. Brug disse udbydere til at give brugeren indledende hint om deres placering og senere forbedre nøjagtigheden, efterhånden som mere nøjagtige udbydere initialiseres. Der er en måde, hvorpå du nemt kan lave en hurtig rettelse:
String provider = LocationManager.NETWORK_PROVIDER;
 
// Returns last known location, this is the fastest way to get a location fix.
Location fastLocation = locationManager.getLastKnownLocation(provider);

String provider =LocationManager.NETWORK_PROVIDER; // Returnerer sidst kendte placering, dette er den hurtigste måde at få en placeringsfix.Location fastLocation =locationManager.getLastKnownLocation(provider);

  • Mens du venter på, at GPS-udbyderen initialiserer (eller en anden mere præcis), skal du håndtere placeringsrettelser fra andre udbydere, filtrere dem ved at afvise mindre nøjagtige og ældre. Hold fast i den mest nøjagtige placering, smid den ikke væk. En eksempelalgoritme til at bortfiltrere lokationer er nedenfor.
/**
* Make use of location after deciding if it is better than previous one.
*
* @param location Newly acquired location.
*/
void doWorkWithNewLocation(Location location) {
    if(isBetterLocation(getOldLocation(), location) {
        // If location is better, do some user preview.
        Toast.makeText(MainActivity.this,
                        "Better location found: " + provider, Toast.LENGTH_SHORT)
                        .show();
    }
 
    setOldLocation(location);
}
 
/**
* Time difference threshold set for one minute.
*/
static final int TIME_DIFFERENCE_THRESHOLD = 1 * 60 * 1000;
 
/**
* Decide if new location is better than older by following some basic criteria.
* This algorithm can be as simple or complicated as your needs dictate it.
* Try experimenting and get your best location strategy algorithm.
* 
* @param oldLocation Old location used for comparison.
* @param newLocation Newly acquired location compared to old one.
* @return If new location is more accurate and suits your criteria more than the old one.
*/
boolean isBetterLocation(Location oldLocation, Location newLocation) {
    // If there is no old location, of course the new location is better.
    if(oldLocation == null) {
        return true;
    }
 
    // Check if new location is newer in time.
    boolean isNewer = newLocation.getTime() > oldLocation.getTime();
 
    // Check if new location more accurate. Accuracy is radius in meters, so less is better.
    boolean isMoreAccurate = newLocation.getAccuracy() < oldLocation.getAccuracy();       
    if(isMoreAccurate && isNewer) {         
        // More accurate and newer is always better.         
        return true;     
    } else if(isMoreAccurate && !isNewer) {         
        // More accurate but not newer can lead to bad fix because of user movement.         
        // Let us set a threshold for the maximum tolerance of time difference.         
        long timeDifference = newLocation.getTime() - oldLocation.getTime(); 
 
        // If time difference is not greater then allowed threshold we accept it.         
        if(timeDifference > -TIME_DIFFERENCE_THRESHOLD) {
            return true;
        }
    }
 
    return false;
}

/*** Gør brug af placering efter at have besluttet, om den er bedre end den forrige.** @param placering Nyligt erhvervet placering.*/void doWorkWithNewLocation(Location location) { if(isBetterLocation(getOldLocation(), location) {// If placeringen er bedre, lav en forhåndsvisning af brugeren. Toast.makeText(MainActivity.this, "Bedre placering fundet:" + udbyder, Toast.LENGTH_SHORT) .show(); } setOldLocation(location);} /*** Tærskelværdi for tidsforskel sat i et minut.*/static final int TIME_DIFFERENCE_THRESHOLD =1 * 60 * 1000; /*** Beslut om ny placering er bedre end ældre ved at følge nogle grundlæggende kriterier.* Denne algoritme kan være så enkel eller kompliceret, som dine behov tilsiger det. * Prøv at eksperimentere og få din bedste placeringsstrategialgoritme.* * @param oldLocation Gammel placering brugt til sammenligning.* @param newLocation Nyt erhvervet placering sammenlignet med gammel.* @return Hvis den nye placering er mere nøjagtig og passer til dine kriterier mere end old one.*/boolean isBetterLocation(Location oldLocation, Location n ewLocation) { // Hvis der ikke er nogen gammel placering, er den nye placering selvfølgelig bedre. if(gammelplacering ==null) { return true; } // Tjek om den nye placering er nyere i tide. boolean isNewer =newLocation.getTime()> oldLocation.getTime(); // Tjek om den nye placering er mere nøjagtig. Nøjagtigheden er radius i meter, så mindre er bedre. boolean isMoreAccurate =newLocation.getAccuracy() -TIME_DIFFERENCE_THRESHOLD) { return true; } } returner falsk;}

Du kan finde mange eksempler på placeringsfiltrering online, denne minder meget om den, der leveres af Android-dokumentationen her.

  • Når du har håndteret en placeringsfix med passende nøjagtighed, skal du vise den til brugeren og stoppe med at lytte til placeringsopdateringer (hjælper med at spare strøm).
LocationManager locationManager = (LocationManager) this
                .getSystemService(Context.LOCATION_SERVICE);
 
// Stop listening to location updates, also stops providers.
locationManager.removeUpdates(locationListener);

LocationManager locationManager =(LocationManager) denne .getSystemService(Context.LOCATION_SERVICE); // Stop med at lytte til placeringsopdateringer, stopper også providers.locationManager.removeUpdates(locationListener);

Walking the rope

Efterhånden som din erfaring som en "Android Location Strategies" knowhow-mand udvides over tid, vil du sandsynligvis vende tilbage til placeringsfiltreringsalgoritmen med jævne mellemrum for at justere den for at opnå bedre resultater. Foreløbig forstår du svarene på spørgsmål som "Hvordan ser en normal tidslinje for anskaffelse af sted ud?" og "Hvordan kan en nyere placering være mindre nøjagtig end en gammel?" burde gøre dit liv væsentligt nemmere.

Hvis du beslutter dig for at følge retningslinjerne, bør din typiske tidslinje se sådan ud:

  • Begynd at lytte efter placeringer, få en hurtig løsning med lavt nøjagtighedsniveau. Husk, at hurtig fix-placering vil blive modtaget fra netværksudbyderen (hvis du ikke er sikker, tjek navnet på udbyderen på den givne Placering ). Bevar denne placering, indtil en bedre ankommer.
  • Hvis der modtages en ny placeringsrettelse, skal du lave en sammenligning med dens udbyder, tid og nøjagtighed og kun afvise den, hvis den nye er bedre. Hvis dette er den første opdatering, du modtog efter en hurtig løsning, skal du kun acceptere placeringer, der kommer fra WI-FI eller GPS.
  • Du bliver nødt til at vente noget tid, indtil GPS-udbyderen har varmet op (fundet tilstrækkeligt antal satellitter til præcis placeringsestimat). I løbet af denne tid vil du modtage adskillige placeringsrettelser fra netværks- og WI-FI-udbyderen. Bevar den bedste placering ved hjælp af din filtreringsalgoritme.
  • For at gøre tingene endnu sværere, hvis brugeren er på farten, kan det være en meget udfordrende opgave at finde præcis placering. Den bedste fremgangsmåde er at bruge hurtigere (mindre) placeringsopdateringsintervaller, indtil GPS-udbyderen bliver tilgængelig. Større mængder af lokationer i et tidsinterval giver dig mulighed for at træffe det bedste valg, men husk, sæt mere præcise udbydere foran i beslutningskøen.
  • Efter at have modtaget placering med tilstrækkelig høj nøjagtighed, afhængigt af din applikation, kan du beslutte at stoppe med at lytte til placeringer, hvis du har brug for et groft estimat af brugere, eller sænke placeringsopdateringsintervallerne, hvis du har brug for at spore brugernes bevægelse gennem tid .

Svar på spørgsmålet "Hvordan kan en nyere placering være mindre nøjagtig end en gammel?" kommer direkte fra tidslinjen:På grund af brugerbevægelser og varierende nøjagtighed af modtagne placeringer, kan en enkelt parameter, i dette tilfælde "tid" ikke bruges til præcist at foretage en vurdering af brugernes opholdssted. Når du designer en lokationsfiltreringsalgoritme, skal du altid bruge mindst disse tre parametre: Udbyder (navn på udbyder), Nøjagtighed og Tid .


Java tag