热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

八、使用谷歌的定位服务

八、使用谷歌的定位服务在前一章中,我们构建了我们的基于位置的警报

八、使用谷歌的定位服务

在前一章中,我们构建了我们的基于位置的警报 ( LBA )应用,以包括谷歌地图,添加了标记和位置的定制,并且还构建了用于接收用户对警报的输入的用户界面。

我们现在将专注于谷歌位置应用编程接口与我们的应用的集成,并接收用户位置的更新。用户输入的感兴趣的位置将被保存,并与接收到的位置更新进行比较,以便当用户到达感兴趣的区域时触发警报。

谷歌提供了各种方法来访问和识别用户的位置。谷歌位置应用编程接口提供用户最后已知位置的信息,显示位置地址,接收位置变化的持续更新,等等。开发人员可以添加地理围栏(地理区域周围的围栏),并且每当用户通过地理围栏时,都可以生成警报。

在本章中,我们将学习如何:


  • 使用谷歌定位应用编程接口

  • 接收用户当前位置的更新

  • 利用用户共享的偏好来保持用户感兴趣的位置

  • 当用户到达感兴趣的位置时,匹配并显示警报

本章的主要重点是介绍和解释我们的应用中位置的概念和用法。考虑到这个目标,这些概念是通过应用在前台运行时接收位置更新来解释的。所需权限的处理也以更简单的方式进行。

集成共享首选项

我们应用的用户将输入他们想要触发警报的期望位置。用户输入位置的LatLng,为了我们将其与用户当前所在的位置进行比较,我们需要将他们输入的详细信息存储为所需的位置。

共享首选项是基于文件的存储,包含键值对,并提供了一种更简单的读写方式。共享首选项文件由安卓框架管理,文件可以是私有的,也可以是共享的。

让我们首先将共享首选项集成到我们的代码中,并保存用户在用户界面屏幕中为警报输入的纬度和经度。

共享首选项为我们提供了将数据保存为键值对的选项。虽然我们可以使用通用的共享首选项文件,但最好为我们的应用准备一个特定的共享首选项文件。

我们需要为我们的应用使用的共享首选项文件定义一个字符串。导航到 app | src | main | RES | values | strings . XML。让我们添加一个新字符串PREFS_NAME,并将其命名为LocationAlarmFile:


name="app_name">LocationAlarm
name="title_activity_maps">Map
name="Settings">Settings
name="PREFS_NAME">LocationAlarmFile

我们将在我们的SettingsActivity类中添加以下代码来捕获用户输入,并将其保存在共享首选项文件中。共享首选项文件是通过引用资源文件中的字符串PREFS_NAME打开的,该文件是用MODE_PRIVATE打开的,这表明该文件仅适用于我们的应用。

文件可用后,我们打开编辑器,将用户输入的纬度和经度作为字符串共享,使用putString作为键值对:

val sharedPref = this?.getSharedPreferences(getString(R.string.PREFS_NAME),Context.MODE_PRIVATE) ?: return with(sharedPref.edit()){ putString("userLat", Lat?.text.toString())
putString("userLang",Lang?.text.toString())
commit()

要从共享首选项读取和显示:

val sharedPref =
this?.getSharedPreferences(getString(R.string.PREFS_NAME),
Context.MODE_PRIVATE) ?: return AlarmLat =
java.lang.Double.parseDouble(sharedPref.getString("userLat",
"13.07975"))
AlarmLong =
java.lang.Double.parseDouble(sharedPref.getString("userLang",
"80.1798347"))

用户会收到警报:

用户输入的纬度从共享首选项中存储和读取并显示:

用户输入的经度也从共享首选项中读取并显示:

添加权限

谷歌游戏服务提供基于位置的服务,我们的应用可以集成和使用这些服务。添加位置服务并使用它们需要从用户处识别和获取位置更新的权限。

要使用播放服务中的谷歌定位服务,我们需要在build.gradle文件中包含play-services-location:

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

It is important to include only the specific feature required for the app from Google Play Services. For example, here we require location services, so we need to specify the services for the location. Including all Google Play Services will make the app size huge; ask for permissions that are not really required for the app.

我们还需要在我们的AndroidManifest.xml文件中添加访问精细位置的权限。这使我们能够从网络提供商和全球定位系统提供商处获得位置详细信息:


在运行时,我们需要检查设备是否启用了位置;如果没有,我们将显示一条消息,请求用户启用该位置并授予该应用的权限。

checkLocation布尔函数确定设备是否为我们启用了位置:

private fun checkLocation(): Boolean {
if(!isLocationEnabled())
Toast.makeText(this,"Please enable Location and grant permission for this app for Location",Toast.LENGTH_LONG).show()
return isLocationEnabled();
}
private fun isLocationEnabled(): Boolean {
locationManager = getSystemService(Context.LOCATION_SERVICE) as
LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
}

位置应用编程接口的集成

我们将把位置应用编程接口集成到我们的应用中,以接收位置更新。位置 API 的集成涉及到代码中的一些变化。让我们详细讨论一下。

类和变量

谷歌位置应用编程接口的集成需要GoogleAPIClientConnectionCallbacks的实现,以及MapsActivity连接失败的监听器。让我们继续对MapsActivity进行更改。之前我们有MapsActivity扩展AppCompatActivity实现OnMapReadyCallback接口。现在,由于我们需要使用位置 API,我们还必须实现GoogleAPIClientConnectionCallbacksonConnectionFailedListener,如下所示:

class MapsActivity : AppCompatActivity(), OnMapReadyCallback ,GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener {

我们声明GoogleMap所需的变量和其他变量,以存储从用户和位置 API 接收的纬度和经度:

private lateinit var mMap: GoogleMap
private var newLat: Double? = null
private var newLang: Double? = null
private var chennai: LatLng? = null
private var AlarmLat: Double? = null
private var AlarmLong: Double? = null
private var UserLat: Double? = null
private var UserLong: Double? = null
//location variablesprivate val TAG = "MapsActivity" private lateinit var mGoogleApiClient: GoogleApiClient
private var mLocationManager: LocationManager? = null
lateinit var mLocation: Location
private var mLocationRequest: LocationRequest? = null

我们声明UPDATE_INTERVAL,我们希望从位置 API 接收更新的时间间隔,以及FASTEST_INTERVAL,我们的应用可以处理更新的速率。我们还声明了LocationManager变量:

private val UPDATE_INTERVAL = 10000.toLong() // 10 seconds rate at
// which we would like to receive the updates
private val FASTEST_INTERVAL: LOng= 5000 // 5 seconds - rate at
// which app can handle the update lateinit var locationManager: LocationManager

onCreate功能中,我们为用户界面设置内容视图,并确保GoogleApiClient被实例化。我们还要求用户启用如下位置:

onCreate():

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.*activity_maps*)
// Obtain the SupportMapFragment and get notified when the map
is ready to be used. val mapFragment = *supportFragmentManager
* .findFragmentById(R.id.*map*) as SupportMapFragment
mapFragment.getMapAsync(this)
mGoogleApiClient = GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build()
mLocatiOnManager=
this.getSystemService(Context.LOCATION_SERVICE) as
LocationManager
checkLocation()
}


谷歌应用编程接口客户端

声明、初始化和管理谷歌应用编程接口客户端的连接选项将与安卓应用的生命周期事件一起处理。一旦建立连接,我们还需要获取位置更新。

onStart方法中,我们检查mGoogleAPIClient实例是否不为空,并请求启动连接:

override fun onStart() {
super.onStart();
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
}

onStop方法中,我们检查mGoogleAPIClient实例是否连接,如果连接,我们称之为disconnect方法:

override fun onStop() {
super.onStop();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}

如果出现问题,连接被暂停,我们通过onConnectionSuspended方法请求重新连接:

override fun onConnectionSuspended(p0: Int) {
Log.i(TAG, "Connection Suspended");
mGoogleApiClient.connect();
}

如果谷歌定位应用编程接口无法建立连接,我们会通过获取错误代码来记录原因:

override fun onConnectionFailed(connectionResult:
ConnectionResult) {
Log.i(TAG, "Connection failed. Error: " +
connectionResult.getErrorCode());
}

onConnected方法中,我们首先检查ACCESS _FINE_LOCATION的许可,并且ACCESS_COARSE_LOCATION确实存在于清单文件中。

一旦我们确保权限被授予,我们就调用startLocationUpdates()方法:

override fun onConnected(p0: Bundle?) {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
return;
}
startLocationUpdates();

fusedLocationProviderClient提供当前位置的详细信息,并将其分配给mLocation变量:

var fusedLocationProviderClient :
FusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(this);
fusedLocationProviderClient .getLastLocation()
.addOnSuccessListener(this, OnSuccessListener<Location> {
location ->
if (location != null) {
mLocation = location;
} }) }

startLocationUpdates创建LocationRequest实例,并提供我们为更新设置的参数。我们也致电FusedLocationAPI并请求位置更新:

protected fun 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) {
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}

onLocationChanged方法是我们获取用户当前位置细节的重要方法。我们还从共享首选项中读取用户为警报输入的纬度和经度。一旦我们得到两组细节,我们就调用CheckAlarmLocation方法,该方法匹配纬度/经度,并在用户到达感兴趣区域时提醒用户:

override fun onLocationChanged(location: Location) {
val sharedPref =
this?.getSharedPreferences(getString(R.string.*PREFS_NAME*),
Context.*MODE_PRIVATE*)
?: return
AlarmLat =
java.lang.Double.parseDouble(sharedPref.getString("userLat",
"13.07975"))
AlarmLong =
java.lang.Double.parseDouble(sharedPref.getString("userLang",
"80.1798347"))
UserLat = location.latitude
UserLong = location.longitude
val AlarmLat1 = AlarmLat val AlarmLong1 = AlarmLong
val UserLat1 = UserLat
val UserLong1 = UserLong
if(AlarmLat1 != null && AlarmLong1 != null && UserLat1 != null
&& UserLong1 != null){
checkAlarmLocation(AlarmLat1,AlarmLong1,UserLat1,UserLong1)
}
}

匹配位置

startLocationUpdates方法按照我们设置的时间间隔连续提供用户当前的经纬度。我们需要使用获得的经纬度信息,并需要将其与用户为警报输入的经纬度进行比较。

当用户进入感兴趣的位置时,我们会显示一条警告消息,提示用户已到达设置了警报的区域:

fun checkAlarmLocation(AlarmLat : Double, AlarmLong : Double, UserLat : Double,UserLong : Double) {
Toast.makeText(this,"Check Alarm Called" + AlarmLat + "," + AlarmLong + "," + UserLat + "," + UserLong,Toast.*LENGTH_LONG* ).show()
var LatAlarm: Double
var LongAlarm: Double
var LatUser: Double
var LongUser: Double
LatAlarm = Math.round(AlarmLat * 100.0) / 100.0;
LongAlarm = Math.round(AlarmLong * 100.0) / 100.0;
LatUser = Math.round(UserLat * 100.0) / 100.0;
LongUser = Math.round(UserLong * 100.0) / 100.0;
Toast.makeText(this,"Check Alarm Called" + LatAlarm + "," + LongAlarm + "," + LatUser + "," + LongUser,Toast.*LENGTH_LONG* ).show()
if (LatAlarm == LatUser && LongAlarm == LongUser) {
Toast.makeText(this, "User has reached the area for which
alarm has been set", Toast.LENGTH_LONG).show();
}
}

摘要

在本章中,我们继续开发基于位置的报警应用,以利用谷歌游戏服务中的谷歌位置应用接口,并利用当用户进入感兴趣的区域时提供警报的功能。

我们学习了如何使用共享首选项来保持用户输入的数据,检索相同的数据,并使用位置 API 将用户的当前位置与感兴趣的区域相匹配。


推荐阅读
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
author-avatar
mobiledu2502928311
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有