編輯:關於Android編程
本案例研究討論了如何將地圖和地理定位特性構建到 Android* 商務應用中,包括在 Google Maps* 上覆蓋商店位置,以及在設備進入商店地理圍欄鄰近區域時借助地理圍欄通知用戶。
在本案例研究中,我們將會把地圖和地理定位功能集成到基於 Android 平板電腦的餐館商務應用中(圖 1)。 用戶可以從主菜單項“位置和地理圍欄”訪問地理定位功能(圖 2)。
圖 1 餐館應用主界面
圖 2 浮出控件菜單項
在 Google Maps 上顯示商店位置<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cD4KttTT2tK7v+7JzM7x06bTw7b40dSjrM/Uyr7JzLXq1Nq12M28yc+1xM671sO21NPDu6e3x7Oj1rG527rN09DTw6OozbwgM6OpoaMgR29vZ2xlIE1hcHMgQW5kcm9pZCBBUEkgv8nM4bmp0rvW1rzytaW1xLe9yr29qyBHb29nbGUgTWFwcyC8r7PJ1sEgQW5kcm9pZCDTptPDoaM8L3A+CjxwPgpHb29nbGUgTWFwcyBBbmRyb2lkIEFQSSB2MiA8YnI+CjwvcD4KPHA+CsrHIEdvb2dsZSBQbGF5ILf+zvEgQVBLILXE0ruyv7fWoaMgzqrBy7S0vajKudPDIEdvb2dsZSBNYXBzIEFuZHJvaWQgQVBJIHYyILXEIEFuZHJvaWQg06bTw6Os0OjSqs/C1NiyosXk1sMgR29vZ2xlIFBsYXkgt/7O8SBTREujrLvxyKEgQVBJIMPc1L+yotTa06bTw7XEIEFuZHJvaWRNYW5pZmVzdC54bWwgzsS8/tbQzO2808v50Oi1xMno1sPAtLbUv6q3oru3vrO9+NDQyejWw6GjPC9wPgo8cD4KytfPyKOsxOPQ6NKqsLTV1dLUz8LN+NW+yc+1xMu1w/fAtMno1sMgR29vZ2xlIFBsYXkgt/7O8SBTREujumh0dHA6Ly9kZXZlbG9wZXIuYW5kcm9pZC5jb20vZ29vZ2xlL3BsYXktc2VydmljZXMvc2V0dXAuaHRtbKGjPC9wPgo8cD4KyLu686OsxOPQ6NKqtNO5yLjov6q3osjL1LG/2NbGzKijqEdvb2dsZSBEZXZlbG9wZXJzIENvbnNvbGWjqcnPttTE47XEz+7Ev7340NDXorLhsqK78cih0ru49iBBUEkgw9zUv6O6aHR0cHM6Ly9jb25zb2xlLmRldmVsb3BlcnMuZ29vZ2xlLmNvbS9wcm9qZWN0oaMKIMTj0OjSqtTaIEFuZHJvaWRNYW5pZmVzdC54bWwgzsS8/tbQzO280yBBUEkgw9zUv6GjPC9wPgo8cD4KPGltZyBzcmM9"/uploadfile/Collfiles/20140724/2014072409390170.jpg" alt="\">
圖 3 餐館應用在谷歌地圖上顯示商店的位置。
01
<
uses-permission
android:name=”android.permission.INTERNET"/>
02
<
uses-permission
android:name
=
"android.permission.ACCESS_NETWORK_STATE"
/>
03
<
uses-permission
android:name
=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
04
<
uses-permission
android:name
=
"com.google.android.providers.gsf.permission.READ_GSERVICES"
/>
05
07
<
uses-permission
android:name
=
"android.permission.ACCESS_COARSE_LOCATION"
/>
08
<
uses-permission
android:name
=
"android.permission.ACCESS_FINE_LOCATION"
/>
09
<
uses-permission
android:name
=
"android.permission.ACCESS_MOCK_LOCATION"
/>
10
11
<
uses-feature
12
android:glEsVersion
=
"0x00020000"
13
android:required
=
"true"
/>
代碼示例 1。 建議在使用 Google Maps Android API 的應用上指定的權限。 包括 “ACCESS_MOCK_LOCATION” 權限(僅當需要使用模擬位置對應用進行測試時使用)
我們同樣需要將在1
<
meta-data
2
android:name
=
"com.google.android.gms.version"
3
android:value
=
"@integer/google_play_services_version"
/>
4
5
<
meta-data
6
android:name
=
"com.google.android.maps.v2.API_KEY"
7
android:value
=
"copy your API Key here"
/>
代碼示例 2。 指定 Google Play 服務版本和 API 密鑰 **
首先,在你的 activity 布局 xml 文件中,添加一個 MapFragment 元素(代碼示例 3)。
1
<
fragment
2
android:id
=
"@+id/storelocationmap"
3
android:layout_width
=
"fill_parent"
4
android:layout_height
=
"fill_parent"
5
android:name
=
"com.google.android.gms.maps.MapFragment"
6
/>
代碼示例 3。 在 Activity 布局中添加 MapFragment **
在你的 activity 類中,您可以檢索 Google Maps MapFragment 對象並在每個商店位置處繪制商店圖標。
01
…
02
private
static
final
LatLng CHANDLER =
new
LatLng(
33.455
,-
112.0668
);
03
…
04
private
static
final
StoreLocation[] ALLRESTURANTLOCATIONS =
new
StoreLocation[] {
05
new
StoreLocation(
new
LatLng(
33.455
,-
112.0668
),
new
String(
"Phoenix, AZ"
)),
06
new
StoreLocation(
new
LatLng(
33.5123
,-
111.9336
),
new
String(
"SCOTTSDALE, AZ"
)),
07
new
StoreLocation(
new
LatLng(
33.3333
,-
111.8335
),
new
String(
"Chandler, AZ"
)),
08
new
StoreLocation(
new
LatLng(
33.4296
,-
111.9436
),
new
String(
"Tempe, AZ"
)),
09
new
StoreLocation(
new
LatLng(
33.4152
,-
111.8315
),
new
String(
"Mesa, AZ"
)),
10
new
StoreLocation(
new
LatLng(
33.3525
,-
111.7896
),
new
String(
"Gilbert, AZ"
))
11
};
12
…
13
@Override
14
protected
void
onCreate(Bundle savedInstanceState) {
15
super
.onCreate(savedInstanceState);
16
setContentView(R.layout.geolocation_view);
17
18
mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.storelocationmap)).getMap();
19
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(CHANDLER, ZOOM_LEVEL));
20
Drawable iconDrawable = getResources().getDrawable(R.drawable.ic_launcher);
21
Bitmap iconBmp = ((BitmapDrawable) iconDrawable).getBitmap();
22
for
(
int
ix =
0
; ix < ALLRESTURANTLOCATIONS.length; ix++) {
23
mMap.addMarker(
new
MarkerOptions()
24
.position(ALLRESTURANTLOCATIONS[ix].mLatLng)
25
.icon(BitmapDescriptorFactory.fromBitmap(iconBmp)));
26
}
27
…
代碼示例 4。 在 Google Maps 上繪制商店圖標 **
在我們的餐館應用中,我們能夠為每個商店位置定義地理圍欄。 當設備進入商店附近時,應用將會發送一條通知,如“您已進入最喜愛的餐館的附近!” (圖 4)。
圖 4 我們根據興趣點和半徑將地理圍欄定義為一個圓形范圍。
在 Android SDK 中,位置服務也是 Google Play 服務 APK 的一部分,位於 “Extras” 目錄下。
如要申請地理圍欄監控,首先我們需要在應用的清單文件中指定 “ACCESS_FINE_LOCATION” 權限,該操作我們已經在上一部分中完成。
此外,我們還需要查看 Google Play 服務的可用性(代碼示例 5 中的 checkGooglePlayServices()
方法)。 locationClient().connect()
調用與位置客戶端成功建立連接後,位置服務將會調用onConnected(Bundle
bundle)
函數,位置客戶端可通過該函數申請添加或刪除地理圍欄。
001
public
class
GeolocationActivity
extends
Activity
implements
002
GooglePlayServicesClient.ConnectionCallbacks
003
…
004
{
005
…
006
private
LocationClient mLocationClient;
007
008
…
009
010
static
class
StoreLocation {
011
public
LatLng mLatLng;
012
public
String mId;
013
StoreLocation(LatLng latlng, String id) {
014
mLatLng = latlng;
015
mId = id;
016
}
017
}
018
019
@Override
020
protected
void
onCreate(Bundle savedInstanceState) {
021
super
.onCreate(savedInstanceState);
022
setContentView(R.layout.geolocation_view);
023
024
mLocationClient =
new
LocationClient(
this
,
this
,
this
);
025
026
// Create a new broadcast receiver to receive updates from the listeners and service
027
mGeofenceBroadcastReceiver =
new
ResturantGeofenceReceiver();
028
029
// Create an intent filter for the broadcast receiver
030
mIntentFilter =
new
IntentFilter();
031
032
// Action for broadcast Intents that report successful addition of geofences
033
mIntentFilter.addAction(ACTION_GEOFENCES_ADDED);
034
035
// Action for broadcast Intents that report successful removal of geofences
036
mIntentFilter.addAction(ACTION_GEOFENCES_REMOVED);
037
038
// Action for broadcast Intents containing various types of geofencing errors
039
mIntentFilter.addAction(ACTION_GEOFENCE_ERROR);
040
041
// All Location Services sample apps use this category
042
mIntentFilter.addCategory(CATEGORY_LOCATION_SERVICES);
043
044
createGeofences();
045
046
mRegisterGeofenceButton = (Button)findViewById(R.id.geofence_switch);
047
mGeofenceState = CAN_START_GEOFENCE;
048
049
}
050
051
@Override
052
protected
void
onResume() {
053
super
.onResume();
054
// Register the broadcast receiver to receive status updates
055
LocalBroadcastManager.getInstance(
this
).registerReceiver(
056
mGeofenceBroadcastReceiver, mIntentFilter);
057
}
058
059
/**
060
* Create a Geofence list
061
*/
062
public
void
createGeofences() {
063
for
(
int
ix=
0
; ix > ALLRESTURANTLOCATIONS.length; ix++) {
064
Geofence fence =
new
Geofence.Builder()
065
.setRequestId(ALLRESTURANTLOCATIONS[ix].mId)
066
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
067
.setCircularRegion(
068
ALLRESTURANTLOCATIONS[ix].mLatLng.latitude, ALLRESTURANTLOCATIONS[ix].mLatLng.longitude, GEOFENCERADIUS)
069
.setExpirationDuration(Geofence.NEVER_EXPIRE)
070
.build();
071
mGeofenceList.add(fence);
072
}
073
}
074
075
// callback function when the mRegisterGeofenceButton is clicked
076
public
void
onRegisterGeofenceButtonClick(View view) {
077
if
(mGeofenceState == CAN_REGISTER_GEOFENCE) {
078
registerGeofences();
079
mGeofenceState = GEOFENCE_REGISTERED;
080
mGeofenceButton.setText(R.string.unregister_geofence);
081
mGeofenceButton.setClickable(
true
);
082
else
{
083
unregisterGeofences();
084
mGeofenceButton.setText(R.string.register_geofence);
085
mGeofenceButton.setClickable(
true
);
086
mGeofenceState = CAN_REGISTER_GEOFENCE;
087
}
088
}
089
090
private
boolean
checkGooglePlayServices() {
091
int
result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(
this
);
092
if
(result == ConnectionResult.SUCCESS) {
093
return
true
;
094
}
095
else
{
096
Dialog errDialog = GooglePlayServicesUtil.getErrorDialog(
097
result,
098
this
,
099
CONNECTION_FAILURE_RESOLUTION_REQUEST);
100
101
if
(errorDialog !=
null
) {
102
errorDialog.show();
103
}
104
}
105
return
false
;
106
}
107
108
109
public
void
registerGeofences() {
110
111
if
(!checkGooglePlayServices()) {
112
113
return
;
114
}
115
mRequestType = REQUEST_TYPE.ADD;
116
117
try
{
118
// Try to add geofences
119
requestConnectToLocationClient();
120
}
catch
(UnsupportedOperationException e) {
121
// handle the exception
122
}
123
124
}
125
126
public
void
unregisterGeofences() {
127
128
if
(!checkGooglePlayServices()) {
129
return
;
130
}
131
132
// Record the type of removal
133
mRequestType = REQUEST_TYPE.REMOVE;
134
135
// Try to make a removal request
136
try
{
137
mCurrentIntent = getRequestPendingIntent());
138
requestConnectToLocationClient();
139
140
}
catch
(UnsupportedOperationException e) {
141
// handle the exception
142
}
143
}
144
145
public
void
requestConnectToLocationServices ()
throws
UnsupportedOperationException {
146
// If a request is not already in progress
147
if
(!mRequestInProgress) {
148
mRequestInProgress =
true
;
149
150
locationClient().connect();
151
}
152
else
{
153
// Throw an exception and stop the request
154
throw
new
UnsupportedOperationException();
155
}
156
}
157
158
159
/**
160
* Get a location client and disconnect from Location Services
161
*/
162
private
void
requestDisconnectToLocationServices() {
163
164
// A request is no longer in progress
165
mRequestInProgress =
false
;
166
167
locationClient().disconnect();
168
169
if
(mRequestType == REQUEST_TYPE.REMOVE) {
170
mCurrentIntent.cancel();
171
}
172
173
}
174
175
/**
176
* returns A LocationClient object
177
*/
178
private
GooglePlayServicesClient locationClient() {
179
if
(mLocationClient ==
null
) {
180
181
mLocationClient =
new
LocationClient(
this
,
this
,
this
);
182
}
183
return
mLocationClient;
184
185
}
186
187
/*
188
Called back from the Location Services when the request to connect the client finishes successfully. At this point, you can
189
request the current location or start periodic updates
190
*/
191
@Override
192
public
void
onConnected(Bundle bundle) {
193
if
(mRequestType == REQUEST_TYPE.ADD) {
194
// Create a PendingIntent for Location Services to send when a geofence transition occurs
195
mGeofencePendingIntent = createRequestPendingIntent();
196
197
// Send a request to add the current geofences
198
mLocationClient.addGeofences(mGeofenceList, mGeofencePendingIntent,
this
);
199
200
}
201
else
if
(mRequestType == REQUEST_TYPE.REMOVE){
202
203
mLocationClient.removeGeofences(mCurrentIntent,
this
);
204
}
205
}
206
…
207
}
代碼示例 5。 通過位置服務申請地理圍欄監控 **
位置服務申請通常是非阻塞或異步調用。 事實上,在上一部分的代碼示例 5 中,我們已經實施了這些函數中的一個:locationClient().connect()
調用和位置客戶端建立連接後,位置服務將會調用onConnected(Bundle
bundle)
函數。 代碼示例 6 列出了我們需要實施的其他位置回調函數。
001
public
class
GeolocationActivity
extends
Activity
implements
002
OnAddGeofencesResultListener,
003
OnRemoveGeofencesResultListener,
004
GooglePlayServicesClient.ConnectionCallbacks,
005
GooglePlayServicesClient.OnConnectionFailedListener {
006
…
007
008
009
@Override
010
public
void
onDisconnected() {
011
mRequestInProgress =
false
;
012
mLocationClient =
null
;
013
}
014
015
016
017
/*
018
* Handle the result of adding the geofences
019
*/
020
@Override
021
public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) {
022
023
// Create a broadcast Intent that notifies other components of success or failure
024
Intent broadcastIntent = new Intent();
025
026
// Temp storage for messages
027
String msg;
028
029
// If adding the geocodes was successful
030
if (LocationStatusCodes.SUCCESS == statusCode) {
031
032
// Create a message containing all the geofence IDs added.
033
msg = getString(R.string.add_geofences_result_success,
034
Arrays.toString(geofenceRequestIds));
035
036
// Create an Intent to broadcast to the app
037
broadcastIntent.setAction(ACTION_GEOFENCES_ADDED)
038
.addCategory(CATEGORY_LOCATION_SERVICES)
039
.putExtra(EXTRA_GEOFENCE_STATUS, msg);
040
// If adding the geofences failed
041
} else {
042
msg = getString(
043
R.string.add_geofences_result_failure,
044
statusCode,
045
Arrays.toString(geofenceRequestIds)
046
);
047
broadcastIntent.setAction(ACTION_GEOFENCE_ERROR)
048
.addCategory(CATEGORY_LOCATION_SERVICES)
049
.putExtra(EXTRA_GEOFENCE_STATUS, msg);
050
}
051
052
LocalBroadcastManager.getInstance(this)
053
.sendBroadcast(broadcastIntent);
054
055
// request to disconnect the location client
056
requestDisconnectToLocationServices();
057
}
058
059
/*
060
* Implementation of OnConnectionFailedListener.onConnectionFailed
061
* If a connection or disconnection request fails, report the error
062
* connectionResult is passed in from Location Services
063
*/
064
@Override
065
public
void
onConnectionFailed(ConnectionResult connectionResult) {
066
mInProgress =
false
;
067
if
(connectionResult.hasResolution()) {
068
069
try
{
070
connectionResult.startResolutionForResult(
this
,
071
CONNECTION_FAILURE_RESOLUTION_REQUEST);
072
}
073
catch
(SendIntentException e) {
074
// log the error
075
}
076
}
077
else
{
078
Intent errorBroadcastIntent =
new
Intent(ACTION_CONNECTION_ERROR);
079
errorBroadcastIntent.addCategory(CATEGORY_LOCATION_SERVICES)
080
.putExtra(EXTRA_CONNECTION_ERROR_CODE,
081
connectionResult.getErrorCode());
082
LocalBroadcastManager.getInstance(
this
)
083
.sendBroadcast(errorBroadcastIntent);
084
}
085
}
086
087
@Override
088
public
void
onRemoveGeofencesByPendingIntentResult(
int
statusCode,
089
PendingIntent requestIntent) {
090
091
// Create a broadcast Intent that notifies other components of success or failure
092
Intent broadcastIntent =
new
Intent();
093
094
// If removing the geofences was successful
095
if
(statusCode == LocationStatusCodes.SUCCESS) {
096
097
//
Set the action and add the result message
098
broadcastIntent.setAction(ACTION_GEOFENCES_REMOVED);
099
broadcastIntent.putExtra(EXTRA_GEOFENCE_STATUS,
100
getString(R.string.remove_geofences_intent_success));
101
102
}
103
else
{
104
//
removing the geocodes failed
105
106
107
//
Set the action and add the result message
108
broadcastIntent.setAction(ACTION_GEOFENCE_ERROR);
109
broadcastIntent.putExtra(EXTRA_GEOFENCE_STATUS,
110
getString(R.string.remove_geofences_intent_failure,
111
statusCode));
112
}
113
LocalBroadcastManager.getInstance(
this
)
114
.sendBroadcast(broadcastIntent);
115
116
//
request to disconnect the location client
117
requestDisconnectToLocationServices();
118
}
119
120
121
public
class
ResturantGeofenceReceiver
extends
BroadcastReceiver {
122
123
124
@Override
125
public
void
onReceive(Context context, Intent intent) {
126
String
action = intent.getAction();
127
128
//
Intent contains information about errors in adding or removing geofences
129
if
(TextUtils.equals(action, ACTION_GEOFENCE_ERROR)) {
130
//
handleGeofenceError(context, intent);
131
}
132
else
if
(TextUtils.equals(action, ACTION_GEOFENCES_ADDED)
133
||
134
TextUtils.equals(action,
ACTION_GEOFENCES_REMOVED)) {
135
//
handleGeofenceStatus(context, intent);
136
}
137
else
if
(TextUtils.equals(action, ACTION_GEOFENCE_TRANSITION)) {
138
//
handleGeofenceTransition(context, intent);
139
}
140
else
{
141
//
handle error
142
}
143
144
}
145
}
146
147
一,系統啟動Android設備的開機流程總得來分可以分為三部分:加載引導程序引導程序bootloader是開機運行的第一個小程序,因此它是針對特定的主板與芯片的。boot
你現在還是把錢都存在余額寶?相信你也注意到,現在理財方式越來越多,余額寶的收益也越來越低,比如說財付通的理財通,性質類似於余額寶。現在微信也有理財通,收益還
(一)概述上節我們對BroadcastReceiver已經有了一個初步的了解了,知道兩種廣播類型:標准與有序, 動態或靜態注冊廣播接收者,監聽系統廣播,自己發送廣播!已經
Android 的 Handler 機制(也有人叫消息機制)目的是為了跨線程通信,也就是多線程通信。之所以需要跨線程通信是因為在 Android 中主線程通常只負責 UI