Android(安卓)位置服务
Android 位置 API 使您可以轻松构建位置感知应用程序,而无需关注底层位置技术的细节。
这在 Google Play 服务 的帮助下成为可能,该服务通过自动位置跟踪、地理围栏和活动识别为您的应用程序添加位置感知。
本教程向您展示如何在 APP 中使用位置服务来获取当前位置、定期更新位置、查找地址等。
Location 对象
Location
对象表示一个地理位置,它可以由纬度、经度、时间戳和其他信息(如方位角、高度和速度)组成。
有以下重要方法可以用于 Location
对象以获取特定于位置的信息:
编号 | 方法 & 描述 |
---|---|
1 | float distanceTo(Location dest) 返回此位置与给定位置之间的近似距离(米)。 |
2 | float getAccuracy() 获取此位置的估计精度,单位为米。 |
3 | double getAltitude() 获取海拔高度(如有),以海平面以上米为单位。 |
4 | float getBearing() 以度为单位获取方位角。 |
5 | double getLatitude() 获取纬度,单位为度。 |
6 | double getLongitude() 获取经度,单位为度。 |
7 | float getSpeed() 获取速度(如果可用),以米/秒为单位。 |
8 | boolean hasAccuracy() 如果此位置具有准确性,则为 True。 |
9 | boolean hasAltitude() 如果此位置具有海拔高度,则为 True。 |
10 | boolean hasBearing() 如果此位置具有方位角,则为 True。 |
11 | boolean hasSpeed() 如果此位置具有速度,则为 True。 |
12 | void reset() 清除位置的内容。 |
13 | void setAccuracy(float accuracy) 设置此位置的估计精度,米。 |
14 | void setAltitude(double altitude) 设置海拔高度,以海平面以上米为单位。 |
15 | void setBearing(float bearing) 设置方位角,以度为单位。 |
16 | void setLatitude(double latitude) 设置纬度,单位为度。 |
17 | void setLongitude(double longitude) 设置经度,单位为度。 |
18 | void setSpeed(float speed) 设置地面上的速度(米/秒)。 |
19 | String toString() 返回一个字符串,该字符串包含此对象的简明、可读的描述。 |
获取当前位置
要获取当前位置,请创建一个 LocationClient
对象的位置客户端,使用 connect()
方法将其连接到LocationServices
,然后调用其 getLastLocation()
方法。此方法以 location
对象的形式返回最近的位置,该对象包含纬度和经度坐标以及如上所述的其他信息。要在活动中具有基于位置的功能,您必须实现两个接口:
- GooglePlayServicesClient.ConnectionCallbacks
- GooglePlayServicesClient.OnConnectionFailedListener
这些接口提供了以下重要的回调方法,您需要在活动类中实现这些方法:
编号 | 回调方法 & 描述 |
---|---|
1 | abstract void onConnected(Bundle connectionHint) 当位置服务成功连接到位置客户端时,将调用此回调方法您将使用 connect() 方法连接到位置客户端 |
2 | abstract void onDisconnected() 当客户端断开连接时调用此回调方法您将使用 disconnect() 方法断开与位置客户端的连接。 |
3 | abstract void onConnectionFailed(ConnectionResult result) 当将客户端连接到服务时发生错误时,将调用此回调方法。 |
onCreate()
方法中创建位置客户端,然后在 onStart()
中将其连接,以便位置服务在您的活动完全可见时维护当前位置。您应该使用 onStop()
方法断开客户端连接,以便在看不到您的应用程序时,位置服务不会维护当前位置。这样可以最大程度地节省电池电量。获取更新的位置
如果您愿意进行位置更新,那么除了上述接口之外,您还需要实现 LocationListener
接口。此接口提供以下回调方法,您需要在活动类中实现:
编号 | 回调方法 & 描述 |
---|---|
1 | abstract void onLocationChanged(Location location) 此回调方法用于在位置发生更改时从 LocationClient 接收通知。 |
位置服务质量
LocationRequest
对象用于向 LocationClient
请求位置更新的服务质量(QoS)。您可以使用以下有用的 setter 方法来处理 QoS。您可以在 Android 官方文档中查看等效的 getter 方法。
编号 | 方法 & 描述 |
---|---|
1 | setExpirationDuration(long millis) 设置此请求的持续时间(毫秒)。 |
2 | setExpirationTime(long millis) 设置启动后的请求过期时间(毫秒)。 |
3 | setFastestInterval(long millis) 显式设置位置更新的最快间隔(毫秒)。 |
4 | setInterval(long millis) 设置活动位置更新的所需间隔(毫秒)。 |
5 | setNumUpdates(int numUpdates) 设置位置更新的次数。 |
6 | setPriority(int priority) 设置请求的优先级。 |
例如,如果您的应用程序想要高精度定位,它应该创建一个定位请求,setPriority(int)
设置为 PRIORITY_high_accuracy,setInterval(long)
设置为 5 秒。您还可以使用更大的间隔和/或其他优先级,如 PRIORITY_LOW_POWER
来请求 “城市” 级别的精度,或 PRIORITIY_BALANCED_POWER_accuracy
来请求 "街道" 级别的精度。
onPause()
),或至少将请求切换到更大的间隔和更低的质量以节省功耗。显示位置地址
一旦有了 Location
对象,就可以使用 Geocoder.getFromLocation()
方法获取给定纬度和经度的地址。这个方法是同步的,可能需要很长时间才能完成其工作,因此您应该使用 AsyncTask
类的 doInBackground()
方法调用该方法。
必须对 AsyncTask
进行子类化才能使用,子类将重写 doInBackground(Params…)
方法以在后台执行任务,并在后台计算完成后在UI线程上调用onPostExecute(Result)
方法以显示结果。AyncTask
中还有一个更重要的方法是 execute(Params…Params)
,该方法使用指定的参数执行任务。
实例
下面的示例向您展示了如何在应用程序中使用位置服务来获取当前位置及其等效地址等。
创建 Android 应用程序
步骤 | 描述 |
---|---|
1 | 您将使用 Android studio IDE 创建一个 Android 应用程序,并在 com.example.demo.myapplication 包下将其命名为 myapplication。 |
2 | 添加 src/GPSTracker.java 文件以及必要代码。 |
3 | 修改 src/MainActivity.ava f文件并添加所需的代码,如下所示,以获取当前位置及其等效地址。 |
4 | 修改布局 XML 文件 res/layout/activity_main.xml 添加包括 3 个按钮和两个文本视图的所有 GUI 组件以显示位置/地址。 |
5 | 修改 res/values/strings.xml 定义必要常量。 |
6 | 修改 AndroidManifest.xml 文件。 |
7 | 运行应用程序以启动 Android 模拟器并验证应用程序中所做更改的结果。 |
下面是修改的主活动文件 MainActivity.java 的内容:
package com.example.demo.myapplication;
import android.Manifest;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.test.mock.MockPackageManager;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
Button btnShowLocation;
private static final int REQUEST_CODE_PERMISSION = 2;
String mPermission = Manifest.permission.ACCESS_FINE_LOCATION;
// GPSTracker class
GPSTracker gps;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
if (ActivityCompat.checkSelfPermission(this, mPermission)
!= MockPackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{mPermission},
REQUEST_CODE_PERMISSION);
// 如果用户不允许上述任何权限,则此条件将每次都执行,否则你的其他部分代码将起作用
}
} catch (Exception e) {
e.printStackTrace();
}
btnShowLocation = (Button) findViewById(R.id.button);
// 显示位置按钮单击事件
btnShowLocation.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
// 创建类对象
gps = new GPSTracker(MainActivity.this);
// 检查 GPS 是否可用
if(gps.canGetLocation()){
double latitude = gps.getLatitude();
double longitude = gps.getLongitude();
// \n 是换行
Toast.makeText(getApplicationContext(), "Your Location is - \nLat: "
+ latitude + "\nLong: " + longitude, Toast.LENGTH_LONG).show();
}else{
// 无法获取定位
// GPS 或网络未启用
// 要求用户在设置中启用 GPS/网络
gps.showSettingsAlert();
}
}
});
}
}
下面是修改的主活动文件 GPSTracker.java 的内容:
package com.example.demo.myapplication;
import android.app.AlertDialog;
import android.app.Service;
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.os.IBinder;
import android.provider.Settings;
import android.util.Log;
public class GPSTracker extends Service implements LocationListener {
private final Context mContext;
// GPS 状态标识
boolean isGPSEnabled = false;
// 网络状态标识
boolean isNetworkEnabled = false;
// 是否能获取位置标识
boolean canGetLocation = false;
Location location; // 位置
double latitude; // 纬度
double longitude; // 经度
// 更改更新的最小距离(米)
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
// 更新之间的最短时间(毫秒)
private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute
// 声明位置管理器
protected LocationManager locationManager;
public GPSTracker(Context context) {
this.mContext = context;
getLocation();
}
public Location getLocation() {
try {
locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
// 获取 GPS 状态
isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
// 获取网络状态
isNetworkEnabled = locationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!isGPSEnabled && !isNetworkEnabled) {
// 网络不可用
} else {
this.canGetLocation = true;
// 首先通过网络基站获取位置
if (isNetworkEnabled) {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
Log.d("Network", "Network");
if (locationManager != null) {
location = locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
// 如果启用 GPS,则使用 GPS 服务获取经纬度
if (isGPSEnabled) {
if (location == null) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
Log.d("GPS Enabled", "GPS Enabled");
if (locationManager != null) {
location = locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return location;
}
/**
* 停止使用 GPS 监听器
* 调用此函数将停止在应用程序中使用 GPS
* */
public void stopUsingGPS(){
if(locationManager != null){
locationManager.removeUpdates(GPSTracker.this);
}
}
/**
* 获取纬度的函数
* */
public double getLatitude(){
if(location != null){
latitude = location.getLatitude();
}
// 返回纬度
return latitude;
}
/**
* 获取纬度的经度
* */
public double getLongitude(){
if(location != null){
longitude = location.getLongitude();
}
// 返回经度
return longitude;
}
/**
* 检查 GPS/wifi 是否可用的函数
* @return boolean
* */
public boolean canGetLocation() {
return this.canGetLocation;
}
/**
* 用于显示设置警报对话框的功能
* 按下设置按钮将启动设置选项
* */
public void showSettingsAlert(){
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
// 设置对话框标题
alertDialog.setTitle("GPS is settings");
// 设置对话框信息
alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
// 按下设置按钮时
alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// 按下取消按钮时
alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// 弹出提示
alertDialog.show();
}
@Override
public void onLocationChanged(Location location) {
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
}
下面是 res/layout/activity_main.xml 文件的内容:
<?xml version = "1.0" encoding = "utf-8"?>
<LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:orientation = "vertical" >
<Button
android:id = "@+id/button"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "getlocation"/>
</LinearLayout>
下面是 res/values/strings.xml 文件,定义了两个常量:
<?xml version = "1.0" encoding = "utf-8"?>
<resources>
<string name = "app_name">myapplication</string>
</resources>
下面是默认的 AndroidManifest.xml 文件:
<?xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
package = "com.example.demo.myapplication">
<uses-permission android:name = "android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name = "android.permission.INTERNET" />
<application
android:allowBackup = "true"
android:icon = "@mipmap/ic_launcher"
android:label = "@string/app_name"
android:supportsRtl = "true"
android:theme = "@style/AppTheme">
<activity android:name = ".MainActivity">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
让我们尝试运行 My Application 应用程序。假设您已经将实际的 Android 移动设备与计算机连接。要从 Android Studio 运行应用程序,请打开项目的 activity 文件之一,然后单击工具栏上的运行 Eclipse 图标。在启动应用程序之前,Android studio 安装程序将显示以下窗口,以选择要运行 Android 应用程序的选项。
现在要查看位置,请选择 Get Location 按钮,该按钮将显示如下位置信息: