摘要

本案例研究讨论了怎样将地图和地理定位特性构建到 Android* 商务应用中。包含在 Google Maps* 上覆盖商店位置,以及在设备进入商店地理围栏邻近区域时借助地理围栏通知用户。

文件夹

  1. 摘要
  2. 概述
  3. 在 Google Maps 上显示商店位置
    1. Google Maps Android API v2
    2. 在应用清单中指定应用设置
    3. 加入地图 Fragment
  4. 发送地理围栏通知
    1. 注冊和取消注冊地理围栏
    2. 实施位置服务回调
    3. 实施意向服务
  5. 总结
  6. 參考文献
  7. 作者介绍

概述

在本案例研究中,我们将会把地图和地理定位功能集成到基于 Android 平板电脑的餐馆商务应用中(图 1)。 用户能够从主菜单项“位置和地理围栏”訪问地理定位功能(图 2)。



图 1 餐馆应用主界面



图 2 浮出控件菜单项

在 Google Maps 上显示商店位置

对于一款商务应用而言,显示商店在地图上的位置对用户很直观和实用(图 3)。 Google Maps Android API 可提供一种简单的方式将 Google Maps 集成至 Android 应用。

Google Maps Android API v2

Google Maps Android API v2 是 Google Play 服务 APK 的一部分。 为了创建使用 Google Maps Android API v2 的 Android 应用,须要下载并配置 Google Play 服务 SDK。获取 API 密钥并在应用的 AndroidManifest.xml 文件里加入所需的设置来对开发环境进行设置。

首先,你须要依照下面站点上的说明来设置 Google Play 服务 SDK:http://developer.android.com/google/play-services/setup.html

然后,你须要从谷歌开发者控制台(Google Developers Console)上对你的项目进行注冊并获取一个 API 密钥:https://console.developers.google.com/project

你须要在 AndroidManifest.xml 文件里加入 API
密钥。



图 3 餐馆应用在谷歌地图上显示商店的位置。

在应用清单中指定应用设置

为了使用 Google Maps Android API v2。须要将一些权限和特性指定为 <manifest> 元素的子项(代码演示样例 1)。

当中包含网络连接、外部存储和位置訪问的一些必要权限。

此外。为了使用 Google Maps Android API,须要使用 OpenGL ES 版本号 2 特性。

<uses-permission android:name=”android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<!-- The following two permissions are not required to use
Google Maps Android API v2, but are recommended. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> <uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>

代码演示样例 1。

建议在使用 Google Maps Android API 的应用上指定的权限。

包含 “ACCESS_MOCK_LOCATION” 权限(仅当须要使用模拟位置相应用进行測试时使用)

我们相同须要将在 <meta-data> 元素中获得的 Google Play 服务版本号和 API 密钥作为 <application> 元素的子项(代码演示样例 2)。

    <meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" /> <meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="copy your API Key here"/>

代码演示样例 2。

指定 Google Play 服务版本号和 API 密钥 **

加入地图 Fragment

首先,在你的 activity 布局 xml 文件里,加入一个 MapFragment 元素(代码演示样例 3)。


private static final LatLng CHANDLER = new LatLng(33.455,-112.0668);

private static final StoreLocation[] ALLRESTURANTLOCATIONS = new StoreLocation[] {
new StoreLocation(new LatLng(33.455,-112.0668), new String("Phoenix, AZ")),
new StoreLocation(new LatLng(33.5123,-111.9336), new String("SCOTTSDALE, AZ")),
new StoreLocation(new LatLng(33.3333,-111.8335), new String("Chandler, AZ")),
new StoreLocation(new LatLng(33.4296,-111.9436), new String("Tempe, AZ")),
new StoreLocation(new LatLng(33.4152,-111.8315), new String("Mesa, AZ")),
new StoreLocation(new LatLng(33.3525,-111.7896), new String("Gilbert, AZ"))
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geolocation_view); mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.storelocationmap)).getMap();
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(CHANDLER, ZOOM_LEVEL));
Drawable iconDrawable = getResources().getDrawable(R.drawable.ic_launcher);
Bitmap iconBmp = ((BitmapDrawable) iconDrawable).getBitmap();
for(int ix = 0; ix < ALLRESTURANTLOCATIONS.length; ix++) {
mMap.addMarker(new MarkerOptions()
.position(ALLRESTURANTLOCATIONS[ix].mLatLng)
.icon(BitmapDescriptorFactory.fromBitmap(iconBmp)));
}

代码演示样例 4。 在 Google Maps 上绘制商店图标 **

发送地理围栏通知

地理围栏是一个圆形区域,该区域由一点的经纬度坐标和半径决定。 Android 应用能够注冊带有 Android 位置服务的地理围栏。 Android 应用还可指定地理围栏的使用期限。

不管地理围栏何时切换,比如,当 Android 设备进入注冊的地理围栏或从当中退出时。Android 位置服务都会即时通知 Android 应用。

在我们的餐馆应用中,我们可以为每一个商店位置定义地理围栏。

当设备进入商店附近时。应用将会发送一条通知,如“您已进入最喜爱的餐馆的附近!” (图 4)。



图 4 我们依据兴趣点和半径将地理围栏定义为一个圆形范围。

注冊和取消注冊地理围栏

在 Android SDK 中。位置服务也是 Google Play 服务 APK 的一部分。位于 “Extras” 文件夹下。

如要申请地理围栏监控,首先我们须要在应用的清单文件里指定 “ACCESS_FINE_LOCATION” 权限,该操作我们已经在上一部分中完毕。

此外,我们还须要查看 Google Play 服务的可用性(代码演示样例 5 中的 checkGooglePlayServices() 方法)。

locationClient().connect() 调用与位置client成功建立连接后,位置服务将会调用
onConnected(Bundle bundle) 函数,位置client可通过该函数申请加入或删除地理围栏。

public class GeolocationActivity extends Activity implements
GooglePlayServicesClient.ConnectionCallbacks

{

private LocationClient mLocationClient; … static class StoreLocation {
public LatLng mLatLng;
public String mId;
StoreLocation(LatLng latlng, String id) {
mLatLng = latlng;
mId = id;
}
} @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geolocation_view); mLocationClient = new LocationClient(this, this, this); // Create a new broadcast receiver to receive updates from the listeners and service
mGeofenceBroadcastReceiver = new ResturantGeofenceReceiver(); // Create an intent filter for the broadcast receiver
mIntentFilter = new IntentFilter(); // Action for broadcast Intents that report successful addition of geofences
mIntentFilter.addAction(ACTION_GEOFENCES_ADDED); // Action for broadcast Intents that report successful removal of geofences
mIntentFilter.addAction(ACTION_GEOFENCES_REMOVED); // Action for broadcast Intents containing various types of geofencing errors
mIntentFilter.addAction(ACTION_GEOFENCE_ERROR); // All Location Services sample apps use this category
mIntentFilter.addCategory(CATEGORY_LOCATION_SERVICES); createGeofences(); mRegisterGeofenceButton = (Button)findViewById(R.id.geofence_switch);
mGeofenceState = CAN_START_GEOFENCE; } @Override
protected void onResume() {
super.onResume();
// Register the broadcast receiver to receive status updates
LocalBroadcastManager.getInstance(this).registerReceiver(
mGeofenceBroadcastReceiver, mIntentFilter);
} /**
* Create a Geofence list
*/
public void createGeofences() {
for(int ix=0; ix > ALLRESTURANTLOCATIONS.length; ix++) {
Geofence fence = new Geofence.Builder()
.setRequestId(ALLRESTURANTLOCATIONS[ix].mId)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
.setCircularRegion(
ALLRESTURANTLOCATIONS[ix].mLatLng.latitude, ALLRESTURANTLOCATIONS[ix].mLatLng.longitude, GEOFENCERADIUS)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build();
mGeofenceList.add(fence);
}
} // callback function when the mRegisterGeofenceButton is clicked
public void onRegisterGeofenceButtonClick(View view) {
if (mGeofenceState == CAN_REGISTER_GEOFENCE) {
registerGeofences();
mGeofenceState = GEOFENCE_REGISTERED;
mGeofenceButton.setText(R.string.unregister_geofence);
mGeofenceButton.setClickable(true);
else {
unregisterGeofences();
mGeofenceButton.setText(R.string.register_geofence);
mGeofenceButton.setClickable(true);
mGeofenceState = CAN_REGISTER_GEOFENCE;
}
} private boolean checkGooglePlayServices() {
int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (result == ConnectionResult.SUCCESS) {
return true;
}
else {
Dialog errDialog = GooglePlayServicesUtil.getErrorDialog(
result,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST); if (errorDialog != null) {
errorDialog.show();
}
}
return false;
} public void registerGeofences() { if (!checkGooglePlayServices()) { return;
}
mRequestType = REQUEST_TYPE.ADD; try {
// Try to add geofences
requestConnectToLocationClient();
} catch (UnsupportedOperationException e) {
// handle the exception
} } public void unregisterGeofences() { if (!checkGooglePlayServices()) {
return;
} // Record the type of removal
mRequestType = REQUEST_TYPE.REMOVE; // Try to make a removal request
try {
mCurrentIntent = getRequestPendingIntent());
requestConnectToLocationClient(); } catch (UnsupportedOperationException e) {
// handle the exception
}
} public void requestConnectToLocationServices () throws UnsupportedOperationException {
// If a request is not already in progress
if (!mRequestInProgress) {
mRequestInProgress = true; locationClient().connect();
}
else {
// Throw an exception and stop the request
throw new UnsupportedOperationException();
}
} /**
* Get a location client and disconnect from Location Services
*/
private void requestDisconnectToLocationServices() { // A request is no longer in progress
mRequestInProgress = false; locationClient().disconnect(); if (mRequestType == REQUEST_TYPE.REMOVE) {
mCurrentIntent.cancel();
} } /**
* returns A LocationClient object
*/
private GooglePlayServicesClient locationClient() {
if (mLocationClient == null) { mLocationClient = new LocationClient(this, this, this);
}
return mLocationClient; } /*
Called back from the 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 bundle) {
if (mRequestType == REQUEST_TYPE.ADD) {
// Create a PendingIntent for Location Services to send when a geofence transition occurs
mGeofencePendingIntent = createRequestPendingIntent(); // Send a request to add the current geofences
mLocationClient.addGeofences(mGeofenceList, mGeofencePendingIntent, this); }
else if (mRequestType == REQUEST_TYPE.REMOVE){ mLocationClient.removeGeofences(mCurrentIntent, this);
}
}

}

代码演示样例 5。

通过位置服务申请地理围栏监控 **

实施位置服务回调

位置服务申请一般是非堵塞或异步调用。 其实。在上一部分的代码演示样例 5 中,我们已经实施了这些函数中的一个:locationClient().connect() 调用和位置client建立连接后,位置服务将会调用onConnected(Bundle bundle) 函数。 代码演示样例 6 列出了我们须要实施的其它位置回调函数。

public class GeolocationActivity extends Activity implements
OnAddGeofencesResultListener,
OnRemoveGeofencesResultListener,
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
… @Override
public void onDisconnected() {
mRequestInProgress = false;
mLocationClient = null;
} /*
* Handle the result of adding the geofences
*/
@Override
public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) { // Create a broadcast Intent that notifies other components of success or failure
Intent broadcastIntent = new Intent(); // Temp storage for messages
String msg; // If adding the geocodes was successful
if (LocationStatusCodes.SUCCESS == statusCode) { // Create a message containing all the geofence IDs added.
msg = getString(R.string.add_geofences_result_success,
Arrays.toString(geofenceRequestIds)); // Create an Intent to broadcast to the app
broadcastIntent.setAction(ACTION_GEOFENCES_ADDED)
.addCategory(CATEGORY_LOCATION_SERVICES)
.putExtra(EXTRA_GEOFENCE_STATUS, msg);
// If adding the geofences failed
} else {
msg = getString(
R.string.add_geofences_result_failure,
statusCode,
Arrays.toString(geofenceRequestIds)
);
broadcastIntent.setAction(ACTION_GEOFENCE_ERROR)
.addCategory(CATEGORY_LOCATION_SERVICES)
.putExtra(EXTRA_GEOFENCE_STATUS, msg);
} LocalBroadcastManager.getInstance(this)
.sendBroadcast(broadcastIntent); // request to disconnect the location client
requestDisconnectToLocationServices();
} /*
* Implementation of OnConnectionFailedListener.onConnectionFailed
* If a connection or disconnection request fails, report the error
* connectionResult is passed in from Location Services
*/
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
mInProgress = false;
if (connectionResult.hasResolution()) { try {
connectionResult.startResolutionForResult(this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
}
catch (SendIntentException e) {
// log the error
}
}
else {
Intent errorBroadcastIntent = new Intent(ACTION_CONNECTION_ERROR);
errorBroadcastIntent.addCategory(CATEGORY_LOCATION_SERVICES)
.putExtra(EXTRA_CONNECTION_ERROR_CODE,
connectionResult.getErrorCode());
LocalBroadcastManager.getInstance(this)
.sendBroadcast(errorBroadcastIntent);
}
} @Override
public void onRemoveGeofencesByPendingIntentResult(int statusCode,
PendingIntent requestIntent) { // Create a broadcast Intent that notifies other components of success or failure
Intent broadcastIntent = new Intent(); // If removing the geofences was successful
if (statusCode == LocationStatusCodes.SUCCESS) { // Set the action and add the result message
broadcastIntent.setAction(ACTION_GEOFENCES_REMOVED);
broadcastIntent.putExtra(EXTRA_GEOFENCE_STATUS,
getString(R.string.remove_geofences_intent_success)); }
else {
// removing the geocodes failed // Set the action and add the result message
broadcastIntent.setAction(ACTION_GEOFENCE_ERROR);
broadcastIntent.putExtra(EXTRA_GEOFENCE_STATUS,
getString(R.string.remove_geofences_intent_failure,
statusCode));
}
LocalBroadcastManager.getInstance(this)
.sendBroadcast(broadcastIntent); // request to disconnect the location client
requestDisconnectToLocationServices();
} public class ResturantGeofenceReceiver extends BroadcastReceiver { @Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction(); // Intent contains information about errors in adding or removing geofences
if (TextUtils.equals(action, ACTION_GEOFENCE_ERROR)) {
// handleGeofenceError(context, intent);
}
else if (TextUtils.equals(action, ACTION_GEOFENCES_ADDED)
||
TextUtils.equals(action, ACTION_GEOFENCES_REMOVED)) {
// handleGeofenceStatus(context, intent);
}
else if (TextUtils.equals(action, ACTION_GEOFENCE_TRANSITION)) {
// handleGeofenceTransition(context, intent);
}
else {
// handle error
} }
} public PendingIntent getRequestPendingIntent() {
return createRequestPendingIntent();
} private PendingIntent createRequestPendingIntent() { if (mGeofencePendingIntent != null) { // Return the existing intent
return mGeofencePendingIntent; // If no PendingIntent exists
} else { // Create an Intent pointing to the IntentService
Intent intent = new Intent(this,
ReceiveGeofenceTransitionIntentService.class); return PendingIntent.getService(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
} @Override
public void onRemoveGeofencesByRequestIdsResult(int statusCode,
String[] geofenceRequestIds) { // it should not come here because we only remove geofences by PendingIntent
// Disconnect the location client
requestDisconnection();
}

代码演示样例 6。

实施位置服务回调 **

实施意向服务

最后,我们须要实施 IntentService 类。以处理地理围栏切换(代码演示样例 7)。

代码演示样例 7。 实施 IntentService 类以处理地理围栏切换

总结

在本文中,我们介绍了怎样将地图和地理围栏特性集成至 Android 商务应用。

这些特性可支持丰富的地理定位内容和基于强大定位功能的服务,并參照了应用中的已有案例。

參考文献

关于作者

Miao Wei 是英特尔软件及服务事业部的软件project师。

他眼下负责英特尔® 凌动™ 处理器大规模支持项目。

* 其它的名称和品牌可能是其它全部者的资产。

** 该演示样例源码依据英特尔演示样例源码许可协议公布

优化声明

英特尔的编译器针对非英特尔微处理器的优化程度可能与英特尔微处理器同样(或不同)。

这些优化包含 SSE2,SSE3 和 SSSE3 指令集以及其他优化。 对于在非英特尔制造的微处理器上进行的优化,英特尔不正确对应的可用性、功能或有效性提供担保。

此产品中依赖于处理器的优化仅适用于英特尔微处理器。 某些不是专门面向英特尔微体系结构的优化保留专供英特尔微处理器使用。 请參阅对应的产品用户和參考指南。以了解关于本通知涉及的特定指令集的很多其它信息。

通知版本号 #20110804

在 Android* 商务应用中实施地图和地理围栏特性的更多相关文章

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

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

  2. Android Studio 项目中集成百度地图SDK报Native method not found: com.baidu.platform.comjni.map.commonmemcache.JNICommonMemCache.Create:()I错误

    Android Studio 项目中集成百度地图SDK报以下错误: java.lang.UnsatisfiedLinkError: Native method not found: com.baidu ...

  3. Android学习开发中如何保持API的兼容

    Android学习开发中如何保持API的兼容: 1,采用良好的设计思路 在设计过程中,如果能按照下面的方式来进行设计,会让这个API生命更长久 面向用例的设计,收集用户建议,把自己模拟成用户,保证AP ...

  4. Android 源码中的设计模式

    最近看了一些android的源码,发现设计模式无处不在啊!感觉有点乱,于是决定要把设计模式好好梳理一下,于是有了这篇文章. 面向对象的六大原则 单一职责原则 所谓职责是指类变化的原因.如果一个类有多于 ...

  5. Android Studio下加入百度地图的使用(二)——定位服务

    上一章(http://www.cnblogs.com/jerehedu/p/4891216.html)中我们已经完成了环境的搭建,这一章我们来研究一下如何使用. 第一步:在xml文件中加入以下权限 & ...

  6. Android 给app加入百度地图

    1.获取sha1值 (1)win+R进入cmd窗口 (2)输入以下代码 C:\SoftApplication\javajdk\jdk1.8.0_151\bin>keytool -list -v ...

  7. 我的Android开发之路——百度地图开源工具获取定位信息

    定位技术在现在的移动设备上是必不可少的,许多app都会使用定位功能. 通常定位方式有两种:GPS定位:网络定位. Android系统对这两种定位方式都提供了相应的API支持,但是因为google的网络 ...

  8. 《!--suppress ALL --> 在Android XML 文件中的用途是什么?

    <!--suppress ALL --> 在Android XML 文件中的用途是什么? 警告一次又一次地出现在谷歌地图的 XML 文件中,但是当我使用时,所有警告都被禁用.那么压制所有评 ...

  9. Eclipse与Android源码中ProGuard工具的使用

    由于工作需要,这两天和同事在研究android下面的ProGuard工具的使用,通过查看android官网对该工具的介绍以及网络上其它相关资料,再加上自己的亲手实践,算是有了一个基本了解.下面将自己的 ...

随机推荐

  1. EF数据迁移命令

    在包管理器控制台中输入命令“enable-migrations”,然后按Enter键!Visual Studio将生成一个名为“Configurations.cs”的文件; 你可以安全地忽略它,但你需 ...

  2. HDU 5438 Ponds dfs模拟

    2015 ACM/ICPC Asia Regional Changchun Online 题意:n个池塘,删掉度数小于2的池塘,输出池塘数为奇数的连通块的池塘容量之和. 思路:两个dfs模拟就行了 # ...

  3. Weka中数据挖掘与机器学习系列之Exploer界面(七)

    不多说,直接上干货! Weka的Explorer(探索者)界面,是Weka的主要图形化用户界面,其全部功能都可通过菜单选择或表单填写进行访问.本博客将详细介绍Weka探索者界面的图形化用户界面.预处理 ...

  4. 使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控--转

    原文地址:http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247483789&idx=1&sn=ae11f04780 ...

  5. Docker安装配置教程

    Docker公开课 1 Docker介绍 1.1 Docker是什么 云计算\云服务 IAAS(基础设施即服务).PAAS(平台即服务).SAAS(软件即服务) Docker到底是什么呢? Docke ...

  6. 关于HTML5和CSS3的几个“新增”

    html5和css3分别是目前最新的web前端编程的标准,加入了新的标准和要求. 1.HTML5新增input输入类型,即type后面的值 文本域 <input type="text& ...

  7. CSUOJ 1532 JuQueen

    Problem H JuQueen JuQueen is the super computer with the best performance allover Germany. It is on ...

  8. Activiti工作流框架学习(一)——环境的搭建和数据表的了解

    一.什么是工作流 工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档.信息或任务的过程自动进行,从而实现 ...

  9. android ActionBar的使用

    Action Bar主要功能包括:   1. 显示选项菜单   2. 提供标签页的切换方式的导航功能,能够切换多个fragment.    3.  提供下拉的导航条目.   4. 提供交互式活动视图取 ...

  10. DataTable填充实体类返回泛型集合

    昨天找坤哥看到我的一段代码.例如以下: 略微解释下,这段代码时D层查询结束后,将datatable查询到的结果赋值给实体对象的属性,然后返回实体的过程.坤哥看了之后问我.假设实体有500多个属性.难道 ...