【Android Developers Training】 104. 接受地点更新
注:本文翻译自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服务。
三). 定义位置服务回调函数
在你创建定位客户端之前,实现定位服务的接口,以和你的应用进行交互:
指定当定位连接上或者没有连接上时,定位服务调用的方法。
指定当尝试连接到定位客户端时,如果出现了错误,定位服务调用的方法。这一方法使用之前定义的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. 接受地点更新的更多相关文章
- 【Android Developers Training】 102. 序言:让你的应用获知地点
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 103. 查询当前地点
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 84. 将定期更新的影响最小化
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 4. 启动另一个Activity
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 108. 使用模拟定位进行测试
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 107. 认知用户当前的行为
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 106. 创建并检测地理围栏
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 105. 显示一个位置地址
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 100. 使用Intent修改联系人数据
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
随机推荐
- Android N多窗口支持
Android N 可以同时显示多个应用窗口. 在手机上,两个应用可以在"分屏"模式中左右并排或上下并排显示.例如,用户可以 在上面窗口聊QQ,下面窗口发送短信. 如图所示,两个a ...
- 网页web前端学习技巧
1. 写js效果时一定要注意先分析好效果的行为,尽量用最简单通用性的代码.分析步骤可以是1.先把要实现的功能一步一步的写在纸上(即自然语言)2.再根据自然语言翻译成机器语言,用jquery写的代码一定 ...
- jQ实现的一个轮播图
众所周知,轮播图是被广泛的运用的. 轮播图我们在很多的网站上都可以看到,例如淘宝.京东这些网站都很常见. 下面开始我们的轮播之旅: 搭建我们的骨架: <!DOCTYPE html> < ...
- 8、单选按钮(JRadioButton)和复选框(JCheckBox)
8.单选按钮(JRadioButton)和复选框(JCheckBox) 实现一个单选按钮(或复选框),此按钮项可被选择或取消选择,并显示其状态.JRadioButton对象与ButtonGroup对象 ...
- OBS实现直播解决方案【html实现直播】
项目的需要,要整一个视频直播,但又不想在其他平台那种直播室盗链展示,那我就直接用播放器来实现rtmp流媒体服务器推流吧!没废话,走起 1.你要有一个媒体服务器,暂时用[盘古云],这个还好,算是不错的平 ...
- java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)
概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...
- angularjs里重要的route
写一段代码来解释吧! <!DOCTYPE html><html ng-app="mainApp"><head lang="en"& ...
- Elasticsearch与Solr
公司之前有个用Lucene实现的伪分布式项目,实时性很差,后期数据量逐渐增大的时候,数据同步一次需要十几小时.当时项目重构考虑到的是Solr和ES,我参与的是Solr技术的预研.因为项目实时性要求很高 ...
- Spring与Mybatis整合
一 概述 1.整合的目的 将Mapper映射器的创建任务交给Spring容器. 二 具体实现 1.创建sqlSessionFactory: <bean id="sqlSessionFa ...
- Node.js 8有哪些重要功能和修复?
欢迎大家持续关注葡萄城控件技术团队博客,更多更好的原创文章尽在这里~~ 5月30日12点,Node.js 8正式发布了,这个版本具有一系列新功能和性能改进,并且这些功能和改进将获得长期支持(LTS). ...