【Android】18.1 利用安卓内置的定位服务实现位置跟踪
分类:C#、Android、VS2015;
创建日期:2016-03-04
一、安卓内置的定位服务简介
通常将各种不同的定位技术称为位置服务或定位服务。这种服务是通过电信运营商的无线电通信网络(如GSM网、CSMA网等)或外部定位方式(如GPS)来实现的。
Android提供了对移动数据(cell tower,也叫蜂窝发射塔)、无线网络(Wi-fi)、全球定位系统(GPS)等多种定位技术的访问。
在安卓系统中,无论你选择哪种定位服务API获取位置数据,这些基本概念都是相同的。
Android平台提供的定位服务API主要包括以下几种。
1、LocationManager
定位管理器。用于获取和管理用户当前的位置,追踪设备的移动路线,或者设定敏感区域(在进入或离开敏感区域时设备会发出警报)。
2、Location Providers
LocationProviders:定位提供程序。提供定位功能的组件集合,集合中的每种组件都以不同的技术提供设备的当前位置,这些提供程序的区别在于定位的精度、速度和成本等方面。
Android收集位置数据是靠硬件来实现的,所用的硬件取决于位置提供程序的类型。
Android提供了三种类型的位置提供程序:
- GPS Provider – 该方式使用 GPS 和辅助GPS (aGPS) 返回 GPS 数据收集的蜂窝塔的组合。全球定位系统(GPS)最耗电,但它给出的位置也最准确,适合在户外使用。
- Network Provider – 该方式提供 WiFi 和蜂窝数据的组合,包括辅助GPS收集的数据。它比GPS Privider的耗电量少一些,但返回的位置精度没有GPS高。
- Passive Provider – 该方式由其他应用程序或服务来生成位置数据,由于不需要持续的位置更新,因此这种方式的耗电量最少,但该方式得到的位置数据不太可靠(可信性不高)。
另外,位置提供程序并不总是可用的。例如,你编写的应用程序希望使用GPS定位,但是手机的GPS可能处于关闭状态或者手机根本就没有获取GPS数据的硬件,这种情况下,实际上是无法使用GPS Privider的。
如果所要求的位置提供程序不可用,该提供程序返回null。
3、Location Permissions
凡是位置感知的应用程序,都需要访问设备的硬件传感器才能接收定位数据,包括GPS、Wi-fi或移动数据(蜂窝数据)。这些数据的访问权限都是通过AndroidManifest来配置的。
一共有两个基于硬件定位服务的数据访问权限,根据应用程序要求和所选择的API,你可以选择这两种的其中之一(一般不要两个都选):
- ACCESS_FINE_LOCATION – 该权限允许应用程序访问GPS。该权限可使用GPS Provider和Passive Provider(Passive Provider需要访问由另一个应用程序或服务收集的GPS数据的权限)。另外,根据需要,也可以选择Network Provider。
- ACCESS_COARSE_LOCATION – 如果未设置ACCESS_FINE_LOCATION,利用该权限可使用Network Provider访问移动电话和WiFi。
权限设置办法:在【解决方案资源管理器】中,双击项目的【Properties】文件夹,即可弹出该项目的属性窗口,在该窗口中,设置相应的访问权限即可。
再次提醒注意:设置ACCESS_FINE_LOCATION权限意味着已经拥有对Coarse和fine这两个位置数据的访问权限。无论什么情况,应用程序都应该仅使用最小的运行权限以节省手机的耗电量。或者说,不管你的应用程序实现什么功能,都没有必要“同时”设置ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION。
4、定位服务的基本设计思路
定位服务是一种特殊类型的由Android系统管理的服务。系统服务总是与设备的硬件进行交互,并总是处于运行状态。
在应用程序中,可利用ILocationListener与LocationManager类来访问定位服务。具体来说,要利用Android Location Service获取用户的位置,需要完成下面的步骤:
- 获取对LocationManager类的引用。
- 使用LocationManager请求提供程序更新位置
- 当位置发生改变时,在实现的ILocationListener接口中处理位置信息。
- 当应用程序转入后台时停止位置更新。
要在应用程序中获取位置更新的数据,需要先获取对LocationManager的引用,然后再调用RequestLocationUpdates()方法获取位置服务提供的更新数据。
二、利用Android内置的定位服务API实现定位跟踪
Android的定位服务是安卓系统提供的标准API。位置数据是通过硬件传感器(hardware sensors)和系统服务(system Service)来共同收集的。
1、获取LocationManager实例
LocationManager是一个特殊的类,利用它提供的方法可以与系统位置服务进行交互。下面的代码演示了如何在Activity中调用GetSystemService()方法获取对LocationManager的引用:
private LocationManager locMgr;
...
locMgr = GetSystemService(Context.LocationService) as LocationManager;
一般先将locMgr声明为字段,然后再在重写的OnCreate()方法中获取对LocationManager的引用。这样一来,就可以在Activity生命周期期间调用的不同方法中使用这个实例了。
2、请求位置更新
一旦得到了LocationManager的实例,就可以调用该实例的RequestionLocationUpdates()方法告诉它你希望开始接收位置更新的服务,包括需要什么类型的位置信息,以及希望接收位置数据的频率,更新频率用时间(单位:毫秒)和距离阈值(单位:米)等。
例如,下面的C#代码实现每2000毫秒发出一次定位请求,当位置变化超过1米时调用一次位置更新:
protected override void OnResume ()
{
base.OnResume ();
string Provider = LocationManager.GpsProvider;
if(locMgr.IsProviderEnabled(Provider))
{
locMgr.RequestLocationUpdates(Provider, 2000, 1, this);
}
else
{
Log.Info(tag, Provider + " 不可用,请检查设备的定位权限设置");
}
}
应用程序应该仅在需要时才请求位置更新,这会保留电池寿命并创建更好的用户体验。
3、获取位置信息
一旦应用程序得到了更新的数据,就可以实现 ILocationListener 接口,利用它接收来自定位服务的信息。此接口提供了用于监听位置服务和位置提供程序的方法。
下面的代码演示了如何在MainActivity中实现ILocationListener接口:
public class MainActivity : Activity, ILocationListener
{
...
public void OnProviderEnabled (string provider)
{
...
}
public void OnProviderDisabled (string provider)
{
...
}
public void OnStatusChanged (string provider, Availability status, Bundle extras)
{
...
}
public void OnLocationChanged (Android.Locations.Location location)
{
...
}
}
该接口允许我们用四个系统事件来检查提供程序的状态并获取位置信息:
- OnProviderEnabled 和 OnProviderDisabled - 通知应用程序用户启用或禁用了对应的提供程序(例如,用户可能会禁用 GPS 以节省电池寿命)。
- OnStatusChanged - 通知应用程序该提供程序的可用性以及对应的状态发生了更改(例如,用户在室内走的时候GPS可用性发生了更改)。
- OnLocationChanged - 当请求的更新位置发生变化时(什么时候请求更新与设置的位置更新参数有关),系统会自动调用OnLocationChanged方法。
下面的代码解释了如何在Activity中实现OnLocationChanged方法,并将接收的位置更新输出到屏幕上:
TextView latitude;
TextView longitude;
public void OnLocationChanged (Location location)
{
latitude.Text = "纬度(Latitude):" + location.Latitude;
longitude.Text = "经度(Longitude):" + location.Longitude;
}
4、停止位置更新
RemoveUpdates()方法告诉系统停止定位服务向应用程序发送更新。在重写的OnPause()方法中调用RemoveUpdates()的目的是为了节省电量消耗以延长电池的寿命:
protected override void OnPause ()
{
base.OnPause ();
locMgr.RemoveUpdates (this);
}
如果应用程序需要在后台获取位置更新,可创建一个自定义的服务,然后在该服务中使用定位服务。
5、GetBestProvider方法
前面介绍了如何用GPS作为位置提供程序。然而,GPS可能无法在所有情况下都可用,例如,如果该设备是在室内或者没有GPS接收硬件,此时提供程序将为null。
当GPS不可用时,如果想要应用程序仍然能继续工作,此时可以利用GetBestProvider()方法寻求已启动的最佳可用的设备而不是死板地只会去调用特定的提供程序。或者说,如果你希望实现的代码更简单,而不是自己去考虑和处理各种可能性,可利用GetBestProvider()方法让它帮你找到最佳的提供程序。
另外,还可以通过参数告诉GetBestProvider()方法对定位提供程序的要求(比如准确性和耗电量等),此时GetBestProvider()方法就会按给定的条件返回最佳的提供程序:
Criteria locationCriteria = new Criteria();
locationCriteria.Accuracy = Accuracy.Coarse;
locationCriteria.PowerRequirement = Power.Medium;
locationProvider = locMgr.GetBestProvider(locationCriteria, true);
if(locationProvider != null)
{
locMgr.RequestLocationUpdates (locationProvider, 2000, 1, this);
}
else
{
Log.Info(tag, "No location providers available");
}
注意:如果用户禁用了所有的定位服务,GetBestProvider()将返回null。
要查看此代码在实际设备上是如何工作的,一定要确保你的设备启用了GPS、Wi-fi、移动数据(蜂窝网络)。具体设置办法如下面的截图所示,其中左图是真实手机的定位模式设置(不同型号的手机设置界面可能不同),右图是Android 6.0模拟器的定位模式设置:

下面的屏幕截图演示了GetBestProvider()在真实手机上的运行效果(具体数据与你所在的位置有关):
记住:GetBestProvider()不会动态地更改提供程序,它仅在生命周期期间一次性地决定最佳的可用提供程序。
如果设置后提供程序的状态发生了变化,应用程序需要在ILocationListener接口要求实现的方法中添加额外的代码来处理它,即:在OnProviderEnabled()、OnProviderDisabled() 和 OnStatusChanged()方法中处理各种可能性。
三、示例1—安卓内置的定位服务基本用法
运行截图
在真实手机上运行该程序,才能看到实际的数据。在模拟器上,开始看到的是下面截图所示的界面,单击【启动定位服务】即可根据位置的变化更新所在位置,但是由于模拟器没有gps硬件传感器,因此显示的经纬度结果将为空。
设计步骤
1、设置权限
该例子需要下面的权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
设置办法:在【解决方案资源管理器】中双击项目的Properties文件夹,在弹出的窗口中查看是勾选了该权限,如果没勾选就勾选它。
2、添加ch1801Main.axml文件
在layout文件夹下添加该文件,模板选择【Layout】。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/myButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/latitude" />
<TextView
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/longitude" />
<TextView
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/provider" />
</LinearLayout>
3、添加ch1801MainActivity.cs文件
在SrcDemos文件夹下添加该文件,模板选择【Acivity】。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Locations;
using Android.Util; namespace MyDemos.SrcDemos
{
[Activity(Label = "【例18-1】安卓定位服务基本用法")]
public class ch1801MainActivity : Activity, ILocationListener
{
private LocationManager locMgr;
private string tag = "ch1801MainActivity";
private Button button;
private TextView latitude;
private TextView longitude;
private TextView provider;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ch1801Main);
button = FindViewById<Button>(Resource.Id.myButton);
latitude = FindViewById<TextView>(Resource.Id.latitude);
longitude = FindViewById<TextView>(Resource.Id.longitude);
provider = FindViewById<TextView>(Resource.Id.provider);
} protected override void OnStart()
{
base.OnStart();
Log.Debug(tag, "OnStart called");
} // 由于每次启动Activity时都会调用OnResume()方法,所以将请求位置更新的代码放在此方法中
protected override void OnResume()
{
base.OnResume();
Log.Debug(tag, "OnResume called");
// 初始化定位管理器
locMgr = GetSystemService(Context.LocationService) as LocationManager;
button.Click += delegate
{
button.Text = "定位服务正在运行";
var locationCriteria = new Criteria();
locationCriteria.Accuracy = Accuracy.Coarse;
locationCriteria.PowerRequirement = Power.Medium;
string locationProvider = locMgr.GetBestProvider(locationCriteria, true);
Log.Debug(tag, "开始定位更新,使用的提供程序:" + locationProvider.ToString());
LocationProvider p = locMgr.GetProvider(locationProvider);
provider.Text = "提供程序(Provider): " + locationProvider.ToString();
// 更新所需的最短时间minTime=2000毫秒,更新所需的最短移动距离minDistance=1米
locMgr.RequestLocationUpdates(locationProvider, 2000, 1, this);
};
} protected override void OnPause()
{
base.OnPause();
Log.Debug(tag, "OnPause called"); // 当应用程序转入后台时,停止发送定位更新
// RemoveUpdates takes a pending intent - here, we pass the current Activity
locMgr.RemoveUpdates(this);
Log.Debug(tag, "程序转入后台,定位更新停止。");
} protected override void OnStop()
{
base.OnStop();
Log.Debug(tag, "OnStop called");
} public void OnLocationChanged(Location location)
{
Log.Debug(tag, "位置发生了变化");
latitude.Text = "纬度(Latitude): " + location.Latitude.ToString();
longitude.Text = "经度(Longitude): " + location.Longitude.ToString();
provider.Text = "提供程序(Provider): " + location.Provider.ToString();
} public void OnProviderDisabled(string provider)
{
Log.Debug(tag, provider + " disabled by user");
} public void OnProviderEnabled(string provider)
{
Log.Debug(tag, provider + " enabled by user");
} public void OnStatusChanged(string provider, [GeneratedEnum] Availability status, Bundle extras)
{
Log.Debug(tag, provider + " availability has changed to " + status.ToString());
}
}
}
【Android】18.1 利用安卓内置的定位服务实现位置跟踪的更多相关文章
- 利用Windows内置工具winsat测试硬盘速度(SSD&机械盘对比)
利用Windows内置工具winsat测试硬盘速度(SSD&机械盘对比) 以下是红色内容是在命令行运行: C:\Users\Administrator>winsat diskWindow ...
- SQL Server利用RowNumber()内置函数与Over关键字实现通用分页存储过程(支持单表或多表结查集分页)
SQL Server利用RowNumber()内置函数与Over关键字实现通用分页存储过程,支持单表或多表结查集分页,存储过程如下: /******************/ --Author:梦在旅 ...
- 如何利用.Net内置类,解析未知复杂Json对象
如何利用.Net内置类,解析未知复杂Json对象 如果你乐意,当然可以使用强大的第三方类库Json.Net中的JObject类解析复杂Json字串 . 我不太希望引入第三方类库,所以在.Net内置类J ...
- python 练习题:请利用Python内置的hex()函数把一个整数转换成十六进制表示的字符串
# -*- coding: utf-8 -*- # 请利用Python内置的hex()函数把一个整数转换成十六进制表示的字符串 n1 = 255 n2 = 1000 print(hex(n1)) pr ...
- 利用Java内置的API开发JMX功能
一.什么是JMX JMS是一种Java规范,定义了如何管理一个软件系统(或应用程序)的规范. 对于一个简单的应用程序,该程序本身不需要被管理.但如果是开发的一个复杂系统(如一个电商平台.一个企业内部管 ...
- 利用jQuery内置的data()方法存储数据
jQuery提供了内置的data()方法,与DOM元素不同的是,它可以用来存储key/value类型的数据.数据的存储是很容易的: $('#myDiv').data('currentState', ' ...
- python学习 day12 (3月18日)----(装饰器内置函数)
读时间函数: # import time # def func(): # start_time = time.time() # 代码运行之前的时间 # print('这是一个func函数') # ti ...
- Qt给应用程序添加版本信息(对rc文件的设置,可利用QT内置变量)
作者:daodaoliang 时间:2016年7月11日16:12:09 版本:V 0.0.4 邮箱:daodaoliang@yeah.net 0. 环境说明 系统环境: win10 64位 Qt环境 ...
- 如何在Crystal框架项目中内置启动MetaQ服务?
当Crystal框架项目中需要使用消息机制,而项目规模不大.性能要求不高时,可内置启动MetaQ服务器. 分步指南 项目引入crystal-extend-metaq模块,如下: <depende ...
随机推荐
- 【CSS】瀑布流布局的两种方式:传统多列浮动和绝对定位布局
传统多列浮动 各列固定宽度,并且左浮动: 一列中的数据块为一组,列中的每个数据块依次排列即可: 更多数据加载时,需要分别插入到不同的列上: 优点: (1)布局简单,应该说没啥特别的难点: (2)不用明 ...
- PHP高级教程-安全邮件
PHP Secure E-mails 在上一节中的 PHP e-mail 脚本中,存在着一个漏洞. PHP E-mail 注入 首先,请看上一章中的 PHP 代码: <html> < ...
- php之快速入门学习-13(PHP 循环 - While 循环)
PHP 循环 - While 循环 循环执行代码块指定的次数,或者当指定的条件为真时循环执行代码块. PHP 循环 在您编写代码时,您经常需要让相同的代码块一次又一次地重复运行.我们可以在代码中使用循 ...
- 本地文件夹变远程仓库并且提交Github
//初始化本地仓库 $ git init Initialized empty Git repository in C:/Users/root/Desktop/vue-music/.git/ root@ ...
- POJ 1436 Horizontally Visible Segments (线段树·区间染色)
题意 在坐标系中有n条平行于y轴的线段 当一条线段与还有一条线段之间能够连一条平行与x轴的线不与其他线段相交 就视为它们是可见的 问有多少组三条线段两两相互可见 先把全部线段存下来 并按x ...
- jQuery中first-child与first选择器区别
1.first-child first-child为每个父级元素匹配第一个子元素,可以匹配出多个元素: 示例代码: <!DOCTYPE html> <html lang=" ...
- nexus 批量导入本地库
1.复制D:\maven\repository(本地仓库)到D:\sonatype-work\nexus\storage\central(nexus库路径) 2.Central --> upda ...
- 使用Nginx Upstream 部署 OpenERP
Openerp 6.1 使用werkzeug 作为web服务的框架,性能比之前的cherrypy 有了很大的改善.但无论是 werkzeug 还是cherrypy ,都不是专门的web服务器.通常的做 ...
- .NET 垃圾回收机制要点整理
1. .NET资源分托管资源和非托管资源,对于托管资源,.NET GC可以很好的回收无用的垃圾,而对于非托管(例如文件访问,网络访问等)需要手动清理垃圾(显式释放). 2. 非托管资源的释放,.NET ...
- 让一个表单以post的方式在window.open的窗口中打开
我们都知道window.open(url)是最基本的用法,很多人都在url里用?xx=11&yy=22的方式拼接参数,但是其实我们不愿意让使用者看到后面的参数,这时我们可以如下方法 JS如下: ...