本案例研究討論了怎樣將地圖和地理定位特性構建到 Android* 商務應用中。包含在 Google Maps* 上覆蓋商店位置,以及在設備進入商店地理圍欄鄰近區域時藉助地理圍欄通知用戶。html
在本案例研究中,咱們將會把地圖和地理定位功能集成到基於 Android 平板電腦的餐館商務應用中(圖 1)。 用戶可以從主菜單項「位置和地理圍欄」訪問地理定位功能(圖 2)。java
對於一款商務應用而言,顯示商店在地圖上的位置對用戶很直觀和實用(圖 3)。 Google Maps Android API 可提供一種簡單的方式將 Google Maps 集成至 Android 應用。api
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。app
而後,你需要從谷歌開發者控制檯(Google Developers Console)上對你的項目進行註冊並獲取一個 API 密鑰:https://console.developers.google.com/project。異步
你需要在 AndroidManifest.xml 文件里加入 API 密鑰。ide
爲了使用 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 密鑰 **
首先,在你的 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)。
在 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