在安卓系统上,获取用户当前位置的最简单和最稳健的方法是什么?

859
在Android上,LocationManager API似乎对于仅需要偶尔和粗略近似用户位置的应用程序而言有些麻烦。
我正在开发的应用程序实际上不是一个定位应用程序,但它需要获取用户位置以便显示附近企业的列表。它无需担心用户是否在移动等方面。
以下是我的想法:
  1. 向用户显示附近位置列表。
  2. 预加载用户位置,以便在我需要在 Activity X 中使用它时,它将可用。
  3. 我不特别关心更新的准确性或频率。只需获得一个位置足以,只要它不完全错误。也许,如果我想要花哨一点,每隔几分钟更新一次位置,但这不是一个很重要的事情。
  4. 适用于任何具有GPS或网络位置提供商的设备。
看起来这不应该太难,但我似乎必须启动两个不同的位置提供程序(GPS和NETWORK)并管理每个提供程序的生命周期。此外,我还必须在多个活动中复制相同的代码以满足第2条要求。我之前尝试过使用getBestProvider()来将解决方案缩减为仅使用一个位置提供程序,但它似乎只会给出最好的“理论”提供程序,而不是实际上会给出最好结果的提供程序。
有没有更简单的方法来实现这一点?

1
你可以使用一个简单的库,它抽象了所有必须在“引擎盖下”发生的事情: https://github.com/delight-im/Android-SimpleLocation - caw
在这里获取 Kotlin 的答案:https://dev59.com/QnA75IYBdhLWcg3wuLjD#53800632 - MHSaffari
你可以在安卓中使用融合定位捕捉技术。 - SIVAKUMAR.J
FusedLocation 工作得很好(虽然我仍然不知道 getCurrentLocation() 为什么要被弃用)。 - Taslim Oseni
28个回答

969

以下是我的工作内容:

  1. 首先,我会检查设备上启用了哪些提供程序。有些可能在设备上被禁用,有些可能在应用程序清单中被禁用。
  2. 如果有任何提供程序可用,我会启动位置监听器和超时计时器。在我的示例中为20秒,对于GPS来说可能不够,您可以将其加大。
  3. 如果我从位置监听器中获得更新,则使用提供的值。我停止监听器和计时器。
  4. 如果我没有收到任何更新并且计时器已经过期,则必须使用最后已知的值。
  5. 我从可用的提供程序中获取最后已知的值,并选择其中最新的一个。

以下是如何使用我的类:

LocationResult locationResult = new LocationResult(){
    @Override
    public void gotLocation(Location location){
        //Got the location!
    }
};
MyLocation myLocation = new MyLocation();
myLocation.getLocation(this, locationResult);

以下是MyLocation类:

import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;

public class MyLocation {
    Timer timer1;
    LocationManager lm;
    LocationResult locationResult;
    boolean gps_enabled=false;
    boolean network_enabled=false;

    public boolean getLocation(Context context, LocationResult result)
    {
        //I use LocationResult callback class to pass location value from MyLocation to user code.
        locationResult=result;
        if(lm==null)
            lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

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

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

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

    LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location) {
            timer1.cancel();
            locationResult.gotLocation(location);
            lm.removeUpdates(this);
            lm.removeUpdates(locationListenerNetwork);
        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    };

    LocationListener locationListenerNetwork = new LocationListener() {
        public void onLocationChanged(Location location) {
            timer1.cancel();
            locationResult.gotLocation(location);
            lm.removeUpdates(this);
            lm.removeUpdates(locationListenerGps);
        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    };

    class GetLastLocation extends TimerTask {
        @Override
        public void run() {
             lm.removeUpdates(locationListenerGps);
             lm.removeUpdates(locationListenerNetwork);

             Location net_loc=null, gps_loc=null;
             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);
        }
    }

    public static abstract class LocationResult{
        public abstract void gotLocation(Location location);
    }
}

有些人可能想修改我的逻辑。例如,如果您从网络提供商获取更新,则不要停止侦听器,而是继续等待。GPS提供更精确的数据,因此值得等待。如果计时器到期并且您已经从网络获取了更新但没有从GPS获取,则可以使用网络提供的值。

另一种方法是使用LocationClient http://developer.android.com/training/location/retrieve-current.html。但它需要在用户设备上安装Google Play服务apk。


4
嗨Fedor,谢谢你的回复,它看起来与我以前做过的项目非常相似。这也促使我提出了这个问题:肯定有更简单的方法吧?! - emmby
7
据我理解,谷歌通常建议不要使用android:configChanges。相反,我选择了一种在onDestroy中注册计时器任务并取消它们的解决方案。 - emmby
50
这对我非常有效!但是,我用它来检查履历上的位置信息。如果用户在位置信息返回之前退出该活动,则应用程序会崩溃。我通过将此方法添加到MyLocation类中来解决这个问题。public void cancelTimer() { timer1.cancel(); lm.removeUpdates(locationListenerGps); lm.removeUpdates(locationListenerNetwork); } 我在onPause()中调用它,这样就修复了崩溃问题。 - dbaugh
4
这段代码如何获取位置信息?请展示如何将其转化为toast或其他形式的输出。 - Adam Varhegyi
3
如果您忘记添加 USES-PERMISSION,会很麻烦。 - Rudolf Real
显示剩余32条评论

49

在寻找最佳实现方式以获取最精确定位用户位置后,我成功地将所有最佳方法结合起来,创建了以下类:

/**
 * 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;
        }
    }
}

这个类尝试连接到min_gps_sat_count颗卫星,如果启用了GPS。否则,返回LocationManager.getBestProvider()的位置。检查代码!

2
它将在1分钟后停止或找到位置时停止。 - wormhit
@wormhit 在我的情况下,它在计数到120后仍未停止。我已启用了GPS和网络提供程序也可用,并在Menifest文件中添加了适当的权限。 - Narendra Pal
只需将以下内容导入即可: import java.util.Iterator; import android.content.Context; import android.location.Criteria; import android.location.GpsSatellite; import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; - Jackie Chan

33
通过Fedor的解决方案,我遇到了多次执行回调gotLocation的问题。这似乎是由于重写的LocationListener.onLocationChanged方法中出现了竞争条件,当gotLocation方法“足够长”时会出现这种情况。我不确定,但我猜想removeUpdates会防止在Looper队列中排队新消息的入队,但它不会删除已经入队但尚未被消耗的消息。因此存在竞争条件。
为了减少这种错误行为的概率,可以在触发onLocationChanged事件之前调用removeUpdates,但仍然存在竞争条件。
我找到的最好的解决方案是将requestLocationUpdates替换为requestSingleUpdate。
以下是我的版本,基于Fedor的解决方案,使用Handler将消息发送到looper线程:
public class LocationResolver {
    private Timer timer;
    private LocationManager locationManager;
    private LocationResult locationResult;
    private boolean gpsEnabled = false;
    private boolean networkEnabled = false;
    private Handler locationTimeoutHandler;

    private final Callback locationTimeoutCallback = new Callback() {
        public boolean handleMessage(Message msg) {
            locationTimeoutFunc();
            return true;
        }

        private void locationTimeoutFunc() {   
            locationManager.removeUpdates(locationListenerGps);
            locationManager.removeUpdates(locationListenerNetwork);

            Location networkLocation = null, gpsLocation = null;
            if (gpsEnabled)
                gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            if (networkEnabled)
                networkLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

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

            if (gpsLocation != null) {
                locationResult.gotLocation(gpsLocation);
                return;
            }
            if (networkLocation != null) {
                locationResult.gotLocation(networkLocation);
                return;
            }
            locationResult.gotLocation(null);           
        }
    };
    private final LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location) {              
            timer.cancel();
            locationResult.gotLocation(location);
            locationManager.removeUpdates(this);
            locationManager.removeUpdates(locationListenerNetwork);
        }

        public void onProviderDisabled(String provider) {
        }

        public void onProviderEnabled(String provider) {
        }

        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
    };
    private final LocationListener locationListenerNetwork = new LocationListener() {
        public void onLocationChanged(Location location) {    
            timer.cancel(); 
            locationResult.gotLocation(location);
            locationManager.removeUpdates(this);
            locationManager.removeUpdates(locationListenerGps);
        }

        public void onProviderDisabled(String provider) {
        }

        public void onProviderEnabled(String provider) {
        }

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

    public void prepare() {
        locationTimeoutHandler = new Handler(locationTimeoutCallback);
    }

    public synchronized boolean getLocation(Context context, LocationResult result, int maxMillisToWait) {
        locationResult = result;
        if (locationManager == null)
            locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

        // exceptions will be thrown if provider is not permitted.
        try {
            gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        } catch (Exception ex) {
        }
        try {
            networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        } catch (Exception ex) {
        }

        // don't start listeners if no provider is enabled
        if (!gpsEnabled && !networkEnabled)
            return false;

        if (gpsEnabled)
            locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, locationListenerGps, Looper.myLooper());
            //locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
        if (networkEnabled)
            locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, locationListenerNetwork, Looper.myLooper());
            //locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);

        timer = new Timer();
        timer.schedule(new GetLastLocationTask(), maxMillisToWait);
        return true;
    }

    private class GetLastLocationTask extends TimerTask {
        @Override
        public void run() { 
            locationTimeoutHandler.sendEmptyMessage(0);
        }
    }

    public static abstract class LocationResult {
        public abstract void gotLocation(Location location);
    }
}

我使用这个类来自一个定制的looper线程,就像下面这样:
public class LocationGetter {
    private final Context context;
    private Location location = null;
    private final Object gotLocationLock = new Object();
    private final LocationResult locationResult = new LocationResult() {            
        @Override
        public void gotLocation(Location location) {
            synchronized (gotLocationLock) {
                LocationGetter.this.location = location;
                gotLocationLock.notifyAll();
                Looper.myLooper().quit();
            }
        }
    };

    public LocationGetter(Context context) {
        if (context == null)
            throw new IllegalArgumentException("context == null");

        this.context = context;
    }

    public synchronized Coordinates getLocation(int maxWaitingTime, int updateTimeout) {
        try {
            final int updateTimeoutPar = updateTimeout;
            synchronized (gotLocationLock) {            
                new Thread() {
                    public void run() {
                        Looper.prepare();
                        LocationResolver locationResolver = new LocationResolver();
                        locationResolver.prepare();
                        locationResolver.getLocation(context, locationResult, updateTimeoutPar);
                        Looper.loop();
                    }
                }.start();

                gotLocationLock.wait(maxWaitingTime);
            }
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        if (location != null)
            coordinates = new Coordinates(location.getLatitude(), location.getLongitude());
        else
            coordinates = Coordinates.UNDEFINED;
        return coordinates; 
    }
}

Coordinates是一个简单的类,具有两个属性:纬度和经度。


4
有趣的一点,无论如何,requestSingleUpdate需要API Level 9。但是加一赞你指出了这一点。 - Eduardo
@Eduardo:另一种可能性是使用与looper相关联的Handler执行任务代码,并通过向处理程序发送(空)消息来调用它。由于消息在looper队列上进行序列化,因此您可以手动删除竞争条件(例如使用标志)。这不需要API Livel 9,但需要显式使用looper。 - differenziale

19

我创建了一个小应用程序,详细描述如何获取当前位置的GPS坐标。

enter image description here

完整的示例源代码在下面的URL中:


在Android中获取当前位置坐标、城市名称


看看它是如何工作的:

  • 我们需要做的就是在清单文件中添加此权限

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION">  
</uses-permission>
并创建如下所示的LocationManager实例。
LocationManager locationManager = (LocationManager) 
                                  getSystemService(Context.LOCATION_SERVICE);
  • 检查GPS是否启用

  • 然后实现LocationListener并获取坐标

  • LocationListener locationListener = new MyLocationListener();  
    locationManager.requestLocationUpdates(  
    LocationManager.GPS_PROVIDER, 5000, 10, locationListener);
    
  • 这是一个实现的示例代码


  • /*----------Listener class to get coordinates ------------- */
    private class MyLocationListener implements LocationListener {
    
        @Override
        public void onLocationChanged(Location loc) {
            editLocation.setText("");
            pb.setVisibility(View.INVISIBLE);
            Toast.makeText(
                getBaseContext(),
                "Location changed: Lat: " + loc.getLatitude() + " Lng: "
                    + loc.getLongitude(), Toast.LENGTH_SHORT).show();
            String longitude = "Longitude: " + loc.getLongitude();
            Log.v(TAG, longitude);
            String latitude = "Latitude: " + loc.getLatitude();
            Log.v(TAG, latitude);
            /*-------to get City-Name from coordinates -------- */
            String cityName = null;
            Geocoder gcd = new Geocoder(getBaseContext(), Locale.getDefault());
            List<Address> addresses;
            try {
                addresses = gcd.getFromLocation(loc.getLatitude(),
                    loc.getLongitude(), 1);
                if (addresses.size() > 0)
                    System.out.println(addresses.get(0).getLocality());
                cityName = addresses.get(0).getLocality();
            } catch (IOException e) {
                e.printStackTrace();
            }
            String s = longitude + "\n" + latitude + "\n\nMy Current City is: "
                + cityName;
            editLocation.setText(s);
        }
    
        @Override
        public void onProviderDisabled(String provider) {}
    
        @Override
        public void onProviderEnabled(String provider) {}
    
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    }
    

    1
    位置需要改变。不要在站立时进行修改。 - user3575963

    18

    您可以始终使用LocationManager.getLastKnownLocation(),但正如它所说的那样,它可能已过时。

    而获取大致位置的简单方法是注册网络(通常非常快速)。

    LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    locationManager.requestLocationUpdates(
         LocationManager.NETWORK_PROVIDER, 1000, 1000, this);
    

    然后执行

    locationManager.removeUpdates(this);
    

    在监听器的onLocationChanged()方法中。


    感谢BrennaSoft。我发现getLastKnownLocation()经常会偏差很大,因此不能作为独立的解决方案。此外,我不确定仅依赖NETWORK_PROVIDER是否可行,因为该国许多地区的wifi接入点没有非常好的GPS坐标(而我不知道手机塔)。 - emmby

    11

    我在demonuts.com写了一个详细的教程,涵盖了获取当前位置。你可以在这里找到更多描述,并下载整个演示源代码以便更好地理解。

    虽然已有许多答案,但我想展示使用Google API获取位置的最新方法,以便新程序员可以使用这种新方法:

    首先,在gradle文件中加入以下内容:

    compile 'com.google.android.gms:play-services:8.4.0'
    

    然后实现必要的接口

    public class MainActivity  extends BaseActivitiy implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener
    

    声明实例

      private GoogleApiClient mGoogleApiClient;
      private Location mLocation;
      private LocationManager locationManager;
      private LocationRequest mLocationRequest;
    

    将此放入 onCreate()

     mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
            locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    

    最后,覆盖必要的方法

     @Override
        public void onConnected(Bundle bundle) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }
            mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            if(mLocation == null){
                startLocationUpdates();
            }
            if (mLocation != null) {
                double latitude = mLocation.getLatitude();
                double longitude = mLocation.getLongitude();
            } else {
                // Toast.makeText(this, "Location not Detected", Toast.LENGTH_SHORT).show();
            }
        }
    
        protected void startLocationUpdates() {
            // Create the location request
            mLocationRequest = LocationRequest.create()
                    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                    .setInterval(UPDATE_INTERVAL)
                    .setFastestInterval(FASTEST_INTERVAL);
            // Request location updates
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
                    mLocationRequest, this);
            Log.d("reque", "--->>>>");
        }
    
        @Override
        public void onConnectionSuspended(int i) {
            Log.i(TAG, "Connection Suspended");
            mGoogleApiClient.connect();
        }
    
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Log.i(TAG, "Connection failed. Error: " + connectionResult.getErrorCode());
        }
    
        @Override
        public void onStart() {
            super.onStart();
            mGoogleApiClient.connect();
        }
    
        @Override
        public void onStop() {
            super.onStop();
            if (mGoogleApiClient.isConnected()) {
                mGoogleApiClient.disconnect();
            }
        }
        @Override
        public void onLocationChanged(Location location) {
    
        }
    

    在运行应用程序之前,请不要忘记在您的设备中启动GPS。


    使用API?那么这个服务是有速率限制的,对吧。 - user3304007

    10

    实际上我们可以使用两个提供者(GPS和NETWORK)。它们只共享一个公共的监听器:

    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10 * 1000, (float) 10.0, listener);
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 90 * 1000, (float) 10.0, listener);
    

    这是必要的,因为OnLocationChanged()方法总是需要及时调用。


    7
    使用以下代码,它将提供最佳的服务提供商:
    String locCtx = Context.LOCATION_SERVICE; 
    
    LocationManager locationMgr = (LocationManager) ctx.getSystemService(locCtx);
    
    Criteria criteria  = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    criteria.setAltitudeRequired(false);
    criteria.setBearingRequired(false);
    criteria.setCostAllowed(true);
    criteria.setPowerRequirement(Criteria.POWER_LOW);
    
    String provider = locationMgr.getBestProvider(criteria, true);
    
    System.out.println("Best Available provider::::"+provider);
    

    如何初始化ctx,Context ctx= this;?它崩溃了。 - user3575963

    7
    我不确定基于位置的服务是否可以从除GPS以外的其他基础设施获取位置信息,但根据那篇文章,这似乎是可能的。
    应用程序可以调用多种类型的定位方法。
    使用移动电话网络:当前的小区ID可以用于确定设备正在与哪个基站通信以及该基站的位置。显然,此方法的准确性取决于小区的大小,并且可能非常不准确。 GSM小区的直径可以在2到20公里之间。除小区ID外,其他技术结合使用可以实现150米内的精度。
    使用卫星:由美国国防部控制的全球定位系统(GPS)使用绕地球轨道的24颗卫星星座。 GPS通过计算来自不同卫星的信号到达接收器所需的时间差来确定设备的位置。 GPS信号被编码,因此移动设备必须配备GPS接收器。 GPS是最准确的方法(如果GPS接收器有清晰的天空视野,则为4到40米之间),但它也有一些缺点:额外的硬件成本高,在使用时会耗电并且需要在冷启动后进行一些预热以获取可见卫星的初始修复。 它在城市中遭受“峡谷效应”,其中卫星可见性间歇性。
    使用短距离定位信标:在相对较小的区域内,例如单个建筑物,局域网可以提供位置以及其他服务。 例如,适当配备的设备可以使用蓝牙进行短距离定位。

    1
    @ElijahSaounkine 显然npinti没有听说过辅助GPS。将来,您可以仅告知人们他们忽略的事情,而不是对此采取攻击性的态度。 - saltandpepper
    @Sammy 5年前的我似乎比现在更容易激动。关于我的留言中提到的“未来”,可能已经开始并结束了 ;) - Ilya Saunkin

    6
    推荐的方法是使用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;
    

    请让您的Activity实现GooglePlayServicesClient.ConnectionCallbacksGooglePlayServicesClient.OnConnectionFailedListenerLocationListener
    public class LocationActivity extends Activity implements 
    GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {}
    

    然后,在您的ActivityonCreate()方法中设置一个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);
    }
    

    将所需的方法添加到您的Activity中;当LocationClient连接时,会调用onConnected()方法。在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;
            }
        }
    }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接