Android 4.3 sur Moto G, Android 4.4.2 sur Nexus 7 2012, Android 4.4.2 sur Nexus 5. Android = Studio 0.4.
Je ne souhaite pas recevoir de mises à jour régulières sur l'emplacement, mais simplement un emplacement précis lorsque l'utilisateur appuie sur un bouton.
J'ai suivi cet exemple: https://developer.Android.com/training/location/retrieve-current.html
Dans le fichier manifeste:
<uses-permission Android:name="Android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission Android:name="Android.permission.ACCESS_FINE_LOCATION" />
Je vérifie que les services de lecture sont disponibles à l'aide de GooglePlayServicesUtil.isGooglePlayServicesAvailable
En activité principale:
//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();
Je n'ai pas de carte SIM. Si j'active le Wifi, je reçois parfois un emplacement précis. D'autres fois, mCurrentLocation est null.
Si je désactive le Wifi, alors mCurrentLocation est toujours nul.
Je teste dehors à plusieurs endroits avec toujours une vue dégagée du ciel. J'ai attendu trois minutes à chaque endroit.
Je ne vois jamais l'icône GPS apparaître sur la barre de notification Android) en haut de l'écran.
J'ai ces paramètres de localisation:
Une application de test GPS parvient à utiliser le GPS à l'intérieur avec succès sur le même appareil avec le Wi-Fi désactivé pour que le GPS fonctionne:
L'enregistrement pour les mises à jour d'emplacement, à la https://developer.Android.com/training/location/receive-location-updates.html , ne fonctionne pas non plus. Méthode enregistrée jamais appelée.
Qu'est-ce que je fais mal?
Je l'ai résolu. Le problème était que "Laisser les applications Google accéder à votre position" a été désactivé:
Lorsque je l'allume, je reçois des relevés GPS et lorsqu'il est éteint, je ne le fais pas.
Je l'avais laissé pour deux raisons:
Je développe une application pour pouvoir utiliser de nombreux appareils dans une entreprise et je souhaite une configuration manuelle minimale.
L'écran indique clairement "Ce paramètre concerne uniquement les applications Google." Je sais que Play Services est un logiciel de Google, mais je ne pensais pas que Google s’attendrait à ce que l’utilisateur final le comprenne.
Ensuite, j'ai reçu la mise à jour Android 4.4.2 et la page des paramètres de localisation a été modifiée. Il semble que Google Location Reporting puisse être désactivé et que les relevés GPS du fournisseur de localisation fusionné soient toujours disponibles:
Alors peut-être que Google s'est rendu compte que le réglage était confus et l'a amélioré. De toute façon, j'aurais gagné beaucoup de temps si j'avais eu 4.4.2 il y a quelques jours.
Le problème est avec getLastLocation()
parce qu'il utilise un emplacement mis en cache. J'ai eu le même problème car j'ai également essayé d'utiliser cette approche simple. Depuis, je suis passé à l'écoute des mises à jour (et à l'arrêt automatique après la première mise à jour).
Ceci est mon code qui fonctionne.
Tout d'abord, le contrôle de disponibilité dans l'application (non essentiel, peut être dans l'activité et sans conservation du résultat):
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;
}
}
}
Ensuite, pour l'activité:
public class MainActivity extends SherlockFragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {
Dans sa 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();
}
Le reste de la classe 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;
}
}
}
J'espère que cela vous aidera à le faire fonctionner!
Le fournisseur de localisation ne réveille le GPS que lorsque certains clients demandent (s'abonner) une localisation de haute précision (comme décrit dans les exemples donnés par d'autres utilisateurs). L'application de test GPS n'utilise pas le fournisseur de localisation mais utilise l'ancien moyen "direct" d'obtenir la localisation.
Il existe également un mécanisme d'expiration, qui supprime les informations sur le dernier emplacement après un certain temps s'il est considéré comme obsolète.
En résumé, il est fort possible que LP (Location Provider) n’ait rien à vous donner.
Je pense que vous testez votre application en intérieur, cela ne fonctionne pas ..
et le flux de code ..
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;
}
Selon les docs
Cette méthode offre un moyen simplifié d’obtenir un emplacement. Il est particulièrement bien adapté aux applications ne nécessitant pas d'emplacement précis et ne souhaitant pas conserver de logique supplémentaire pour les mises à jour d'emplacement.
de sorte qu'il peut ou non renvoyer un emplacement très précis.
Le GPS peut prendre un certain temps à se verrouiller, alors appeler getLastLocation
peut ou non renvoyer une position.
vous feriez mieux de demander des mises à jour d'emplacement, alors après avoir obtenu un emplacement, arrêtez simplement de demander des mises à jour d'emplacement.
En regardant également le code que vous avez fourni, attendez-vous que le LocationClient
se connecte avant d'essayer d'obtenir un emplacement? Cela vous donnera certainement un emplacement nul puisqu'il n'est pas encore connecté pour obtenir l'emplacement.
ce que vous devriez faire est dans votre onConnected
obtenir le dernier emplacement, exemple
public void onConnected(Bundle connectionHint) {
Location location = mLocationClient.getLastLocation();
}
comme il est dit dans cet exemple onConnected est 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
Tout ce que vous avez à faire est d’ajouter une propriété prioritaire à l’objet requête comme ceci.
public void onConnected(Bundle arg0)
{
locationrequest = LocationRequest.create();
locationrequest.setInterval(1000);
locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationclient.requestLocationUpdates(locationrequest, this);
}
Peut-être utiliser une variable booléenne pour permettre à l'utilisateur de choisir entre forcer le GPS
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);
}
Qu'y a-t-il dans la méthode OnConnected
?
Dans cette méthode, vous devriez créer l'objet LocationRequest
avec PRIORITY_HIGH_ACCURACY
priorité.
@Override
public void onConnected(Bundle dataBundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationClient.requestLocationUpdates(mLocationRequest, fusedListener);
}
Sans carte SIM, le coarse location provider
n'a aucun moyen de connaître la position grossière, à moins de pouvoir trouver un réseau WiFi mappé par Google.
Demander le dernier emplacement connu peut aboutir à un emplacement obsolète et, en tant que tel, est plutôt inutile. Je suppose que c'est la position qui a été enregistrée la dernière fois qu'une application a demandé une mise à jour d'emplacement.
J'utilise le code suivant pour obtenir un emplacement récent:
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);
Le dernier appel garantit que nous demandons l'emplacement actuel, pas l'emplacement où il a été trouvé auparavant.
Vous pouvez essayer de le changer en Criteria.ACCURACY_FINE
afin de mettre le GPS en marche. Sachez que si le GPS n’a pas eu de solution pendant un bon moment, il peut s'écouler plusieurs minutes avant qu’il soit réellement capable de l’obtenir. Je m'attendrais en même temps à voir l'icône du GPS indiquant qu'elle attend une solution.
Puisque personne n'a partagé ce lien, j'ai trouvé que c'était la documentation la plus utile http://developer.Android.com/guide/topics/location/strategies.html
"Vous pouvez vous attendre à ce que le correctif de localisation le plus récent soit le plus précis. Toutefois, comme l'exactitude varie, le correctif le plus récent n'est pas toujours le meilleur. Vous devez inclure une logique permettant de choisir des correctifs de localisation en fonction de plusieurs critères. Les critères varient également en fonction des cas d'utilisation de l'application et des tests sur le terrain. "
Votre meilleur choix est de garder un écouteur d'emplacement actif tant que l'activité est au premier plan et de sélectionner l'emplacement mis en cache le plus précis lorsque vous appuyez sur le bouton. Il se peut que vous deviez afficher une visionneuse ou quelque chose du même genre et désactiver le bouton pendant qu’il attend une mesure précise à afficher.
Essaye ça:
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 mètres. De cette façon, lorsque vous appuyez sur un bouton (qui appelle la méthode findMe), le GPS est activé, votre position est trouvée avec une précision minimale, la méthode foundLoc récupère les données et l'auditeur se termine (ce qui désactive le GPS).
Il y a deux choses qui se passent ici et qui vous font parfois obtenir null
et parfois un emplacement.
Tout d'abord, vous créez une nouvelle instance de LocationClient
dans la méthode onClick
, qui n'est pas la même instance que vous appelez connect()
dans onStart()
. Cela créera un comportement imprévisible où le client aura parfois suffisamment de temps pour se connecter avant de renvoyer un résultat de LocationClient.getLastLocation()
, et parfois non.
Deuxièmement, vous devriez garder l'appel à LocationClient.getLastLocation()
avec un appel à LocationClient.isConnected()
. Dans la documentation LocationClient.isConnected()
, il indique ce qui suit: https://developer.Android.com/reference/com/google/Android/gms/location/LocationClient.html#isConnected ()
Vérifie si le client est actuellement connecté au service afin que les requêtes adressées à d'autres méthodes aboutissent. Les applications doivent protéger les actions du client causées par l'utilisateur avec un appel à cette méthode.
Puisque l'utilisateur déclenche le code onClick()
en appuyant sur le bouton, vous devez appeler cette méthode avant d'essayer d'obtenir le dernier emplacement.
Donc, votre code devrait ressembler à ceci:
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();
}
}
Cela devrait donner au LocationClient
un temps suffisant pour se connecter et vous donner un emplacement valide, qui devrait inclure les données GPS.
Je préfère utiliser le service qui implémente LocationListener
pour obtenir un emplacement actuel presque précis. Je prends normalement les mesures suivantes:
LocationManager
et appeler requestLocationUpdates
pour GPS_PROVIDER
Et NETWORK_PROVIDER
Dans onCreate()
getLastKnownLocation
pour GPS_PROVIDER
Et NETWORK_PROVIDER
Et utilisez getTime()
pour connaître le dernier emplacement le plus récent.onLocationChanged
est appelé, je compare le dernier emplacement mis à jour au dernier meilleur emplacement (le cas échéant) et vérifie le niveau de précision.locationManager.removeUpdates(this);
onDestroy
)MODIFIER:
OK ... La méthode ci-dessus utilisait Location API, j'ai codé ci-dessous à l'aide de Fused Provider (GooglePlayServices). J'ai testé sur mon Nexus 5 (Android 4.4.2), NO SIM, NO WIFI ... et j'obtiens des résultats.
Edit 2: J'ai aussi testé sur Android 2.3.3 LG Optimus (Pas de SIM ni de Wifi)) et il a fallu environ 5 minutes pour obtenir le verrouillage (avec Fused Fourni), mais je devenais instantanément icône de localisation.
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);
}
}
}