Serviciile de redare a locației getLastLocation returnează null (Programare, Java, Android, Google Play Services)

Walid Ammar a intrebat.
a intrebat.

Încerc să ascult schimbările de locație, dar uneori onLocationChanged callback nu este niciodată apelat și getLastLocation returnează null, în timp ce Google Maps funcționează întotdeauna perfect.

Notă

Dacă îmi repornesc dispozitivul, serviciul de localizare va funcționa doar pentru ~2 zile; după aceea, nici aplicația mea, nici exemplul SDK vor funcționa, în timp ce Google Maps funcționează în continuare.

Acesta este codul serviciului meu:

public class VisitService extends Service implements
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener, 
        LocationListener {

    private final IBinder mBinder = new VisitBinder();


    // A request to connect to Location Services
    private LocationRequest mLocationRequest;

    // Stores the current instantiation of the location client in this object
    private LocationClient mLocationClient;


    private boolean mLocationServiceConnected = false;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        initLocation();
        return super.onStartCommand(intent, flags, startId);
    }

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

    public void startVisit() {
        if (!servicesConnected()) {
            listener.onVisitStartError();
            return;
        }

        if (mLocationServiceConnected) {
            if (isAcceptableLocation(mLocationClient.getLastLocation())) {
                Toast.makeText(this, "You have arrived!", Toast.LENGTH_LONG);
            } else {
                mVisitListener.onVisitStartError();
            }
        }    
    }

    private boolean isAcceptableLocation(Location location) {
        if (location == null) {
            Toast.makeText(this, "Location is null", Toast.LENGTH_LONG).show();
            return false;
        }

        return true;
    }


    private void initLocation() {

        if (mLocationRequest == null) {
            // Create a new global location parameters object
            mLocationRequest = LocationRequest.create();
            /*
             * Set the update interval
             */
            mLocationRequest
                    .setInterval(LocationUtils.UPDATE_INTERVAL_IN_MILLISECONDS);

            // Use high accuracy
            mLocationRequest
                    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

            // Set the interval ceiling to one minute
            mLocationRequest
                    .setFastestInterval(LocationUtils.FAST_INTERVAL_CEILING_IN_MILLISECONDS);

        }

        if (mLocationClient == null) {
            mLocationClient = new LocationClient(this, this, this);
            mLocationClient.connect();
        }
    }

    /**
     * Verify that Google Play services is available before making a request.
     * 
     * @return true if Google Play services is available, otherwise false
     */
    private boolean servicesConnected() {

        // Check that Google Play services is available
        int resultCode = GooglePlayServicesUtil
                .isGooglePlayServicesAvailable(this);

        // If Google Play services is available
        if (ConnectionResult.SUCCESS == resultCode) {
            // In debug mode, log the status
            Log.d(LocationUtils.APPTAG,
                    getString(R.string.play_services_available));

            return true;
        } else {

            return false;
        }
    }

    public class VisitBinder extends Binder {
        public VisitService getService() {
            return VisitService.this;
        }
    }


    /**
     * In response to a request to start updates, send a request to Location
     * Services
     */
    private void startPeriodicUpdates() {

        mLocationClient.requestLocationUpdates(mLocationRequest, this);
        Toast.makeText(this, "Location Update Requested", Toast.LENGTH_LONG).show();
    }

    /**
     * In response to a request to stop updates, send a request to Location
     * Services
     */
    private void stopPeriodicUpdates() {
        mLocationClient.removeLocationUpdates(this);

    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.d(LocationUtils.APPTAG, "Location Services Connection faild");


    }

    @Override
    public void onConnected(Bundle bundle) {
        Log.d(LocationUtils.APPTAG, "Location Services Connected");
        mLocationServiceConnected = true;
        startPeriodicUpdates();
    }

    @Override
    public void onDisconnected() {
        mLocationServiceConnected = false;
    }

    @Override
    public void onLocationChanged(Location location) {
        Toast.makeText(this, "Location has been changed", Toast.LENGTH_LONG).show();
    }
}

Manifest Android

<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"/>

Comentarii

  • Din documentație: If a location is not available, which should happen very rarely, null will be returned. Se pare că vă loviți de rarely moment, ar putea fi din cauza dispozitivului. Ați încercat să obțineți ultima locație cunoscută de la LocationManager în schimb? –  > Por Nikola Despotoski.
  • Aș solicita, de asemenea, „ACCESS_COARSE_LOCATION” în cazul în care locația fină nu este disponibilă. –  > Por Robert.
  • Ați putea aștepta onLocationChanged să fie apelată înainte de a apela startVisit() metoda. De exemplu, ai putea să o faci în felul următor, nu-i așa? Ce vrei să spui mai exact prin „Nu pot primi onLocationChanged” ? –  > Por MagicMicky.
  • @MagicMicky înseamnă onLocationChanged nu este niciodată invocată de serviciile de localizare a jocurilor. –  > Por Walid Ammar.
  • @Robert Am încercat să adaug permisiunea ACCESS_COARSE_LOCATION, dar nu s-a schimbat nimic. –  > Por Walid Ammar.
8 răspunsuri
AndroidHacker

În primul rând este un bug. Și eu m-am confruntat cu această problemă.

Puteți încerca o soluție simplă.

Faceți o cerere de actualizare a locației înainte de a încerca să obțineți ultima locație, adică Solicitați actualizări ale locației înainte de a încerca să găsiți ultima locație utilizând getLastLocation();

La fel ca:

LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Criteria crta = new Criteria();
crta.setAccuracy(Criteria.ACCURACY_FINE);
crta.setAltitudeRequired(true);
crta.setBearingRequired(true);
crta.setCostAllowed(true);
crta.setPowerRequirement(Criteria.POWER_LOW); 
String provider = locationManager.getBestProvider(crta, true);
Log.d("","provider : "+provider);
// String provider = LocationManager.GPS_PROVIDER; 
locationManager.requestLocationUpdates(provider, 1000, 0, locationListener);
Location location = locationManager.getLastKnownLocation(provider); 

Această implementare se bazează pe LocationManager. Vă rugăm să optați pentru Location Client pentru același lucru.

Sper să vă fie de ajutor!

UPDATE

În acest caz, puteți opta și pentru metoda onLocationChanged(), deoarece vă va actualiza de fiecare dată când locația este modificată, în timp ce metoda getLastLocation() returnează cea mai bună locație cunoscută, care poate fi NULL.

Conform documentului, metoda GetLastLocation() va returna NULL în cazuri foarte rare. Dar acest lucru se întâmplă des și în cazul meu.

CEA MAI RECENTĂ ACTUALIZARE

Băieți, doar o singură actualizare în ceea ce privește GPS-ul:

În primul rând, trebuie să folosim GoogleApiClient în loc de LocationClient clasa.

În al doilea rând, trebuie să implementăm GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener

În al treilea rând, trebuie să suprascriem onConnected() , onConnectionSuspended() și onConnectionFailed() metoda .

În rest, sunteți gata de plecare.

NOROC!

Comentarii

  • Prin cazuri rare cred că Google se referă la dispozitive precum Samsung GT-S7562, Xiaomi redmi 2, Galaxy core prime. Am fost la un showroom cu Exemple de aplicații Google Location în github și am testat pe mai multe dispozitive. Se pare că funcționează doar pe dispozitive mai noi, cum ar fi Galaxy Note 2, Note 4, OnePlus2, Xiaomi redmi note 4g, OPX, etc. –  > Por Saifur Rahman Mohsin.
Deep Shah

Mi-am dezvoltat aplicația pe baza codului de mai jos pe care îl puteți folosi dacă doriți Numele aplicației mele este „https://play.google.com/store/apps/details?id=com.deep.profilemaper” .

Aici este codul meu : BackgroundLocationService.java

public class BackgroundLocationService extends Service implements
    GooglePlayServicesClient.ConnectionCallbacks,GooglePlayServicesClient.OnConnectionFailedListener,LocationListener  {

    IBinder mBinder = new LocalBinder();

    private LocationClient mLocationClient;
    private LocationRequest mLocationRequest;
   // Flag that indicates if a request is underway.
   private boolean mInProgress;

   private Boolean servicesAvailable = false;

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

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


    mInProgress = false;
    // Create the LocationRequest object
    mLocationRequest = LocationRequest.create();
    // Use high accuracy
    mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
    // Set the update interval to 5 seconds
    mLocationRequest.setInterval(Constants.UPDATE_INTERVAL);
    // Set the fastest update interval to 1 second
    mLocationRequest.setFastestInterval(Constants.FASTEST_INTERVAL);

    servicesAvailable = servicesConnected();

    /*
     * Create a new location client, using the enclosing class to
     * handle callbacks.
     */
    mLocationClient = new LocationClient(this, this, this);


}

private boolean servicesConnected() {

    // Check that Google Play services is available
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) {

        return true;
    } else {

        return false;
    }
}

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

    if(!servicesAvailable || mLocationClient.isConnected() || mInProgress)
        return START_STICKY;

    setUpLocationClientIfNeeded();
    if(!mLocationClient.isConnected() || !mLocationClient.isConnecting() && !mInProgress)
    {
        appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Started", Constants.LOG_FILE);
        mInProgress = true;
        mLocationClient.connect();
    }

    return START_STICKY;
}

/*
 * Create a new location client, using the enclosing class to
 * handle callbacks.
 */
private void setUpLocationClientIfNeeded()
{
    if(mLocationClient == null) 
        mLocationClient = new LocationClient(this, this, this);
}

// Define the callback method that receives location updates
@Override
public void onLocationChanged(Location location) {
    // Report to the UI that the location was updated
    String msg = Double.toString(location.getLatitude()) + "," +
            Double.toString(location.getLongitude());
    Log.d("debug", msg);
    // Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    appendLog(msg, Constants.LOCATION_FILE);
}

@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());
}

public void appendLog(String text, String filename)
{       
   File logFile = new File(filename);
   if (!logFile.exists())
   {
      try
      {
         logFile.createNewFile();
      } 
      catch (IOException e)
      {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }
   try
   {
      //BufferedWriter for performance, true to set append to file flag
      BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true)); 
      buf.append(text);
      buf.newLine();
      buf.close();
   }
   catch (IOException e)
   {
      // TODO Auto-generated catch block
      e.printStackTrace();
   }
}

@Override
public void onDestroy(){
    // Turn off the request flag
    mInProgress = false;
    if(servicesAvailable && mLocationClient != null) {
        mLocationClient.removeLocationUpdates(this);
        // Destroy the current location client
        mLocationClient = null;
    }
    // Display the connection status
    // Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ": Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
    appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Stopped", Constants.LOG_FILE);
    super.onDestroy();  
}

/*
 * 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) {

    // Request location updates using static settings
    mLocationClient.requestLocationUpdates(mLocationRequest, this);
    appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Connected", Constants.LOG_FILE);

}

/*
 * Called by Location Services if the connection to the
 * location client drops because of an error.
 */
@Override
public void onDisconnected() {
    // Turn off the request flag
    mInProgress = false;
    // Destroy the current location client
    mLocationClient = null;
    // Display the connection status
    // Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ": Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
    appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Disconnected", Constants.LOG_FILE);
}

/*
 * Called by Location Services if the attempt to
 * Location Services fails.
 */
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    mInProgress = false;

    /*
     * Google Play services can resolve some errors it detects.
     * If the error has a resolution, try sending an Intent to
     * start a Google Play services activity that can resolve
     * error.
     */
    if (connectionResult.hasResolution()) {

    // If no resolution is available, display an error dialog
    } else {

      }
    }

}

***Constants.java***

public final class Constants {

// Milliseconds per second
private static final int MILLISECONDS_PER_SECOND = 1000;
// Update frequency in seconds
private static final int UPDATE_INTERVAL_IN_SECONDS = 30;
// Update frequency in milliseconds
public static final long UPDATE_INTERVAL = MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
// The fastest update frequency, in seconds
private static final int FASTEST_INTERVAL_IN_SECONDS = 30;
// A fast frequency ceiling in milliseconds
public static final long FASTEST_INTERVAL = MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
// Stores the lat / long pairs in a text file
public static final String LOCATION_FILE = "sdcard/location.txt";
// Stores the connect / disconnect data in a text file
public static final String LOG_FILE = "sdcard/log.txt";


/**
 * Suppress default constructor for noninstantiability
 */
private Constants() {
    throw new AssertionError();
    }
}

Acest cod conține unele variabile, cum ar fi data și ora curentă și constante precum LOG_File, care este doar pentru scopuri de logare, așa că am exclus acest cod. Acest cod este perfect testat și funcționează. Încercați-l.

Mulțumesc.

Comentarii

  • Am folosit Fused Location Provider în acest cod, astfel încât nu trebuie să comutați între furnizorul de rețea sau GPS. Vedeți developer.android.com/google/play-services/location.html –  > Por Deep Shah.
  • Mulțumesc pentru sugestia dvs., cred că este un fel de bug pe dispozitivele vechi Android, +1 pentru o interfață frumoasă:) –  > Por Walid Ammar.
  • Vă mulțumim pentru revizuire @MohammadWalid –  > Por Deep Shah.
Walid Ammar

Primul

Este un fel de bug pe dispozitivele Android vechi.

Se pare că API-urile de localizare au un comportament diferit pe diferite versiuni Android (LocationManager are probleme pe Android >= 4.1, în timp ce Play Services are probleme pe Android 2.3), vezi aici.

Deci, ceea ce am ajuns să am este mai jos:

Încercarea de a prelua ultima locație prin serviciile de joc, dacă nu a reușit, încercați cu LocationManager.

Al doilea

Aș dori să vă mulțumesc tuturor pentru sugestiile valoroase, am primit indiciul de a utiliza soluția anterioară de la postul lui AndroidHacker, așa că cred că el este cel care merită recompensa.

Androider

Această problemă poate apărea dacă Google Maps nu a determinat încă locația dispozitivului dvs. Încercați să lansați Google Maps. Dacă nu se poate, atunci remediați această problemă în setările dispozitivului (de exemplu, activând WLAN + GPS).

danny117

Folosiți getLastLocation() într-un alt scop decât cel pentru care a fost conceput. getLastLocation() este utilizat atunci când o aplicație care cunoaște locația este la prima pornire. După două zile ar trebui să aveți o fixare foarte precisă. De ce nu ați urmat ghidurile de proiectare location aware.

Aș spune că trebuie să vă întoarceți START_STICKY. Dar ați codificat onStartCommand() și onBind(). De asemenea, ar trebui să porniți serviciul și apoi să vă legați la el pentru a obține START_STICKY să funcționeze după ce clienții s-au dezlegat.

Nu verificați GooglePlay în serviciu. Serviciul trebuie să fie ușor și să verifice dacă GooglePlay se află într-o activitate înainte de a porni serviciul. Cu cât este mai ușor un serviciu, cu atât mai multe șanse are să rămână în memorie. Rulați serviciul Service într-un proces separat.

MagicMicky

De la documentul, este clar că este posibil ca uneori să se întâmple că getLastLocation() returnează null. Așadar, în funcție de ceea ce înțelegeți prin „Nu pot primi callback-uri onLocationChanged„, ați putea să amânați startVisit apelurile importante (afișarea toastului în acest moment) până când veți primi acest callback onLocationChanged. În acest fel, dacă aveți acces direct la ultima locație cunoscută, acest lucru va fi direct, iar în caz contrar așteptați callback-urile onLocationChanged

public class VisitService extends Service implements
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener, 
        LocationListener {
    //...
    boolean mIsInitialized=false;
    Location mCurrentLocation;
    boolean mStartVisitsCalled=false;
    //...

    public void startVisit() {
        if (!servicesConnected()) {
            listener.onVisitStartError();
            return;
        }

        if (mLocationServiceConnected) {
        if (((mLocationClient.getLastLocation() != null && isAcceptableLocation(mLocationClient.getLastLocation()))
                || (isInitialized && isAcceptableLocation(mCurrentLocation))) {
                //doNiceStuff();
            } else
                mStartVisitsCalled=true;
                //Wait...
            }
        }    
    }
    //...

    @Override
    public void onLocationChanged(Location location) {
        this.mIsInitialized=true;
        this.mCurrrentLocation = location;
        if(mStartVisitsCalled) {
            //delayed doNiceStuff();
        }

    }
}

Comentarii

  • din păcate, uneori onLocationChange nu este niciodată apelat, iar atunci când se întâmplă acest lucru, exemplul SDK nu va funcționa. –  > Por Walid Ammar.
  • ați încercat să utilizați requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener); și requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener); pentru a solicita actualizări de locație mai precise? (așa cum este detaliat aici stackoverflow.com/a/9008453/1376834 ) – –  > Por MagicMicky.
  • Și, ați putea încerca să verificați în onConnected() care sunt valorile de getLastLocation? –  > Por MagicMicky.
  • Din moment ce folosiți serviciile Google Play,Puteți activa Wifi și să vă conectați la SSID și să încercați din nou. M-am confruntat cu această problemă mai devreme, Deoarece furnizorul de locație fuzionat încearcă să găsească locația, în cazul în care nu este prezent wifi-ul, uneori nu obține schimbarea locației? –  > Por AnkitSomani.
nPn

Este posibil ca serviciul dvs. să fie pur și simplu ucis, se poate întâmpla. încercați să schimbați valoarea returnată de onStartCommand() în

return STICKY_START;

Acest lucru va reporni serviciul de îndată ce resursele sunt disponibile.

Aici este secțiunea relevantă din referință API

public static final int START_STICKY

Adăugată la nivelul 5 al APIConstant pentru a fi returnată de la onStartCommand(Intent, int, int): dacă procesul acestui serviciu este ucis în timp ce este pornit (după returnarea de la onStartCommand(Intent, int, int)), atunci lăsați-l în starea de pornire, dar nu păstrați această intenție livrată. Ulterior, sistemul va încerca să recreeze serviciul. Deoarece acesta se află în starea de pornire, va garanta apelarea onStartCommand(Intent, int, int) după crearea noii instanțe de serviciu; dacă nu există nicio comandă de pornire în așteptare care să fie transmisă serviciului, aceasta va fi apelată cu un obiect de intenție nul, deci trebuie să aveți grijă să verificați acest lucru.

Acest mod are sens pentru lucrurile care vor fi pornite și oprite în mod explicit pentru a funcționa pentru perioade de timp arbitrare, cum ar fi un serviciu care efectuează redarea muzicii în fundal.

Valoare constantă: 1 (0x00000001)

Comentarii

  • Mulțumesc @nPn, dar nu cred că aceasta este problema, pentru că pot vedea câteva Toasts creare de către serviciu. –  > Por Walid Ammar.
  • Se pare că majoritatea toasturilor dvs. ar fi timpurii și apoi ați continua să primiți actualizări periodice ale locației, deci spuneți că primiți toasturi în curs de la serviciu, dar nu vedeți că este apelat onLocationChanged? –  > Por nPn.
  • De fapt, pot vedea Location is null, Location Services Connected, Location Update Requested … dar nu și Location has been changed așa că sunt sigur că serviciul meu funcționează, dar onLocationChanged nu este apelat. –  > Por Walid Ammar.
rupesh jain

Hărțile Google trebuie să funcționeze deoarece trebuie să stocheze în memoria cache informațiile de locație anterioare sau să utilizeze direct api de locație – nu api de locație a serviciilor Google Play…