android - ubicacion - El proveedor de la ubicación fusionada no parece usar el receptor GPS
seleccionar aplicacion de ubicacion de prueba no aparece (12)
¿Qué hay en el método OnConnected
?
En este método, debe crear el objeto LocationRequest
con prioridad PRIORITY_HIGH_ACCURACY
.
@Override
public void onConnected(Bundle dataBundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationClient.requestLocationUpdates(mLocationRequest, fusedListener);
}
Android 4.3 en Moto G, Android 4.4.2 en Nexus 7 2012, Android 4.4.2 en Nexus 5. Android Studio 0.4.
No quiero recibir actualizaciones de ubicación regulares, solo quiero una ubicación precisa cuando el usuario presiona un botón.
He seguido este ejemplo: https://developer.android.com/training/location/retrieve-current.html
En archivo manifiesto:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Compruebo que los Servicios de reproducción están disponibles usando GooglePlayServicesUtil.isGooglePlayServicesDisponible
En la actividad principal:
//in activity onCreate method
mLocationClient = new LocationClient(this, this, this);
@Override
protected void onStart() {
mLocationClient.connect();
super.onStart();
}
@Override
protected void onStop() {
mLocationClient.disconnect();
super.onStop();
}
//in button onclick method
mCurrentLocation = mLocationClient.getLastLocation();
No tengo tarjeta SIM. Si habilito Wifi, a veces obtengo una ubicación precisa. Otras veces mCurrentLocation es nulo.
Si deshabilito Wifi, mCurrentLocation siempre es nulo.
Estoy probando afuera en varios lugares siempre con una vista clara del cielo. Esperé tres minutos en cada ubicación.
Nunca veo el icono de GPS aparecer en la barra de notificaciones de Android en la parte superior de la pantalla.
Tengo estas configuraciones de ubicación:
Una aplicación de prueba de GPS logra usar el GPS con éxito en el interior en el mismo dispositivo con Wi-Fi desactivado para que el GPS funcione:
El registro de actualizaciones de ubicación, como en https://developer.android.com/training/location/receive-location-updates.html , tampoco funciona. Método registrado nunca llamado.
¿Qué estoy haciendo mal?
Como nadie ha compartido este enlace, he encontrado que esta es la documentación más útil http://developer.android.com/guide/topics/location/strategies.html
"Es probable que la corrección de ubicación más reciente sea la más precisa. Sin embargo, como la precisión de una corrección de ubicación varía, la corrección más reciente no siempre es la mejor. Debe incluir la lógica para elegir arreglos de ubicación según varios criterios. los criterios también varían según los casos de uso de la aplicación y las pruebas de campo ".
Su mejor opción es mantener activo un oyente de localización siempre que la actividad esté en primer plano y seleccionar la ubicación en caché más precisa cuando se presione el botón. Es posible que deba mostrar una ruleta o algo así y desactivar el botón mientras espera que aparezca una medición precisa.
Creo que estás probando tu aplicación en Indoor, no funciona ...
y flujo de código ...
public void setUpLocationClientIfNeeded() {
createLocReq();
if (mLocationClient == null) {
mLocationClient = new LocationClient(getApplicationContext(), this, // ConnectionCallbacks
this); // OnConnectionFailedListener
}
}
private void createLocReq() {
if (mLocationRequest == null) {
// Create a new global location parameters object
mLocationRequest = LocationRequest.create();
// Set the update interval
mLocationRequest.setInterval(LocationServices.UPDATE_INTERVAL_IN_MILLISECONDS);
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the interval ceiling to one minute
mLocationRequest.setFastestInterval(LocationServices.FAST_INTERVAL_CEILING_IN_MILLISECONDS);
mLocationClient = new LocationClient(getApplicationContext(), this, this);
mLocationClient.connect();
}
}
public void updateLocation(Location location) {
if (lastTrackedLat == 0.0) {
lastTrackedLat = location.getLatitude();
}
if (lastTrackedLng == 0.0) {
lastTrackedLng = location.getLongitude();
}
currentLat = location.getLatitude();
currentLng = location.getLongitude();
}
@Override
public void onLocationChanged(Location location) {
if (location != null) {
this.location = location;
updateLocation(location);
}
}
public Location getLocation() {
// mLocationClient.getLastLocation();
return location;
}
De acuerdo con los documentos
Este método proporciona una forma simplificada de obtener ubicación. Es especialmente adecuado para aplicaciones que no requieren una ubicación precisa y que no desean mantener una lógica adicional para las actualizaciones de ubicación.
por lo que puede devolver o no una ubicación muy precisa.
El GPS puede tardar un tiempo en getLastLocation
por lo que llamar a getLastLocation
puede devolver una ubicación o no.
es mejor que solicite actualizaciones de ubicación y, luego de obtener una ubicación, simplemente deje de solicitar actualizaciones de ubicación.
También mirando el código que proporcionó, ¿está esperando que LocationClient
conecte antes de intentar obtener una ubicación? Eso sin duda le dará una ubicación nula ya que no está conectado para obtener la ubicación todavía.
lo que deberías hacer es onConnected
tu onConnected
la última ubicación allí, por ejemplo
public void onConnected(Bundle connectionHint) {
Location location = mLocationClient.getLastLocation();
}
como se dice en ese ejemplo onConnected es 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
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
El problema es con getLastLocation()
porque usa una ubicación en caché. Tuve el mismo problema ya que también intenté usar este enfoque simple. Desde entonces, he cambiado a escuchar actualizaciones (y parar después de la primera actualización exitosa de forma automática).
Este es mi código que funciona.
En primer lugar, el control de disponibilidad en la aplicación (no esencial, puede estar en la actividad y sin guardar el resultado):
public class MainApp extends Application {
public static enum PlayServices {
NOT_CHECKED, AVAILABLE, UNAVAILABLE
};
public static PlayServices mPlayServices = PlayServices.NOT_CHECKED;
@Override
public void onCreate() {
super.onCreate();
if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) {
MainApp.mPlayServices = MainApp.PlayServices.AVAILABLE;
}
}
}
Luego, a la Actividad:
public class MainActivity extends SherlockFragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {
En su onCreate()
:
if (MainApp.mPlayServices != MainApp.PlayServices.UNAVAILABLE) {
mLocationClient = new LocationClient(this, this, this);
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(5000);
mLocationRequest.setNumUpdates(1);
mLocationRequest.setFastestInterval(1000);
mUpdatesRequested = false;
MainApp.prefs.edit().putBoolean(MainApp.KEY_LOCATION_UPDATES_REQUESTED, mUpdatesRequested)
.commit();
}
El resto de la clase MainActivity
:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(this.getClass().getSimpleName(), "onActivityResult(" + requestCode + ", " + resultCode
+ ")");
// Decide what to do based on the original request code
switch (requestCode) {
case MainApp.PLAY_CONNECTION_FAILURE_RESOLUTION_REQUEST:
/*
* If the result code is Activity.RESULT_OK, try
* to connect again
*/
switch (resultCode) {
case Activity.RESULT_OK:
// here we want to initiate location requests!
mLocationClient = new LocationClient(this, this, this);
break;
}
break;
}
}
@Override
public void onConnected(Bundle dataBundle) {
Log.d(this.getClass().getSimpleName(), "onConnected()");
Log.d(this.getClass().getSimpleName(), "Google Play Services are available.");
MainApp.mPlayServices = MainApp.PlayServices.AVAILABLE;
if (!mUpdatesRequested) {
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
boolean gps_enabled = false;
try {
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
}
boolean network_enabled = false;
try {
network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception ex) {
}
// don''t start listeners if no provider is enabled
MainApp.locEnabled = gps_enabled || network_enabled;
if (!MainApp.locEnabled) {
// we have access to PlayServices, but user has disabled location visibility --> alert him
alertLocationOff();
} else {
mLocationClient.requestLocationUpdates(mLocationRequest, this);
mUpdatesRequested = true;
}
}
}
@Override
public void onDisconnected() {
Log.d(this.getClass().getSimpleName(), "onDisconnected()");
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(this.getClass().getSimpleName(), "onConnectionFailed()");
Log.d(this.getClass().getSimpleName(), "Google Play Services not available.");
MainApp.mPlayServices = MainApp.PlayServices.UNAVAILABLE;
/*
* 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()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(this,
MainApp.PLAY_CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
/*
* If no resolution is available, display a dialog to the
* user with the error.
*/
GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();
}
}
@SuppressLint("NewApi")
@Override
public void onLocationChanged(Location location) {
Log.d(this.getClass().getSimpleName(), "onLocationChanged(), location=" + location);
if (location != null) {
boolean present = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
present = Geocoder.isPresent();
}
if (present) {
(new ExtractLocationTask(this)).execute(location);
} else {
Log.e(this.getClass().getSimpleName(), "Geocoder not present");
MainApp.mPlayServices = MainApp.PlayServices.UNAVAILABLE;
}
}
}
private class ExtractLocationTask extends AsyncTask<Location, Void, Boolean> {
Context mContext;
public ExtractLocationTask(Context context) {
super();
mContext = context;
}
@Override
protected Boolean doInBackground(Location... params) {
Log.d(getClass().getSimpleName(), "ExtractLocationTask.onPreExecute()");
boolean found = false;
try {
Geocoder geoCoder_local = new Geocoder(mContext, Locale.getDefault());
Geocoder geoCoder_en = new Geocoder(mContext, Locale.ENGLISH);
List<Address> addresses_local = geoCoder_local.getFromLocation(params[0].getLatitude(),
params[0].getLongitude(), 10);
List<Address> addresses_en = geoCoder_en.getFromLocation(params[0].getLatitude(),
params[0].getLongitude(), 10);
if (addresses_local != null && addresses_local.size() > 0) {
// do what you want with location info here
// based on mLocationRequest.setNumUpdates(1), no need to call
// removeLocationUpdates()
MainApp.locEnabled = true;
mUpdatesRequested = false;
MainApp.prefs.edit()
.putBoolean(MainApp.KEY_LOCATION_UPDATES_REQUESTED, mUpdatesRequested).commit();
found = true;
}
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "Exception: ", e);
}
return found;
}
@Override
protected void onPostExecute(Boolean found) {
Log.d(getClass().getSimpleName(), "ExtractLocationTask.onPostExecute()");
if (found) {
// update UI etc.
} else if (!mUpdatesReRequested) {
mLocationClient.requestLocationUpdates(mLocationRequest, (LocationListener) mContext);
mUpdatesRequested = true;
mUpdatesReRequested = true;
}
}
}
¡Espero que esto te ayude a hacer que funcione!
El proveedor de ubicación no activará el GPS hasta que algunos clientes soliciten (suscriban) una ubicación de alta precisión (como se describe en ejemplos proporcionados por otros usuarios). La aplicación de prueba GPS no usa el proveedor de ubicación, pero usa una forma antigua y "directa" de obtener la ubicación.
También hay un mecanismo de expiración, que elimina información sobre la última ubicación después de algún tiempo si se cree que está obsoleta.
Resumiendo todo lo anterior, es muy posible que LP (Proveedor de ubicación) no tenga nada que ofrecerle.
Hay dos cosas que suceden aquí que ocasionan que a veces obtengas un null
, y algunas veces obtienes una ubicación.
Primero, está creando una nueva instancia de LocationClient
en el método onClick
, que no es la misma instancia a la que llama connect()
en onStart()
. Esto creará un comportamiento impredecible en el que a veces el cliente tendrá tiempo suficiente para conectarse antes de devolver un resultado de LocationClient.getLastLocation()
, y en ocasiones no lo hará.
En segundo lugar, debe proteger la llamada a LocationClient.getLastLocation()
con una llamada a LocationClient.isConnected()
. Dice lo siguiente en los documentos de LocationClient.isConnected()
: https://developer.android.com/reference/com/google/android/gms/location/LocationClient.html#isConnected()
Comprueba si el cliente está actualmente conectado al servicio, para que las solicitudes a otros métodos tengan éxito. Las aplicaciones deben proteger las acciones del cliente causadas por el usuario con una llamada a este método.
Como el usuario activa el código onClick()
tocando el botón, debe llamar a este método antes de tratar de obtener la última ubicación.
Entonces, tu código debería verse así:
LocationClient mLocationClient;
Location mCurrentLocation;
@Override
protected void onCreate() {
...
mLocationClient = new LocationClient(this, this, this);
...
}
@Override
protected void onStart() {
mLocationClient.connect();
super.onStart();
}
@Override
protected void onStop() {
mLocationClient.disconnect();
super.onStop();
}
public void onClick(View v) {
...
if (mLocationClient.isConnected()) {
// You should get a valid location here
mCurrentLocation = mLocationClient.getLastLocation();
}
}
Esto debería darle al LocationClient
tiempo suficiente para conectarse y darle una ubicación válida, que con suerte debería incluir datos de GPS.
Lo solucioné El problema era que "Deje que las aplicaciones de Google accedan a su ubicación" estaba desactivado:
Cuando lo enciendo obtengo lecturas de GPS y cuando está apagado no lo hago.
Lo había dejado por dos razones:
Estoy desarrollando una aplicación para utilizar en muchos dispositivos de una empresa y quiero que sea necesaria una configuración manual mínima
La pantalla dice claramente "Esta configuración afecta únicamente a las aplicaciones de Google". Sé que Play Services es el software de Google, pero no creo que Google espere que un usuario final lo entienda.
Luego recibí la actualización de Android 4.4.2 y la página de configuración de ubicación ha cambiado. Parece que puedo desactivar los Informes de Ubicación de Google y aún obtener las lecturas de GPS del proveedor de ubicación fusionada:
Entonces, tal vez Google se dio cuenta de que la configuración era confusa y la mejoró. De cualquier manera, habría ahorrado mucho tiempo si hubiera obtenido 4.4.2 hace unos días.
Prefiero usar el Servicio que implementa LocationListener
para obtener una ubicación actual casi exacta. Normalmente tomo los siguientes pasos:
- Inicialice
LocationManager
y llame arequestLocationUpdates
paraGPS_PROVIDER
yNETWORK_PROVIDER
enonCreate()
- Llame a
getLastKnownLocation
paraGPS_PROVIDER
yNETWORK_PROVIDER
y usegetTime()
para conocer la última ubicación más reciente. - Transmitir la última ubicación (si es <30 segundos de antigüedad) (opcional)
- Mientras tanto, cuando se llama a
onLocationChanged
, comparo la ubicación recientemente actualizada con la última mejor ubicación (si existe) y verifico el nivel de precisión. - Si es preciso delta <MIN_ACCURACY (variable de usuario), úselo como la mejor ubicación
- Transmitir la mejor ubicación actual
- Debe eliminar LocationManager
locationManager.removeUpdates(this);
- Llamar a StopSelf (); (También puede detener el servicio de la actividad en
onDestroy
)
EDITAR:
OK ... El método anterior estaba usando la API de ubicación, a continuación he codificado usando Fused Provider (GooglePlayServices). He probado en mi Nexus 5 (Android 4.4.2), SIN SIM, SIN WIFI ... y obtengo resultados.
Edición 2: también he probado en Android 2.3.3 LG Optimus (Sin SIM y Wifi) y me tomó alrededor de 5 minutos obtener el bloqueo (usando Fused Provided), pero obtuve el ícono de ubicación al instante.
public class HomeActivity extends FragmentActivity implements
ActionBar.OnNavigationListener,
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {
private LocationClient locationClient;
private LocationRequest locationRequest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// ...
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resp == ConnectionResult.SUCCESS) {
locationClient = new LocationClient(this, this, this);
locationClient.connect();
} else {
Toast.makeText(this, "Google Play Service Error " + resp,
Toast.LENGTH_LONG).show();
}
}
@Override
public void onConnected(Bundle connectionHint) {
if (locationClient != null && locationClient.isConnected()) {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(100);
locationClient.requestLocationUpdates(locationRequest, this);
}
}
@Override
public void onLocationChanged(Location location) {
try {
if (location != null) {
LatLng gps = new LatLng(location.getLatitude(), location.getLongitude());
Log.v("JD", "My Location: " + gps.toString());
}
} catch (Exception e) {
Log.d("JD",
"onLocationChanged", e);
}
}
}
Prueba esto:
public final static int MINACCURACY = 50;
private LocationManager lm;
private LocationListener listener;
private void foundLoc(double x, double y) {
// do something with x,y
Log.v("DEBUG", x + "," + y);
}
public void findMe(View v) {
lm = (LocationManager) getSystemService(LOCATION_SERVICE);
listener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if(location.getAccuracy() < MINACCURACY) {
foundLoc(location.getLatitude(), location.getLongitude());
lm.removeUpdates(listener);
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 100, 0, listener);
}
MINACCURACY
está en metros. De esta manera, al presionar un botón (que llama al método findMe), el GPS está habilitado, su ubicación se encuentra con una precisión mínima, el método foundLoc obtiene los datos y el oyente finaliza (lo cual, a su vez, desactiva el GPS).
Sin una tarjeta SIM, el coarse location provider
no tiene forma de conocer la posición aproximada, a menos que pueda encontrar una red WiFi que haya sido asignada por Google.
Solicitar la última ubicación conocida puede dar como resultado una ubicación obsoleta, y como tal es bastante inútil. Supongo que esta es la posición que se grabó la última vez que alguna aplicación solicitó una actualización de ubicación.
Uso el siguiente código para obtener una ubicación reciente:
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
criteria.setAltitudeRequired(false);
criteria.setSpeedRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(false);
final LocationManager manager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
....
LocationListener listener = new LocationListener() {
@Override
public void onLocationChanged(Location lastKnownLocation) {
....
}
// rest of interface
}
manager.requestSingleUpdate(criteria, listener, null);
La última llamada garantiza que solicitemos la ubicación actual , no la ubicación en la que pudo encontrar una cantidad de tiempo desconocida anteriormente.
Puede intentar cambiarlo a Criteria.ACCURACY_FINE
para encender el GPS. Tenga en cuenta que si el GPS no tiene una solución durante bastante tiempo, puede tomar más de varios minutos antes de que sea realmente capaz de obtener una solución. Mientras tanto, esperaría que veas el icono del GPS que indica que está esperando una solución.
Todo lo que necesita hacer es agregar una propiedad de prioridad al objeto de solicitud como este.
public void onConnected(Bundle arg0)
{
locationrequest = LocationRequest.create();
locationrequest.setInterval(1000);
locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationclient.requestLocationUpdates(locationrequest, this);
}
Tal vez use una variable booleana para permitirle al usuario tener opciones para forzar el GPS como
boolean forceGPS=false;
.
.
.//make the user choose to change the value of the boolean
.
.
public void onConnected(Bundle arg0)
{
locationrequest = LocationRequest.create();
locationrequest.setInterval(1000);
if(forceGPS==true)
{
locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
locationclient.requestLocationUpdates(locationrequest, this);
}