Domanda

Sto cercando di ascoltare i cambiamenti di posizione, ma a volte onLocationChanged la richiamata non viene mai chiamata e getLastLocation ritorna null, mentre Google Maps funziona sempre perfettamente.

Nota

Se riavvio il dispositivo, il servizio di localizzazione funzionerà solo per circa 2 giorni;dopodiché né la mia app né l'esempio dell'SDK funzionerà mentre Google Maps continuerà a funzionare.

Questo è il mio codice di servizio:

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

Manifesto 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"/>
È stato utile?

Soluzione 3

Primo

È una specie di bug sui vecchi dispositivi Android.

Apparentemente le API di localizzazione hanno un comportamento diverso su diverse versioni di Android (LocationManager ha problemi su Android >= 4.1, mentre Play Services ha problemi su Android 2.3), vedere Qui.

Quindi quello che ho ottenuto è il seguente:

Tentativo di recuperare l'ultima posizione tramite Play Services, se fallisce prova con LocationManager.

Secondo

Vorrei ringraziare tutti voi per i vostri preziosi suggerimenti, ho ricevuto il suggerimento di utilizzare la soluzione alternativa precedente dal post di AndroidHacker, quindi penso che sia lui a meritare la taglia.

Altri suggerimenti

Innanzitutto è un bug.Ho anche affrontato questo problema.

Puoi provare una soluzione semplice.

Effettua una richiesta di aggiornamento della posizione prima di provare a ottenere l'ultima posizione, ad es.Richiedi aggiornamenti sulla posizione prima di provare a trovare l'ultima posizione utilizzando getLastLocation();

Proprio come:

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); 

Questa implementazione è basata su LocationManager.Si prega di optare per Location Client per lo stesso.

Spero che sia d'aiuto!

AGGIORNAMENTO

Qui puoi anche utilizzare il metodo onLocationChanged() poiché ti aggiornerà ogni volta che la posizione viene modificata mentre il metodo getLastLocation() restituisce la posizione migliore conosciuta che potrebbe essere NULL.

Come da documento, il metodo GetLastLocation() restituirà NULL in casi molto rari.Ma questo succede spesso anche a me.

ULTIMO AGGIORNAMENTO

Ragazzi, solo un aggiornamento riguardante il GPS:

Per prima cosa dobbiamo utilizzare GoogleApiClient Invece LocationClient classe.

In secondo luogo dobbiamo attuare GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener

Terzo, dobbiamo sovrascriverlo onConnected() , onConnectionSuspended() E onConnectionFailed() metodo.

Riposa, sei a posto.

SALUTI!

Ho sviluppato la mia applicazione in base al codice seguente che puoi utilizzare se lo desideri. Il nome della mia applicazione è "https://play.google.com/store/apps/details?id=com.deep.profilemaper" .

Ecco il mio codice: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 {

      }
    }

}

***Costanti.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();
    }
}

Questo codice contiene alcune variabili come la data e l'ora correnti e costanti come LOG_File che serve solo a scopo di registrazione.quindi, ho escluso quel codice.Questo codice è perfettamente testato e funzionante.Provaci.

Grazie.

Questo problema può verificarsi se Google Maps non ha ancora determinato la posizione del tuo dispositivo.Prova ad avviare Google Maps.Se non è possibile, risolvere il problema nelle impostazioni del dispositivo (ad esempio abilitando WLAN + GPS).

Tu stai usando getLastLocation() per uno scopo diverso da quello previsto. getLastLocation() viene utilizzato quando un'app di rilevamento della posizione viene avviata per la prima volta.Dopo due giorni dovresti avere una correzione molto accurata.Perché non hai seguito le guide alla progettazione con riconoscimento della posizione.

Direi che devi tornare START_STICKY.Ma hai codificato onStartCommand() E onBind().Dovresti anche avviare il servizio e quindi collegarlo ad esso per il file START_STICKY lavorare dopo che i clienti si sono sciolti.

Non controllare GooglePlay nel servizio.Il servizio deve essere leggero, controlla GooglePlay in un'attività prima di avviare il servizio.Più un servizio è leggero, maggiori sono le possibilità che rimanga nella memoria.Corri il Service in un processo separato.

Da il documento, è chiaro che a volte è possibile che il getLastLocation() restituisce null.Quindi, a seconda di cosa intendi con "Non riesco a ricevere richiamate onLocationChanged", potresti semplicemente ritardare il tuo startVisit chiamate importanti (il tuo toast viene visualizzato in questo momento) finché non hai ricevuto questa richiamata onLocationChanged.In questo modo, se hai accesso diretto all'ultima posizione conosciuta, questa sarà diretta, altrimenti aspetti i callback 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();
        }

    }
}

È possibile che il tuo servizio venga semplicemente ucciso, può succedere.Prova a cambiare il valore restituito da onStartCommand() in

return STICKY_START;

Ciò riavvierà il servizio non appena le risorse saranno disponibili.

Ecco la sezione pertinente in Riferimento API

pubblico statico finale int START_STICKY

Aggiunto nel livello API 5 costante per tornare da OnStartCommand (intento, int, int):se il processo di questo servizio viene interrotto mentre viene avviato (dopo essere tornato da onStartCommand(Intent, int, int)), lascialo nello stato avviato ma non conservare questo intento consegnato.Successivamente il sistema tenterà di ricreare il servizio.Poiché è nello stato avviato, garantirà di chiamare onStartCommand(Intent, int, int) dopo aver creato la nuova istanza del servizio;se non ci sono comandi di avvio in sospeso da consegnare al servizio, verrà chiamato con un oggetto con intento nullo, quindi è necessario fare attenzione a verificarlo.

Questa modalità ha senso per cose che verranno avviate e interrotte esplicitamente per periodi di tempo arbitrari, ad esempio un servizio che esegue la riproduzione di musica di sottofondo.

Valore costante:1 (0x00000001)

Google Maps deve funzionare perché deve memorizzare nella cache le informazioni sulla posizione precedente o utilizzare direttamente l'API di posizione, non l'API di posizione di Google Play Services.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top