GPS 可在除 Lollipop 之外的所有设备上运行

Posted

技术标签:

【中文标题】GPS 可在除 Lollipop 之外的所有设备上运行【英文标题】:GPS working on all devices except for Lollipop 【发布时间】:2016-08-12 16:29:35 【问题描述】:

很久以前,我编写了一个需要知道设备位置的 android 应用程序。为了抽象对设备位置的访问,我编写了一个类,它管理所有与位置相关的东西,存储当前设备的位置,并在 GPS 或 Internet 状态发生变化时调用主要活动以通知用户。

在我购买了 Lollipop 随附的 Samsung Galaxy A5 2016 之前,此应用程序一直可以在所有设备上运行。它适用于我在旧 Android 版本上测试过的所有 Jellybean 设备,但在 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();
    

【问题讨论】:

【参考方案1】:

试试下面的代码:

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;
    

Manifest 需要你已经知道的权限:

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

最后你的服务应该在 Manifest 中声明如下:

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

您必须从 Activity 或 Application 类启动您的服务。

【讨论】:

谢谢,但我需要知道,你的代码和我的代码有什么区别,为什么我的代码不能只在 Lollipop 上运行?我认为我的代码与您的代码相似,并且我的 onLocationChanged() 方法没有被调用。我不需要创建服务,因为用户只想在使用应用程序时知道位置。 我认为您同时使用 gps 提供商和网络提供商,仅当您的手机上有互联网连接时才使用 gps 提供商,否则使用网络提供商。尝试在 try 块中使用 if() else 。快乐编码:) 我的应用程序再次开始工作,我没有更改任何代码。我不知道发生了什么。也许我的设备配置错误?我输入了位置设置,那里有几个选项:使用 GPS 和网络,仅使用 GPS,仅使用网络……这很奇怪。如果我更改所选选项、关闭位置然后打开,设备不会记住我的选择并返回到之前选择的选项。

以上是关于GPS 可在除 Lollipop 之外的所有设备上运行的主要内容,如果未能解决你的问题,请参考以下文章

动画 HTML5 横幅可在除 Safari 之外的所有设备中使用

Javascript 显示/隐藏带有 iframe 的 div 可在除 IE 之外的所有应用程序中使用

setMaxDate() 在 Lollipop 5.0.1 android 上无法正常工作,需要适当的解决方案

我的 Material Design ActionBar 或 Lollipop 设备中的 StatusBar 上没有显示颜色,但在 Pre-Lollipop 设备的 ActionBar 中显示。为啥?

如何禁用表单上除按钮之外的所有控件?

Android Lollipop - PackageInstaller.Session commit()