注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接:http://developer.android.com/training/location/receive-location-updates.html


如果你的应用有导航的功能,你可能会希望可以定期获取用户的地理位置。虽然你可以通过LocationClient.getLastLocation()做到这一点,但是一个更加直接的方法是向定位服务申请定期更新。作为响应,定位服务会自动用最佳的地理位置信息(基于当前激活的可以提供位置信息的传感器,如WiFi或者GPS)更新到你的应用。

要从定位服务定期获取地理位置更新,你使用定位客户端发送一个请求。根据请求的形式,定位服务或是激活一个回调函数,并把一个Location对象传递给该函数,或是发送一个Intent,在其数据部分包含了地理位置信息。有两方面因素会影响精度和频率,一个是你的应用申请的定位权限,一个是你在请求中传递给定位服务的参数。


一). 指定应用权限

使用位置服务的应用必须请求定位权限。Android有两个定位权限:ACCESS_COARSE_LOCATION(粗定位)和ACCESS_FINE_LOCATION(精定位)。你所选择的权限决定了定位的精度。如果你只请求粗定位,位置服务所范围的地点信息大致会精确到一个城市街区。

如果请求ACCESS_FINE_LOCATION,它也暗含了ACCESS_COARSE_LOCATION的权限。

例如,要添加ACCESS_COARSE_LOCATION,将下面的代码作为<manifest>元素的子元素:

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

二). 检查Google Play服务

位置服务是Google Play服务APK的其中一部分。由于用户设备的状态时难以预料的,你应该一直在你尝试连接定位服务之前,检查APK是否已经安装。要检查APK是否安装,可以调用GooglePlayServicesUtil.isGooglePlayServicesAvailable(),它会返回一个整形的结果码,其含义可以参阅:ConnectionResult。如果你遇到了一个错误,可以调用GooglePlayServicesUtil.getErrorDialog(),来获取一个本地的对话框,引导用户执行正确地行为,之后将这一对话框显示在一个DialogFragment上。这一对话框可能允许用户解决当前的问题,此时Google Play服务会发回一个结果到你的activity中。要处理这一结果,需要覆写onActivityResult()方法。

Note:

要使你的应用可以兼容1.6及以后版本的系统,显示DialogFragment的activity必须是FragmentActivity的子类,而非Activity。使用FragmentActivity还可以允许你调用getSupportFragmentManager()方法来显示DialogFragment

由于你一直需要在你的代码多个地方检查Google Play服务,所以应该定义一个方法将检查行为进行封装,之后在每次连接尝试之前进行检查。下面的代码片段包含了检查Google Play服务所需要的代码:

public class MainActivity extends FragmentActivity {
...
// Global constants
/*
* Define a request code to send to Google Play services
* This code is returned in Activity.onActivityResult
*/
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
...
// Define a DialogFragment that displays the error dialog
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
// Default constructor. Sets the dialog field to null
public ErrorDialogFragment() {
super();
mDialog = null;
}
// Set the dialog to display
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
// Return a Dialog to the DialogFragment.
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
...
/*
* Handle results returned to the FragmentActivity
* by Google Play services
*/
@Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
...
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
/*
* If the result code is Activity.RESULT_OK, try
* to connect again
*/
switch (resultCode) {
case Activity.RESULT_OK :
/*
* Try the request again
*/
...
break;
}
...
}
...
}
...
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d("Location Updates",
"Google Play services is available.");
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Location Updates");
}
}
}
...
}

在后续章节的代码片段中,都会调用这一方法来验证是否可获取Google Play服务。


三). 定义位置服务回调函数

在你创建定位客户端之前,实现定位服务的接口,以和你的应用进行交互:

ConnectionCallbacks

指定当定位连接上或者没有连接上时,定位服务调用的方法。

OnConnectionFailedListener

指定当尝试连接到定位客户端时,如果出现了错误,定位服务调用的方法。这一方法使用之前定义的showErrorDialog方法来显示一个错误对话框,它尝试使用Google Play服务来解决这一问题。

下面的样例代码展示了如何指定接口和定义相关的函数:

public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
...
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
@Override
public void onConnected(Bundle dataBundle) {
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
}
...
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
@Override
public void onDisconnected() {
// Display the connection status
Toast.makeText(this, "Disconnected. Please re-connect.",
Toast.LENGTH_SHORT).show();
}
...
/*
* Called by Location Services if the attempt to
* Location Services fails.
*/
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
/*
* If no resolution is available, display a dialog to the
* user with the error.
*/
showErrorDialog(connectionResult.getErrorCode());
}
}
...
}

定义地理位置更新回调函数

定位服务或是以一个Intent的形式,或者以一个参数的形式将为之更新传递给一个你定义的回调函数。这节课将会讲解如何使用一个回调函数来获取更新,课程中使用的代码基本可以用于任何应用场景。如果你想要以一个Intent的形式接收位置更新,可以阅读:Recognizing the User's Current Activity。它提供了类似的可以参考的模板。

位置服务所调用的将为之更新发送给你的应用的回调函数是在LocationListener接口的onLocationChanged()方法中指定的。传入的参数是一个Location对象,包含了地点的经纬度。下面的代码片段展示了如何指定接口和定义方法:

public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
// Define the callback method that receives location updates
@Override
public void onLocationChanged(Location location) {
// Report to the UI that the location was updated
String msg = "Updated Location: " +
Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
...
}

现在你已经有了回调函数,你可以配置位置更新的请求了。首先第一步是指定控制更新的参数。


四). 指定更新参数

定位服务允许你控制更新之间的时间间隔以及你期望的位置精确度,通过设置LocationRequest对象中的值,再将这一对象作为你的请求的一部分发出以开始更新。

首先,设置下列间隔参数:

更新间隔:

通过LocationRequest.setInterval()设置。这一方法以毫秒为单位设置你的应用接收更新的事件间隔。如果没有其它应用从定位服务接收更新,那么你的应用将会以这一频率接收更新。

最快更新间隔:

通过LocationRequest.setFastestInterval()设置。这一方法设置的是你的应用能处理更新的最快间隔时间,以毫秒为单位。你需要设置这个频率是因为其它应用也会影响位置更新非频率。定位服务会以所有应用通过LocationRequest.setInterval()设置的最快的间隔时间来发送更新。如果这一频率比你的应用能够处理的频率要快,那么你可能会遇到UI闪烁或数据溢出等问题。为了避免这一情况发生,应该调用LocationRequest.setFastestInterval()这一方法设置更新频率的最高限额。

调用LocationRequest.setFastestInterval()方法还可以节省电量。当你通过LocationRequest.setInterval()请求了一个更新间隔后,又用LocationRequest.setFastestInterval()请求了一个最大速率后,你的应用会以正常速率进行更新。如果其它应用使用了一个更快的更新速率,那么你的更新频率也会加快。如果没有其它应用申请了更快的更新速率,那么你的应用会以LocationRequest.setInterval()中所设置的速率进行更新。

接下来,设置精度参数。在一个前台应用程序中,你需要以高频率更新地理位置,所以使用LocationRequest.PRIORITY_HIGH_ACCURACY设置精度。

下面的代码片段展示课如何设置更新间隔和精度:

public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
// Global constants
...
// Milliseconds per second
private static final int MILLISECONDS_PER_SECOND = 1000;
// Update frequency in seconds
public static final int UPDATE_INTERVAL_IN_SECONDS = 5;
// Update frequency in milliseconds
private static final long UPDATE_INTERVAL =
MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
// The fastest update frequency, in seconds
private static final int FASTEST_INTERVAL_IN_SECONDS = 1;
// A fast frequency ceiling in milliseconds
private static final long FASTEST_INTERVAL =
MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
...
// Define an object that holds accuracy and frequency parameters
LocationRequest mLocationRequest;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(
LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
mLocationRequest.setInterval(UPDATE_INTERVAL);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
...
}
...
}

Note:

如果你的应用要访问网络或者在接收到更新后需要做其它长期的任务,那么应该调整更新频率到一个比较慢的值。这可以避免你的应用接收到太多它来不及处理的更新数据。一旦长期处理的任务结束了,可以再通过设置最快更新频率到一个较快的值。


五). 开始位置更新

要发送位置更新请求,在onCreate()创建一个定位客户端,之后连接它,并通过requestLocationUpdates()发起请求。因为你的客户端必须连接以后你的应用才能收到更新,所以你应该在onStart()方法中连接到客户端。这能保证当你的应用可见时,你都能获取一个已连接的有效的客户端。因为你需要在发出请求前先进行连接,所以在ConnectionCallbacks.onConnected()发出更新请求。

另外要记住用户可能会有各种各样的原因希望关闭位置更新。你应该为用户提供一个这样做的方法,并且你应该保证当更新关闭了之后,你不会在onStart()中启动更新。为了记录用户的设置,在onPause()方法中保存应用的SharedPreferences,并在onResume()方法中获取它。

下面的代码片段展示了如何在onCreate()方法中设置客户端,以及如何在onStart()方法中连接并发出更新请求:

public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
// Global variables
...
LocationClient mLocationClient;
boolean mUpdatesRequested;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Open the shared preferences
mPrefs = getSharedPreferences("SharedPreferences",
Context.MODE_PRIVATE);
// Get a SharedPreferences editor
mEditor = mPrefs.edit();
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
// Start with updates turned off
mUpdatesRequested = false;
...
}
...
@Override
protected void onPause() {
// Save the current setting for updates
mEditor.putBoolean("KEY_UPDATES_ON", mUpdatesRequested);
mEditor.commit();
super.onPause();
}
...
@Override
protected void onStart() {
...
mLocationClient.connect();
}
...
@Override
protected void onResume() {
/*
* Get any previous setting for location updates
* Gets "false" if an error occurs
*/
if (mPrefs.contains("KEY_UPDATES_ON")) {
mUpdatesRequested =
mPrefs.getBoolean("KEY_UPDATES_ON", false); // Otherwise, turn off location updates
} else {
mEditor.putBoolean("KEY_UPDATES_ON", false);
mEditor.commit();
}
}
...
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
@Override
public void onConnected(Bundle dataBundle) {
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
// If already requested, start periodic updates
if (mUpdatesRequested) {
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}
}
...
}

更多关于保存配置信息的知识,可以查看:Saving Key-Value Sets


六). 停止位置更新

要停止位置更新,在onPause()方法中保存更新标识的状态,并在onStop()方法中通过调用removeLocationUpdates(LocationListener)来停止更新,例如:

public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
/*
* Called when the Activity is no longer visible at all.
* Stop updates and disconnect.
*/
@Override
protected void onStop() {
// If the client is connected
if (mLocationClient.isConnected()) {
/*
* Remove location updates for a listener.
* The current Activity is the listener, so
* the argument is "this".
*/
removeLocationUpdates(this);
}
/*
* After disconnect() is called, the client is
* considered "dead".
*/
mLocationClient.disconnect();
super.onStop();
}
...
}

现在你已经有了请求并接收定期位置更新的基本应用框架。你可以将这节课中所讲的东西结合到导航,行为识别,反地址解析等等场景中。

下一节课中,我们将会讲解如何使用当前地点显示现在的街道地址。

【Android Developers Training】 104. 接受地点更新的更多相关文章

  1. 【Android Developers Training】 102. 序言:让你的应用获知地点

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  2. 【Android Developers Training】 103. 查询当前地点

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  3. 【Android Developers Training】 84. 将定期更新的影响最小化

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  4. 【Android Developers Training】 4. 启动另一个Activity

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  5. 【Android Developers Training】 108. 使用模拟定位进行测试

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  6. 【Android Developers Training】 107. 认知用户当前的行为

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. 【Android Developers Training】 106. 创建并检测地理围栏

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  8. 【Android Developers Training】 105. 显示一个位置地址

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  9. 【Android Developers Training】 100. 使用Intent修改联系人数据

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

随机推荐

  1. vim - manual -个人笔记

    ##vim配置 ###normal > 输入命令:w 写入保存 > > 粘贴 :p(向下粘贴) P(大写向上粘贴) > > 复制 :yy 复制一行 > > 删 ...

  2. Android分享功能实现

    通过系统分享组件实现分享功能 Intent.createChooser() 方法用来弹出系统分享列表. createChooser(Intent target, CharSequence title, ...

  3. git编译安装与常见问题解决

    1. 先去官网下载一个安装包 ,假设目录/APP/ido   2. cd /APP/ido   3. tar -zxvf git-2.7.2.tar.gz   4. 安装依赖 yum -y insta ...

  4. Vuejs实例-01使用vue-cli脚手架搭建Vue.js项目

    [TOC] 1. 前言 vue-cli 一个简单的构建Vue.js项目的命令行界面 整体过程: $ npm install -g vue-cli $ vue init webpack vue-admi ...

  5. gulp环境搭建,gulp入门教程

    gulp常用地址: gulp官方网址:http://gulpjs.com gulp插件地址:http://gulpjs.com/plugins gulp 官方API:https://github.co ...

  6. UILabel图文混排显示图片和文字

    //传入文字 自动图片放在左边文字紧接着后排排布 -(void)setAttrDetailLabelWithTitle:(NSString *)title { NSMutableAttributedS ...

  7. 多线程编程-- part 3 多线程同步->synchronized关键字

    多线程同时访问一个资源,可以会产生不可预料的结果,所以为这个资源加锁,访问资源的第一个线程为其加锁后,其他线程便不能在使用那个资源,直到锁被解除. 举个例子: 存款1000元,能取出800的时候我就取 ...

  8. 关机和重启Linux命令

    常用命令: shoutdown -h 10  十分钟后关机 shoutdown -r  10  十分钟重启 shoutdow -h now 立刻关机 shoutdow -r now 立刻重启 不安全的 ...

  9. Python教程(2.3)——运算符和类型转换

    Python里有很多运算符(operator),这节就让我们来详细学一学. 注意:本文没有特别说明的地方,只考虑bool.int.float三种类型.例如"两边操作数类型相同时,得到的结果为 ...

  10. Kotlin尝试之一:写代码前的准备

    Kotlin是一种静态类型的编程语言,可在Java虚拟机上运行,也可以编译为JavaScript源代码. 其主要发展来自位于俄罗斯圣彼得堡的JetBrains程序员团队. 虽然语法与Java不兼容,但 ...