Android上的LocationManager API似乎对一个只需要偶尔粗略估计用户位置的应用程序来说有点麻烦。

我正在开发的应用程序本身并不是一个定位应用程序,但它确实需要获取用户的位置,以便显示附近企业的列表。它不需要担心用户是否在移动或类似的事情。

以下是我想做的:

向用户显示附近位置的列表。预加载用户的位置,以便在“活动X”中需要它时,它将可用。我并不特别关心更新的准确性或频率。只要不太远,只要抓住一个位置就足够了。也许如果我想变得更漂亮,我会每隔几分钟左右更新一次位置,但这不是一个很大的优先事项。适用于任何具有GPS或网络位置提供商的设备。

这似乎并不难,但在我看来,我必须组建两个不同的位置提供商(GPS和NETWORK),并管理每个提供商的生命周期。不仅如此,我还必须在多个活动中复制相同的代码以满足#2。过去,我曾尝试使用getBestProvider()将解决方案简化为仅使用一个位置提供程序,但这似乎只提供了最好的“理论”提供程序,而不是实际会为您提供最佳结果的提供程序。

有没有更简单的方法来实现这一点?


当前回答

建议使用LocationClient:

首先,定义位置更新间隔值。根据您的需要进行调整。

private static final int MILLISECONDS_PER_SECOND = 1000;
private static final long UPDATE_INTERVAL = MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
private static final int FASTEST_INTERVAL_IN_SECONDS = 1;
private static final long FASTEST_INTERVAL = MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;

让您的活动实现GooglePlayServicesClient.ConnectionCallbacks、GooglePlayServicesClient OnConnectionFailedListener和LocationListener。

public class LocationActivity extends Activity implements 
GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {}

然后,在Activity的onCreate()方法中设置LocationClient:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mLocationClient = new LocationClient(this, this, this);

    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationRequest.setInterval(UPDATE_INTERVAL);
    mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
}

将所需方法添加到“活动”中;onConnected()是LocationClient连接时调用的方法。onLocationChanged()是检索最新位置的位置。

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    Log.w(TAG, "Location client connection failed");
}

@Override
public void onConnected(Bundle dataBundle) {
    Log.d(TAG, "Location client connected");
    mLocationClient.requestLocationUpdates(mLocationRequest, this); 
}

@Override
public void onDisconnected() {
    Log.d(TAG, "Location client disconnected");
}

@Override
public void onLocationChanged(Location location) {
    if (location != null) {
        Log.d(TAG, "Updated Location: " + Double.toString(location.getLatitude()) + "," + Double.toString(location.getLongitude()));
    } else {
        Log.d(TAG, "Updated location NULL");
    } 
}     

确保连接/断开LocationClient,以便它仅在绝对必要时使用额外的电池,这样GPS不会无限期运行。LocationClient必须连接才能从中获取数据。

public void onResume() {
    super.onResume();
    mLocationClient.connect();
}

public void onStop() {
    if (mLocationClient.isConnected()) {
        mLocationClient.removeLocationUpdates(this);
    }
    mLocationClient.disconnect();
    super.onStop();
}

获取用户的位置。首先尝试使用LocationClient;如果失败,请返回LocationManager。

public Location getLocation() {
    if (mLocationClient != null && mLocationClient.isConnected()) {
        return mLocationClient.getLastLocation();
    } else {
        LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        if (locationManager != null) {
            Location lastKnownLocationGPS = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            if (lastKnownLocationGPS != null) {
                return lastKnownLocationGPS;
            } else {
                return locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            }
        } else {
            return null;
        }
    }
}

其他回答

在Activity Class中创建自定义方法:

private void getTheUserPermission() {
        ActivityCompat.requestPermissions(this, new String[]
                {Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION);
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        LocationGetter locationGetter = new LocationGetter(FreshMenuSearchActivity.this, REQUEST_LOCATION, locationManager);


        if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {

            locationGetter.OnGPS();
        } else {

            locationGetter.getLocation();
        }
    }

生成用户定义的类名LocationGetter:-

public class LocationGetter {

        private int REQUEST_LOCATION;
        private FreshMenuSearchActivity mContext;
        private LocationManager locationManager;
        private Geocoder geocoder;

        public LocationGetter(FreshMenuSearchActivity mContext, int requestLocation, LocationManager locationManager) {
            this.mContext = mContext;
            this.locationManager = locationManager;
            this.REQUEST_LOCATION = requestLocation;
        }


        public void getLocation() {

            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext,

                    Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(mContext, new String[]
                        {Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION);
            } else {
                Location LocationGps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                Location LocationNetwork = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                Location LocationPassive = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);

                if (LocationGps != null) {
                    double lat = LocationGps.getLatitude();
                    double longi = LocationGps.getLongitude();
                    getTheAddress(lat, longi);
                } else if (LocationNetwork != null) {
                    double lat = LocationNetwork.getLatitude();
                    double longi = LocationNetwork.getLongitude();
                    getTheAddress(lat, longi);
                } else if (LocationPassive != null) {
                    double lat = LocationPassive.getLatitude();
                    double longi = LocationPassive.getLongitude();
                    getTheAddress(lat, longi);
                } else {
                    Toast.makeText(mContext, "Can't Get Your Location", Toast.LENGTH_SHORT).show();
                }

            }

        }

        private void getTheAddress(double latitude, double longitude) {
            List<Address> addresses;
            geocoder = new Geocoder(mContext, Locale.getDefault());

            try {
                addresses = geocoder.getFromLocation(latitude, longitude, 1);
                String address = addresses.get(0).getAddressLine(0);
                String city = addresses.get(0).getLocality();
                String state = addresses.get(0).getAdminArea();
                String country = addresses.get(0).getCountryName();
                String postalCode = addresses.get(0).getPostalCode();
                String knownName = addresses.get(0).getFeatureName();
                Log.d("neel", address);
            } catch (IOException e) {
                e.printStackTrace();
            }


        }

        public void OnGPS() {

            final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);

            builder.setMessage("Enable GPS").setCancelable(false).setPositiveButton("YES", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    mContext.startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
                }
            }).setNegativeButton("NO", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {

                    dialog.cancel();
                }
            });
            final AlertDialog alertDialog = builder.create();
            alertDialog.show();
        }

    }

我不确定基于位置的服务是否可以从GPS以外的其他基础设施获取位置,但根据那篇文章,这似乎是可能的:

应用程序可以调用几种类型的定位方法。使用移动电话网络:当前单元格ID可用于识别基站收发信机(BTS)设备正在通信以及该BTS的位置。显然,这种方法的准确性取决于单元格的大小,以及可能非常不准确。GSM蜂窝可以是2到20直径千米。另外与细胞ID一起使用的技术可以精度在150米以内。使用卫星:全球定位系统(GPS),受控美国国防部使用由24颗卫星组成的星座围绕地球运行。GPS确定通过计算设备的位置时间差信号来自不同的卫星到达接受者GPS信号是编码的,所以移动设备必须配备使用GPS接收器。GPS是可能是最准确的方法(如果GPS接收机具有清晰的天空视图),但它也有一些缺点:硬件可能很昂贵电池使用时,需要冷启动后的热身对可见卫星的初步定位。它还受到“峡谷效应”的影响在城市,卫星能见度是间歇性的。使用短程定位信标:相对小面积,如单个建筑,局域网可以提供其他位置服务。例如,适当地配备的设备可以使用蓝牙短程定位。

Kotlin版本的@Fedor Greate回答:

类的用法:

val locationResult = object : MyLocation.LocationResult() {

    override fun gotLocation(location: Location?) {

        val lat = location!!.latitude
        val lon = location.longitude

        Toast.makeText(context, "$lat --SLocRes-- $lon", Toast.LENGTH_SHORT).show()
    }

}

val myLocation = MyLocation()
myLocation.getLocation(inflater.context, locationResult)

MyLocation类:

class MyLocation {
    internal lateinit var timer1: Timer
    internal var lm: LocationManager? = null
    internal lateinit var locationResult: LocationResult
    internal var gps_enabled = false
    internal var network_enabled = false

    internal var locationListenerGps: LocationListener = object : LocationListener {


        override fun onLocationChanged(location: Location) {
            timer1.cancel()
            locationResult.gotLocation(location)
            lm!!.removeUpdates(this)
            lm!!.removeUpdates(locationListenerNetwork)
        }

        override fun onProviderDisabled(provider: String) {}
        override fun onProviderEnabled(provider: String) {}
        override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
    }

    internal var locationListenerNetwork: LocationListener = object : LocationListener {
        override fun onLocationChanged(location: Location) {
            timer1.cancel()
            locationResult.gotLocation(location)
            lm!!.removeUpdates(this)
            lm!!.removeUpdates(locationListenerGps)
        }

        override fun onProviderDisabled(provider: String) {}
        override fun onProviderEnabled(provider: String) {}
        override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
    }

    fun getLocation(context: Context, result: LocationResult): Boolean {
        //I use LocationResult callback class to pass location value from MyLocation to user code.
        locationResult = result
        if (lm == null)
            lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager?

        //exceptions will be thrown if provider is not permitted.
        try {
            gps_enabled = lm!!.isProviderEnabled(LocationManager.GPS_PROVIDER)
        } catch (ex: Exception) {
        }

        try {
            network_enabled = lm!!.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
        } catch (ex: Exception) {
        }

        //don't start listeners if no provider is enabled
        if (!gps_enabled && !network_enabled)
            return false

        if (ActivityCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
            ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) run {

            ActivityCompat.requestPermissions(context as Activity,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 111)
        }


        if (gps_enabled)
            lm!!.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, locationListenerGps)
        if (network_enabled)
            lm!!.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f, locationListenerNetwork)
        timer1 = Timer()
        timer1.schedule(GetLastLocation(context), 20000)
        return true
    }

    internal inner class GetLastLocation(var context: Context) : TimerTask() {
        override fun run() {
            lm!!.removeUpdates(locationListenerGps)
            lm!!.removeUpdates(locationListenerNetwork)

            var net_loc: Location? = null
            var gps_loc: Location? = null

            if (ActivityCompat.checkSelfPermission(context,
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
                ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
            ) run {

                ActivityCompat.requestPermissions(context as Activity,
                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION),111)
            }


            if (gps_enabled)
                gps_loc = lm!!.getLastKnownLocation(LocationManager.GPS_PROVIDER)
            if (network_enabled)
                net_loc = lm!!.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)

            //if there are both values use the latest one
            if (gps_loc != null && net_loc != null) {
                if (gps_loc.getTime() > net_loc.getTime())
                    locationResult.gotLocation(gps_loc)
                else
                    locationResult.gotLocation(net_loc)
                return
            }

            if (gps_loc != null) {
                locationResult.gotLocation(gps_loc)
                return
            }
            if (net_loc != null) {
                locationResult.gotLocation(net_loc)
                return
            }
            locationResult.gotLocation(null)
        }
    }

     abstract class LocationResult {
          abstract fun gotLocation(location: Location?)
    }
}

这里有点晚了,但在这种情况下,我会使用谷歌地图API,并使用谷歌地图的纬度和经度API标记附近的位置。另外,如果你能在地图上显示他/她的位置,用户体验会更好。无需担心用户位置的更新或使用android api搜索。让谷歌地图为您处理内部内容。

@emmby可能已经在他的应用程序中解决了这个问题,但为了将来的参考,我向其他开发人员推荐的是查看谷歌地图API中的位置特定内容。

编辑:在谷歌地图中显示用户位置的链接

在搜索了如何获得最佳精确用户位置的最佳实现后,我成功地结合了所有最佳方法,并提出了以下类:

/**
 * Retrieve accurate location from GPS or network services. 
 * 
 *
 * Class usage example:
 * 
 * public void onCreate(Bundle savedInstanceState) {
 *      ...
 *      my_location = new MyLocation();
 *      my_location.init(main.this, locationResult);
 * }
 * 
 * 
 * public LocationResult locationResult = new LocationResult(){
 *      @Override
 *      public void gotLocation(final Location location){
 *          // do something
 *          location.getLongitude();
 *          location.getLatitude();
 *      }
 *  };
 */
class MyLocation{

    /**
     * If GPS is enabled. 
     * Use minimal connected satellites count.
     */
    private static final int min_gps_sat_count = 5;

    /**
     * Iteration step time.
     */
    private static final int iteration_timeout_step = 500;

    LocationResult locationResult;
    private Location bestLocation = null;
    private Handler handler = new Handler();
    private LocationManager myLocationManager; 
    public Context context;

    private boolean gps_enabled = false;

    private int counts    = 0;
    private int sat_count = 0;

    private Runnable showTime = new Runnable() {
    
         public void run() {
            boolean stop = false;
            counts++;
            System.println("counts=" + counts);
            
            //if timeout (1 min) exceeded, stop tying
            if(counts > 120){
                stop = true;
            }
            
            //update last best location
            bestLocation = getLocation(context);
            
            //if location is not ready or don`t exists, try again
            if(bestLocation == null && gps_enabled){
                System.println("BestLocation not ready, continue to wait");
                handler.postDelayed(this, iteration_timeout_step);
            }else{
                //if best location is known, calculate if we need to continue to look for better location
                //if gps is enabled and min satellites count has not been connected or min check count is smaller then 4 (2 sec)  
                if(stop == false && !needToStop()){
                    System.println("Connected " + sat_count + " sattelites. continue waiting..");
                    handler.postDelayed(this, iteration_timeout_step);
                }else{
                    System.println("#########################################");
                    System.println("BestLocation found return result to main. sat_count=" + sat_count);
                    System.println("#########################################");

                    // removing all updates and listeners
                    myLocationManager.removeUpdates(gpsLocationListener);
                    myLocationManager.removeUpdates(networkLocationListener);    
                    myLocationManager.removeGpsStatusListener(gpsStatusListener);
                    sat_count = 0;
                    
                    // send best location to locationResult
                    locationResult.gotLocation(bestLocation);
                }
            }
         }
    };
        
    /**
     * Determine if continue to try to find best location
     */
    private Boolean needToStop(){

        if(!gps_enabled){
                          return true;
                     }
          else if(counts <= 4){
                return false;
            }
            if(sat_count < min_gps_sat_count){
                //if 20-25 sec and 3 satellites found then stop
                if(counts >= 40 && sat_count >= 3){
                    return true;
                }
                return false;
            }
        }
        return true;
    }

    /**
     * Best location abstract result class
     */
    public static abstract class LocationResult{
         public abstract void gotLocation(Location location);
     }

    /**
     * Initialize starting values and starting best location listeners
     * 
     * @param Context ctx
     * @param LocationResult result
     */
    public void init(Context ctx, LocationResult result){
        context = ctx;
        locationResult = result;
    
        myLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    
        gps_enabled = (Boolean) myLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    
        bestLocation = null;
        counts = 0;
    
        // turning on location updates
        myLocationManager.requestLocationUpdates("network", 0, 0, networkLocationListener);
        myLocationManager.requestLocationUpdates("gps", 0, 0, gpsLocationListener);
        myLocationManager.addGpsStatusListener(gpsStatusListener);
    
        // starting best location finder loop
        handler.postDelayed(showTime, iteration_timeout_step);
    }

    /**
     * GpsStatus listener. OnChainged counts connected satellites count.
     */
    public final GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() {
        public void onGpsStatusChanged(int event) {
        
             if(event == GpsStatus.GPS_EVENT_SATELLITE_STATUS){
                try {
                    // Check number of satellites in list to determine fix state
                     GpsStatus status = myLocationManager.getGpsStatus(null);
                     Iterable<GpsSatellite>satellites = status.getSatellites();
                     
                     sat_count = 0;
                     
                     Iterator<GpsSatellite>satI = satellites.iterator();
                     while(satI.hasNext()) {
                         GpsSatellite satellite = satI.next();
                         System.println("Satellite: snr=" + satellite.getSnr() + ", elevation=" + satellite.getElevation());                         
                         sat_count++;
                     }
                } catch (Exception e) {
                    e.printStackTrace();
                    sat_count = min_gps_sat_count + 1;
                }
                 
                 System.println("#### sat_count = " + sat_count);
             }
         }
    };

    /**
     * Gps location listener.
     */
    public final LocationListener gpsLocationListener = new LocationListener(){
        @Override
         public void onLocationChanged(Location location){
        
        }
         public void onProviderDisabled(String provider){}
         public void onProviderEnabled(String provider){}
         public void onStatusChanged(String provider, int status, Bundle extras){}
    }; 

    /**
     * Network location listener.
     */
    public final LocationListener networkLocationListener = new LocationListener(){
        @Override
         public void onLocationChanged(Location location){
        
        }
         public void onProviderDisabled(String provider){}
         public void onProviderEnabled(String provider){}
         public void onStatusChanged(String provider, int status, Bundle extras){}
    }; 


    /**
     * Returns best location using LocationManager.getBestProvider()
     * 
     * @param context
     * @return Location|null
     */
    public static Location getLocation(Context context){
        System.println("getLocation()");
    
        // fetch last known location and update it
        try {
            LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
             
            Criteria criteria = new Criteria();
            criteria.setAccuracy(Criteria.ACCURACY_FINE);
             criteria.setAltitudeRequired(false);
             criteria.setBearingRequired(false);
             criteria.setCostAllowed(true);
             String strLocationProvider = lm.getBestProvider(criteria, true);
            
             System.println("strLocationProvider=" + strLocationProvider);
             Location location = lm.getLastKnownLocation(strLocationProvider);
             if(location != null){
                return location;
             }
             return null;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

如果启用gps,此类将尝试连接到min_gps_sat_count卫星。Else返回LocationManager.getBestProvider()位置。检查代码!