1

давно я запрограммировал приложение для Android, которое должно знать местоположение устройства. В целях абстрагирования доступа к местоположению устройства я запрограммировал класс, который управляет всеми вещами, связанными с местоположением, сохраняет местоположение текущего устройства и вызывает основное действие при изменении статуса GPS или Интернета, чтобы уведомить пользователя.GPS работает на всех устройствах, за исключением Lollipop

Это приложение продолжает работать на всех устройствах, пока я не купил Samsung Galaxy A5 2016, который поставляется с Lollipop. Он работает на всех устройствах Jellybean, которые я тестировал на старых версиях Android, но на A5 2016 пользователь получает уведомление о изменениях статуса GPS, но метод onLocationChanged() не должен работать, поскольку местоположение, хранящееся в этом классе, всегда равно нулю , Почему этот класс для получения местоположения работает на всех устройствах, а теперь на Lollipop он перестает работать? Действительно расстраивает. Предполагается, что Android-приложения должны быть совместимы с первыми. Вот код класса, управляющего местоположением, и который перестает работать на Lollipop. До Lollipop местоположение, которое хранилось в атрибутах экземпляра класса, которые имеют тип Location, но начиная с Lollipop, местоположение больше не сохраняется.

package bembibre.personlocator.logic.locationservices; 

import android.content.Context; 
import android.location.GpsStatus; 
import android.location.Location; 
import android.location.LocationListener; 
import android.location.LocationManager; 
import android.location.LocationProvider; 
import android.os.Bundle; 
import android.os.SystemClock; 
import bembibre.personlocator.activities.MainActivity; 
import bembibre.personlocator.logic.Action; 
import bembibre.personlocator.logic.internet.InternetManager; 

/** 
* Clase singleton que permite averiguar la localización del usuario en cada 
* momento. 
* 
* @author misines 
* 
*/ 
public class MyLocationManager { 

    /** 
    * Única instancia que puede existir de este objeto (es un singleton). 
    */ 
    private static MyLocationManager instance = new MyLocationManager(); 

    private static int GPS_INTERVAL = 3000; 

    /** 
    * Actividad que llama a este objeto y que necesita conocer la localización 
    * del usuario. 
    */ 
    private MainActivity activity; 

    /** 
    * Objeto de Android que permite acceder a la localización del usuario. 
    */ 
    private LocationManager locationManager; 

    /** 
    * Objeto que se encarga de escuchar cambios de localización basada en red. 
    */ 
    private LocationListener networkLocationListener; 

    /** 
    * Objeto que se encarga de escuchar cambios de localización basada en GPS. 
    */ 
    private LocationListener gpsLocationListener; 

    private int networkStatus = LocationProvider.OUT_OF_SERVICE; 

    private int gpsStatus = LocationProvider.OUT_OF_SERVICE; 

    private EnumGpsStatuses status = EnumGpsStatuses.BAD; 

    /** 
    * Este atributo contiene la última localización del usuario determinada 
    * por red (no muy exacta) o <code>null</code> si no se ha podido 
    * determinar (por ejemplo porque no hay Internet). 
    */ 
    private Location networkLocation; 

    /** 
    * Este atributo contiene la última localización del usuario determinada 
    * por GPS (es más exacta que por red) o <code>null</code> si no se ha 
    * podido determinar (por ejemplo porque no está activado el GPS). 
    */ 
    private Location gpsLocation; 
    private Long gpsLastLocationMillis; 

    private boolean networkProviderEnabled = false; 



    public static MyLocationManager getInstance() { 
     return MyLocationManager.instance; 
    } 

    private void setNetworkLocation(Location location) { 
     this.networkLocation = location; 
    } 

    private void setGpsLocation(Location location) { 
     this.gpsLocation = location; 
    } 

    /** 
    * Método que es llamado cuando el estado de alguno de los proveedores de 
    * localización de los que depende esta clase ha cambiado de estado. 
    */ 
    private void onStatusChanged() { 
     switch(this.gpsStatus) { 
     case LocationProvider.AVAILABLE: 
      this.status = EnumGpsStatuses.GOOD; 
      break; 
     case LocationProvider.OUT_OF_SERVICE: 
     case LocationProvider.TEMPORARILY_UNAVAILABLE: 
     default: 
      switch(this.networkStatus) { 
      case LocationProvider.AVAILABLE: 
       this.status = EnumGpsStatuses.SO_SO; 
       break; 
      case LocationProvider.OUT_OF_SERVICE: 
      case LocationProvider.TEMPORARILY_UNAVAILABLE: 
      default: 
       this.status = EnumGpsStatuses.BAD; 
      } 
     } 

     if (this.activity != null) { 
      this.activity.onGpsStatusChanged(this.status); 
     } 
    } 

    private void setNetworkStatus(int status) { 
     this.networkStatus = status; 
     this.onStatusChanged(); 
    } 

    private void setGpsStatus(int status) { 
     this.gpsStatus = status; 
     this.onStatusChanged(); 
    } 

    private class MyGPSListener implements GpsStatus.Listener { 
     public void onGpsStatusChanged(int event) { 
      boolean isGPSFix; 
      switch (event) { 
       case GpsStatus.GPS_EVENT_SATELLITE_STATUS: 
        if (MyLocationManager.this.gpsLastLocationMillis != null) { 
         isGPSFix = (SystemClock.elapsedRealtime() - MyLocationManager.this.gpsLastLocationMillis) < 3 * MyLocationManager.GPS_INTERVAL; 
        } else { 
         isGPSFix = false; 
        } 

        if (isGPSFix) { // A fix has been acquired. 
         MyLocationManager.this.setGpsStatus(LocationProvider.AVAILABLE); 
        } else { // The fix has been lost. 
         MyLocationManager.this.setGpsStatus(LocationProvider.OUT_OF_SERVICE); 
        } 

        break; 
       case GpsStatus.GPS_EVENT_FIRST_FIX: 
        // Do something. 
        MyLocationManager.this.setGpsStatus(LocationProvider.AVAILABLE); 

        break; 
      } 
     } 
    } 

    /** 
    * Inicializa este objeto para que empiece a funcionar y trate de 
    * determinar la localización del dispositivo. Además inicializa al 
    * <code>InternetManager</code>, de modo que una vez que se haya llamado a 
    * este método, InternetManager estará disponible en todo momento para ver 
    * si la conexión a Internet funciona o hacer pruebas a dicha conexión. 
    * 
    * @param activity 
    */ 
    public void start(final MainActivity activity) { 
     this.activity = activity; 

     // Acquire a reference to the system Location Manager 
     this.locationManager = (LocationManager) this.activity.getSystemService(Context.LOCATION_SERVICE); 

     // Define a listener that responds to location updates 
     this.networkLocationListener = new LocationListener() { 
      public void onLocationChanged(Location location) { 
       // Called when a new location is found by the network location 
       // provider. 
       MyLocationManager.this.setNetworkLocation(location); 
       MyLocationManager.this.networkProviderEnabled = true; 
       InternetManager.getInstance().makeInternetTest(activity); 
      } 

      public void onStatusChanged(String provider, int status, Bundle extras) { 
       MyLocationManager.this.setNetworkStatus(status); 
      } 

      public void onProviderEnabled(String provider) { 
       MyLocationManager.this.networkProviderEnabled = true; 
       InternetManager.getInstance().makeInternetTest(activity); 
      } 

      public void onProviderDisabled(String provider) { 
       MyLocationManager.this.networkProviderEnabled = false; 
       MyLocationManager.this.setNetworkStatus(LocationProvider.OUT_OF_SERVICE); 
      } 
     }; 

     this.gpsLocationListener = new LocationListener() { 
      public void onLocationChanged(Location location) { 
       // Called when a new location is found by the network location 
       // provider. 
       MyLocationManager.this.setGpsLocation(location); 
       MyLocationManager.this.gpsLastLocationMillis = SystemClock.elapsedRealtime(); 
       //MyLocationManager.this.setGpsStatus(LocationProvider.AVAILABLE); 
      } 

      public void onStatusChanged(String provider, int status, Bundle extras) { 
       MyLocationManager.this.setGpsStatus(status); 
      } 

      public void onProviderEnabled(String provider) { 

      } 

      public void onProviderDisabled(String provider) { 
       MyLocationManager.this.setGpsStatus(LocationProvider.OUT_OF_SERVICE); 
      } 
     }; 

     // Register the listener with the Location Manager to receive location 
     // updates 
     try { 
      this.locationManager.requestLocationUpdates(
       LocationManager.NETWORK_PROVIDER, 3000, 0, this.networkLocationListener 
      ); 

      this.locationManager.requestLocationUpdates(
       LocationManager.GPS_PROVIDER, GPS_INTERVAL, 0, this.gpsLocationListener 
      ); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     this.locationManager.addGpsStatusListener(new MyGPSListener()); 



     /* 
     * Hay que inicializar al InternetManager y decirle que avise a este 
     * objeto cada vez que el Internet vuelva o se pierda. Para ello usamos 
     * dos objetos Action. 
     */ 
     Action action1 = new Action() { 

      @Override 
      public void execute(String string) { 
       MyLocationManager.getInstance().internetHasBeenRecovered(); 
      } 

     }; 
     Action action2 = new Action() { 

      @Override 
      public void execute(String string) { 
       MyLocationManager.getInstance().internetHasBeenLost(); 
      } 

     }; 
     InternetManager.getInstance().initialize(activity, action1, action2); 
    } 

    public void stop() { 
     if (this.locationManager != null) { 
      if (this.networkLocationListener != null) { 
       this.locationManager.removeUpdates(this.networkLocationListener); 
      } 
      if (this.gpsLocationListener != null) { 
       this.locationManager.removeUpdates(this.gpsLocationListener); 
      } 
     } 

     this.activity = null; 
    } 

    /** 
    * Devuelve la última localización conocida basada en red o 
    * <code>null</code> si no hay. 
    * 
    * @return la última localización conocida basada en red o 
    * <code>null</code> si no hay. 
    */ 
    public Location getNetworkLocation() { 
     Location result; 

     if (this.networkLocation == null) { 
      result = this.gpsLocation; 
     } else { 
      result = this.networkLocation; 
     } 

     return result; 
    } 

    /** 
    * Si el gps está disponible y tenemos una posición guaradada basada en GPS 
    * entonces la devuelve. En caso contrario intenta devolver la última 
    * localización basada en red, y si tampoco está disponible, devuelve 
    * <code>null</code>. 
    * 
    * @return la localización más precisa que esté disponible en este momento. 
    */ 
    public Location getFinestLocationAvailable() { 
     Location result; 

     switch(this.gpsStatus) { 
     case LocationProvider.AVAILABLE: 
      if (this.gpsLocation == null) { 
       result = this.networkLocation; 
      } else { 
       result = this.gpsLocation; 
      } 
     case LocationProvider.TEMPORARILY_UNAVAILABLE: 
     case LocationProvider.OUT_OF_SERVICE: 
     default: 
      result = this.networkLocation; 
     } 

     return result; 
    } 

    /** 
    * Devuelve el estado actual del GPS. 
    * 
    * @return el estado actual del GPS. 
    */ 
    public EnumGpsStatuses getStatus() { 
     return this.status; 
    } 

    /** 
    * Método al que tenemos que llamar siempre que nos enteremos de que 
    * tenemos Internet, para que se sepa que la localización por red funciona. 
    */ 
    public void internetHasBeenRecovered() { 
     if (this.networkProviderEnabled) { 
      this.setNetworkStatus(LocationProvider.AVAILABLE); 
     } else { 
      this.setNetworkStatus(LocationProvider.OUT_OF_SERVICE); 
     } 
     this.onStatusChanged(); 
    } 

    /** 
    * Método al que tenemos que llamar siempre que nos enteremos de que la 
    * conexión a Internet se ha perdido, para que este objeto se dé cuenta de 
    * que el servicio de localización por red ya no funciona. 
    */ 
    public void internetHasBeenLost() { 
     this.setNetworkStatus(LocationProvider.OUT_OF_SERVICE); 
     this.onStatusChanged(); 
    } 
} 

ответ

0

Try с помощью следующего кода:

public class MyLocationService extends Service{ 

    Location mLastLocation; 
    android.location.LocationListener mLocationListener; 
    LocationManager locationManager; 

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

     // Acquire a reference to the system Location Manager 
     locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); 

     // Define a listener that responds to location updates 
     mLocationListener = new android.location.LocationListener() { 
      @Override 
      public void onLocationChanged(Location location) { 
       // Called when a new location is found by the gps location provider. 

       mLastLocation = location; // this is the object you need to play with Location changes 

       // do stuffs here you want after location changes 
      } 

      @Override 
      public void onStatusChanged(String provider, int status, Bundle extras) {} 

      @Override 
      public void onProviderEnabled(String provider) {} 

      @Override 
      public void onProviderDisabled(String provider) {} 
     }; 

     String locationProvider = LocationManager.GPS_PROVIDER; // here, you can also set Network provider as per your need 

     // Register the listener with the Location Manager to receive location updates 
     try{ 
      locationManager.requestLocationUpdates(locationProvider, 5000, 0, mLocationListener); 
      mLastLocation = locationManager.getLastKnownLocation(locationProvider); 
     } catch (SecurityException e){ 
      e.printStackTrace(); 
     } 

     return START_STICKY; 
    } 
} 

И манифеста требуется разрешение, вы уже знаете:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 

И, наконец, ваша служба должна получить объявленный в манифесте следующим образом:

<service android:name=".service.MyLocationService" /> 

Вам необходимо запустить службу из класса Activity или Application.

+0

Спасибо, но мне нужно знать, в чем разница между вашим кодом и шахтой и почему шахта работает не только на Lollipop? Я думаю, что мой код похож на ваш, и мой метод onLocationChanged() не вызван. Мне не нужно создавать службу, потому что пользователь только хочет знать местоположение, когда он использует приложение. – user3289695

+0

Я думаю, что вы одновременно используете провайдера gps и сетевого провайдера, используйте поставщика gps только тогда, когда на вашем мобильном телефоне есть подключение к Интернету, иначе используйте поставщика услуг сети. Попробуйте использовать 'if() {} else {}' в вашем блоке 'try {}'. Счастливое кодирование :) – zeeali

+0

Мое приложение снова начало работать, и я не изменил код. Я не знаю, что происходит. Возможно, мое устройство было неправильно сконфигурировано? Я ввел настройки местоположения и там было несколько вариантов: используйте GPS и сети, используйте только GPS, используйте только сети ... Это очень странно. Если я изменю выбранную опцию, отключите ее, а затем включите, мой выбор не запомнится устройством и вернется к опции, которая была выбрана ранее. – user3289695