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

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

以下是我想做的:

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

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

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


当前回答

通过使用FusedLocationProviderApi,这是最新的API,也是在Android中获取位置的最佳可能性。将其添加到build.gradle文件中

dependencies {
    compile 'com.google.android.gms:play-services:6.5.87'
}

您可以通过此url获取完整的源代码http://javapapers.com/android/android-location-fused-provider/

其他回答

实际上,我们可以使用两个提供商(GPS和网络)。他们只是分享一个公共听众:

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

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

通过使用FusedLocationProviderApi,这是最新的API,也是在Android中获取位置的最佳可能性。将其添加到build.gradle文件中

dependencies {
    compile 'com.google.android.gms:play-services:6.5.87'
}

您可以通过此url获取完整的源代码http://javapapers.com/android/android-location-fused-provider/

看到所有答案后,提出问题(最简单、最稳健)。我只点击了图书馆Android ReactiveLocation。

当我制作一个位置跟踪应用程序时。然后我意识到,通过优化电池处理位置跟踪是非常典型的。

所以我想告诉新手和开发者,他们不想在未来的优化中维护他们的位置代码。使用此库。

ReactiveLocationProvider locationProvider = new 

    ReactiveLocationProvider(context);
    locationProvider.getLastKnownLocation()
        .subscribe(new Consumer<Location>() {
            @Override
            public void call(Location location) {
                doSthImportantWithObtainedLocation(location);
            }
        });

要放入应用程序级别build.gradle的依赖项

dependencies {
    ...
    compile 'pl.charmas.android:android-reactive-location2:2.1@aar'
    compile 'com.google.android.gms:play-services-location:11.0.4' //you can use newer GMS version if you need
    compile 'com.google.android.gms:play-services-places:11.0.4'
    compile 'io.reactivex:rxjava:2.0.5' //you can override RxJava version if you need
}

使用此库的优点:

现在和将来都将积极维护这种自由。您不必担心电池优化。因为开发人员已经尽力了。易于安装,放置依赖项和播放。轻松连接到Play Services API获取最后一个已知位置订阅位置更新使用位置设置API管理地理围栏地址列表的地理编码位置活动识别使用当前位置API获取位置自动完成建议

尽管这里已经给出了答案。我只是想向全世界分享这一点,以防遇到这种情况。

我的要求是,我需要在最长30到35秒内获取用户的当前位置,所以下面是我根据Nirav Ranpara的回答制定的解决方案。

1.我制作了MyLocationManager.java类,该类处理所有GPS和网络内容

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.app.callbacks.OnLocationDetectectionListener;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;

public class MyLocationManager {
    /** The minimum distance to GPS change Updates in meters **/
    private final long MIN_DISTANCE_CHANGE_FOR_UPDATES_FOR_GPS = 2; // 2
                                                                    // meters
    /** The minimum time between GPS updates in milliseconds **/
    private final long MIN_TIME_BW_UPDATES_OF_GPS = 1000 * 5 * 1; // 5
                                                                    // seconds

    /** The minimum distance to NETWORK change Updates in meters **/
    private final long MIN_DISTANCE_CHANGE_FOR_UPDATES_FOR_NETWORK = 5; // 5
                                                                        // meters
    /** The minimum time between NETWORK updates in milliseconds **/
    private final long MIN_TIME_BW_UPDATES_OF_NETWORK = 1000 * 10 * 1; // 10
                                                                        // seconds

    /**
     * Lets just say i don't trust the first location that the is found. This is
     * to avoid that
     **/

    private int NetworkLocationCount = 0, GPSLocationCount = 0;
    private boolean isGPSEnabled;
    private boolean isNetworkEnabled;
    /**
     * Don't do anything if location is being updated by Network or by GPS
     */
    private boolean isLocationManagerBusy;
    private LocationManager locationManager;
    private Location currentLocation;
    private Context mContext;
    private OnLocationDetectectionListener mListener;

    public MyLocationManager(Context mContext,
            OnLocationDetectectionListener mListener) {
        this.mContext = mContext;
        this.mListener = mListener;
    }

    /**
     * Start the location manager to find my location
     */
    public void startLocating() {
        try {
            locationManager = (LocationManager) mContext
                    .getSystemService(Context.LOCATION_SERVICE);

            // Getting GPS status
            isGPSEnabled = locationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);

            // Getting network status
            isNetworkEnabled = locationManager
                    .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnabled && !isNetworkEnabled) {
                // No network provider is enabled
                showSettingsAlertDialog();
            } else {
                // If GPS enabled, get latitude/longitude using GPS Services
                if (isGPSEnabled) {
                    locationManager.requestLocationUpdates(
                            LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES_OF_GPS,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES_FOR_GPS,
                            gpsLocationListener);

                }
                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(
                            LocationManager.NETWORK_PROVIDER,
                            MIN_TIME_BW_UPDATES_OF_NETWORK,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES_FOR_NETWORK,
                            networkLocationListener);

                }
            }
            /**
             * My 30 seconds plan to get myself a location
             */
            ScheduledExecutorService se = Executors
                    .newSingleThreadScheduledExecutor();
            se.schedule(new Runnable() {

                @Override
                public void run() {
                    if (currentLocation == null) {
                        if (isGPSEnabled) {
                            currentLocation = locationManager
                                    .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        } else if (isNetworkEnabled) {
                            currentLocation = locationManager
                                    .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

                        }
                        if (currentLocation != null && mListener != null) {
                            locationManager.removeUpdates(gpsLocationListener);
                            locationManager
                                    .removeUpdates(networkLocationListener);
                            mListener.onLocationDetected(currentLocation);
                        }
                    }
                }
            }, 30, TimeUnit.SECONDS);

        } catch (Exception e) {
            Log.e("Error Fetching Location", e.getMessage());
            Toast.makeText(mContext,
                    "Error Fetching Location" + e.getMessage(),
                    Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Handle GPS location listener callbacks
     */
    private LocationListener gpsLocationListener = new LocationListener() {

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onProviderEnabled(String provider) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onProviderDisabled(String provider) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onLocationChanged(Location location) {

            if (GPSLocationCount != 0 && !isLocationManagerBusy) {
                Log.d("GPS Enabled", "GPS Enabled");
                isLocationManagerBusy = true;
                currentLocation = location;
                locationManager.removeUpdates(gpsLocationListener);
                locationManager.removeUpdates(networkLocationListener);
                isLocationManagerBusy = false;
                if (currentLocation != null && mListener != null) {
                    mListener.onLocationDetected(currentLocation);
                }
            }
            GPSLocationCount++;
        }
    };
    /**
     * Handle Network location listener callbacks
     */
    private LocationListener networkLocationListener = new LocationListener() {

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onProviderEnabled(String provider) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onProviderDisabled(String provider) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onLocationChanged(Location location) {
            if (NetworkLocationCount != 0 && !isLocationManagerBusy) {
                Log.d("Network", "Network");
                isLocationManagerBusy = true;
                currentLocation = location;
                locationManager.removeUpdates(gpsLocationListener);
                locationManager.removeUpdates(networkLocationListener);
                isLocationManagerBusy = false;
                if (currentLocation != null && mListener != null) {
                    mListener.onLocationDetected(currentLocation);
                }
            }
            NetworkLocationCount++;
        }
    };

    /**
     * Function to show settings alert dialog. On pressing the Settings button
     * it will launch Settings Options.
     * */
    public void showSettingsAlertDialog() {
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

        // Setting Dialog Title
        alertDialog.setTitle("GPS is settings");

        // Setting Dialog Message
        alertDialog
                .setMessage("GPS is not enabled. Do you want to go to settings menu?");

        // On pressing the Settings button.
        alertDialog.setPositiveButton("Settings",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(
                                Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        mContext.startActivity(intent);
                    }
                });

        // On pressing the cancel button
        alertDialog.setNegativeButton("Cancel",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });

        // Showing Alert Message
        alertDialog.show();
    }
}

2.我创建了一个接口(回调)OnLocationDetectionListener.java,以便将结果传递回调用片段或活动

import android.location.Location;

public interface OnLocationDetectectionListener {
    public void onLocationDetected(Location mLocation);
}

3.然后我制作了一个MainAppActivty.java Activity,它实现了OnLocationDetectionListener接口,下面是我如何在其中接收位置

public class MainAppActivty extends Activity implements
        OnLocationDetectectionListener {

    private Location currentLocation;
    private MyLocationManager mLocationManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_home);
        super.onCreate(savedInstanceState);
            mLocationManager = new MyLocationManager(this, this);
            mLocationManager.startLocating();
    }

    @Override
    public void onLocationDetected(Location mLocation) {
        //Your new Location is received here
        currentLocation = mLocation;
    }

4.将以下权限添加到清单文件

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

希望这对其他人有帮助:)

对@Fedor解决方案的改进。我们可以使用位置管理器的requestSingleUpdate方法,而不是使用“0”时间间隔和“0”距离请求位置。更新的代码(kotlin版本)

import android.annotation.SuppressLint
import android.content.Context
import android.location.Criteria
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import java.util.*

@SuppressLint("MissingPermission")
class AppLocationProvider {

  private lateinit var timer: Timer
  private var locationManager: LocationManager? = null
  private lateinit var locationCallBack: LocationCallBack
  private var gpsEnabled = false
  private var networkEnabled = false

  private var locationListener: LocationListener = object : LocationListener {

    override fun onLocationChanged(location: Location) {
      timer.cancel()
      locationCallBack.locationResult(location)
    }

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

  fun getLocation(context : Context, callBack: LocationCallBack): Boolean {
    locationCallBack = callBack
    if (locationManager == null)
      locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager?

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

    try {
      networkEnabled = locationManager!!.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
    } catch (ex: Exception) {
      ex.printStackTrace()
    }

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

    val criteria = Criteria()
    if (gpsEnabled) {
      criteria.accuracy = Criteria.ACCURACY_FINE
    } else {
      criteria.accuracy = Criteria.ACCURACY_COARSE
    }
    locationManager!!.requestSingleUpdate(criteria, locationListener, null)

    timer = Timer()
    timer.schedule(GetLastKnownLocation(), 5000)
    return true
  }

  inner class GetLastKnownLocation : TimerTask() {

    override fun run() {
      locationManager!!.removeUpdates(locationListener)

      var netLoc: Location? = null
      var gpsLoc: Location? = null

      if (gpsEnabled)
        gpsLoc = locationManager!!.getLastKnownLocation(LocationManager.GPS_PROVIDER)
      if (networkEnabled)
        netLoc = locationManager!!.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)

      //check which value use the latest one
      if (gpsLoc != null && netLoc != null) {
        if (gpsLoc.time > netLoc.time)
          locationCallBack.locationResult(gpsLoc)
        else
          locationCallBack.locationResult(netLoc)
        return
      }

      if (gpsLoc != null) {
        locationCallBack.locationResult(gpsLoc)
        return
      }
      if (netLoc != null) {
        locationCallBack.locationResult(netLoc)
        return
      }
      locationCallBack.locationResult(null)
    }
  }

  interface LocationCallBack {
    fun locationResult(location: Location?)
  }
}

要获取位置,只需调用getLocation方法-

AppLocationProvider().getLocation(context, object : AppLocationProvider.LocationCallBack {
  override fun locationResult(location: Location?) {
    // use location, this might get called in a different thread if a location is a last known location. In that case, you can post location on main thread
  }
})

注:在调用getLocation方法之前,必须授予所需的位置权限。