W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
編寫:penkzhou - 原文:http://developer.android.com/training/location/display-address.html
獲取最后可知位置和獲取位置更新課程描述了如何以一個Location對象的形式獲取用戶的位置信息,這個位置信息包括了經(jīng)緯度。盡管經(jīng)緯度對計算地理距離和在地圖上顯示位置很有用,但是更多情況下位置的地址更有用。例如,如果我們想讓用戶知道他們在哪里,那么一個街道地址比地理坐標(biāo)(經(jīng)度/緯度)更加有意義。
使用 Android 框架位置 APIs 的 Geocoder 類,我們可以將地址轉(zhuǎn)換成相應(yīng)的地理坐標(biāo)。這個過程叫做地理編碼?;蛘撸覀兛梢詫⒌乩砦恢棉D(zhuǎn)換成相應(yīng)的地址。這種地址查找功能叫做反向地理編碼。
這節(jié)課介紹了如何用 getFromLocation() 方法將地理位置轉(zhuǎn)換成地址。這個方法返回與制定經(jīng)緯度相對應(yīng)的估計的街道地址。
設(shè)備的最后可知位置對于地址查找功能是很有用的基礎(chǔ)。獲取最后可知位置介紹了如何通過調(diào)用 fused location provider 提供的 getLastLocation()) 方法找到設(shè)備的最后可知位置。
為了訪問 fused location provider,我們需要創(chuàng)建一個 Google Play services API client 的實(shí)例。關(guān)于如何連接 client,請見連接 Google Play Services 。
為了讓 fused location provider 得到一個準(zhǔn)確的街道地址,在應(yīng)用的 manifest 文件添加位置權(quán)限 ACCESS_FINE_LOCATION
,如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.gms.location.sample.locationupdates" >
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
</manifest>
Geocoder 類的 getFromLocation() 方法接收一個經(jīng)度和緯度,返回一個地址列表。這個方法是同步的,可能會花很長時間來完成它的工作,所以我們不應(yīng)該在應(yīng)用的主線程和 UI 線程里調(diào)用這個方法。
IntentService 類提供了一種結(jié)構(gòu)使一個任務(wù)在后臺線程運(yùn)行。使用這個類,我們可以在不影響 UI 響應(yīng)速度的情況下處理一個長時間運(yùn)行的操作。注意到,AsyncTask 類也可以執(zhí)行后臺操作,但是它被設(shè)計用于短時間運(yùn)行的操作。在 activity 重新創(chuàng)建時(例如當(dāng)設(shè)備旋轉(zhuǎn)時),AsyncTask 不應(yīng)該保存 UI 的引用。相反,當(dāng) activity 重建時,不需要取消 IntentService。
定義一個繼承 IntentService 的類 FetchAddressIntentService
。這個類是地址查找服務(wù)。這個 Intent 服務(wù)在一個工作線程上異步地處理一個 intent,并在它離開這個工作時自動停止。Intent 外加的數(shù)據(jù)提供了服務(wù)需要的數(shù)據(jù),包括一個用于轉(zhuǎn)換成地址的 Location 對象和一個用于處理地址查找結(jié)果的 ResultReceiver 對象。這個服務(wù)用一個 Geocoder 來獲取位置的地址,并且將結(jié)果發(fā)送給 ResultReceiver。
在 manifest 文件中添加一個節(jié)點(diǎn)以定義 intent 服務(wù):
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.gms.location.sample.locationaddress" >
<application
...
<service
android:name=".FetchAddressIntentService"
android:exported="false"/>
</application>
...
</manifest>
Note:manifest 文件里的
<service>
節(jié)點(diǎn)不需要包含一個 intent filter,這是因?yàn)槲覀兊闹?activity 通過指定 intent 用到的類的名字來創(chuàng)建一個隱式的 intent。
將一個地理位置傳換成地址的過程叫做反向地理編碼。通過實(shí)現(xiàn) FetchAddressIntentService
類的 onHandleIntent()) 來執(zhí)行 intent 服務(wù)的主要工作,即反向地理編碼請求。創(chuàng)建一個 Geocoder 對象來處理反向地理編碼。
一個區(qū)域設(shè)置代表一個特定的地理上的或者語言上的區(qū)域。Locale 對象用于調(diào)整信息的呈現(xiàn)方式,例如數(shù)字或者日期,來適應(yīng)區(qū)域設(shè)置表示的區(qū)域的約定。傳一個 Locale 對象到 Geocoder 對象,確保地址結(jié)果為用戶的地理區(qū)域作出了本地化。
@Override
protected void onHandleIntent(Intent intent) {
Geocoder geocoder = new Geocoder(this, Locale.getDefault());
...
}
下一步是從 geocoder 獲取街道地址,處理可能出現(xiàn)的錯誤,和將結(jié)果返回給請求地址的 activity。我們需要兩個分別代表成功和失敗的數(shù)字常量來報告地理編碼過程的結(jié)果。定義一個 Constants
類來包含這些值,如下所示:
public final class Constants {
public static final int SUCCESS_RESULT = 0;
public static final int FAILURE_RESULT = 1;
public static final String PACKAGE_NAME =
"com.google.android.gms.location.sample.locationaddress";
public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER";
public static final String RESULT_DATA_KEY = PACKAGE_NAME +
".RESULT_DATA_KEY";
public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME +
".LOCATION_DATA_EXTRA";
}
為了獲取與地理位置相對應(yīng)的街道地址,調(diào)用 getFromLocation(),傳入位置對象的經(jīng)度和緯度,以及我們想要返回的地址的最大數(shù)量。在這種情況下,我們只需要一個地址。geocoder 返回一個地址數(shù)組。如果沒有找到匹配指定位置的地址,那么它會返回空的列表。如果沒有可用的后臺地理編碼服務(wù),geocoder 會返回 null。
如下面代碼介紹來檢查下述這些錯誤。如果出現(xiàn)錯誤,就將相應(yīng)的錯誤信息傳給變量 errorMessage
,從而將錯誤信息發(fā)送給發(fā)出請求的 activity:
使用 Address 類中的 getAddressLine()) 方法來獲得地址對象的個別行。然后將這些行加入一個地址 fragment 列表當(dāng)中。其中,這個地址 fragment 列表準(zhǔn)備好返回到發(fā)出地址請求的 activity。
為了將結(jié)果返回給發(fā)出地址請求的 activity,需要調(diào)用 deliverResultToReceiver()
方法(定義于下面的把地址返回給請求端)。結(jié)果由之前提到的成功/失敗數(shù)字代碼和一個字符串組成。在反向地理編碼成功的情況下,這個字符串包含著地址。在失敗的情況下,這個字符串包含錯誤的信息。如下所示:
@Override
protected void onHandleIntent(Intent intent) {
String errorMessage = "";
// Get the location passed to this service through an extra.
Location location = intent.getParcelableExtra(
Constants.LOCATION_DATA_EXTRA);
...
List<Address> addresses = null;
try {
addresses = geocoder.getFromLocation(
location.getLatitude(),
location.getLongitude(),
// In this sample, get just a single address.
1);
} catch (IOException ioException) {
// Catch network or other I/O problems.
errorMessage = getString(R.string.service_not_available);
Log.e(TAG, errorMessage, ioException);
} catch (IllegalArgumentException illegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = getString(R.string.invalid_lat_long_used);
Log.e(TAG, errorMessage + ". " +
"Latitude = " + location.getLatitude() +
", Longitude = " +
location.getLongitude(), illegalArgumentException);
}
// Handle case where no address was found.
if (addresses == null || addresses.size() == 0) {
if (errorMessage.isEmpty()) {
errorMessage = getString(R.string.no_address_found);
Log.e(TAG, errorMessage);
}
deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);
} else {
Address address = addresses.get(0);
ArrayList<String> addressFragments = new ArrayList<String>();
// Fetch the address lines using getAddressLine,
// join them, and send them to the thread.
for(int i = 0; i < address.getMaxAddressLineIndex(); i++) {
addressFragments.add(address.getAddressLine(i));
}
Log.i(TAG, getString(R.string.address_found));
deliverResultToReceiver(Constants.SUCCESS_RESULT,
TextUtils.join(System.getProperty("line.separator"),
addressFragments));
}
}
Intent 服務(wù)最后要做的事情是將地址返回給啟動服務(wù)的 activity 里的 ResultReceiver。這個 ResultReceiver 類允許我們發(fā)送一個帶有結(jié)果的數(shù)字代碼和一個包含結(jié)果數(shù)據(jù)的消息。這個數(shù)字代碼說明了地理編碼請求是成功還是失敗。在反向地理編碼成功的情況下,這個消息包含著地址。在失敗的情況下,這個消息包含一些描述失敗原因的文本。
我們已經(jīng)可以從 geocoder 取得地址,捕獲到可能出現(xiàn)的錯誤,調(diào)用 deliverResultToReceiver()
方法?,F(xiàn)在我們需要定義 deliverResultToReceiver()
方法來將結(jié)果代碼和消息包發(fā)送給結(jié)果接收端。
對于結(jié)果代碼,使用已經(jīng)傳給 deliverResultToReceiver()
方法的 resultCode
參數(shù)的值。對于消息包的結(jié)構(gòu),連接 Constants
類的 RESULT_DATA_KEY
常量(定義與獲取街道地址數(shù)據(jù))和傳給 deliverResultToReceiver()
方法的 message
參數(shù)的值。如下所示:
public class FetchAddressIntentService extends IntentService {
protected ResultReceiver mReceiver;
...
private void deliverResultToReceiver(int resultCode, String message) {
Bundle bundle = new Bundle();
bundle.putString(Constants.RESULT_DATA_KEY, message);
mReceiver.send(resultCode, bundle);
}
}
上節(jié)課定義的 intent 服務(wù)在后臺運(yùn)行,同時,該服務(wù)負(fù)責(zé)提取與指定地理位置相對應(yīng)的地址。當(dāng)我們啟動服務(wù),Android 框架會實(shí)例化并啟動服務(wù)(如果該服務(wù)沒有運(yùn)行),并且如果需要的話,創(chuàng)建一個進(jìn)程。如果服務(wù)正在運(yùn)行,那么讓它保持運(yùn)行狀態(tài)。因?yàn)榉?wù)繼承于 IntentService,所以當(dāng)所有 intent 都被處理完之后,該服務(wù)會自動停止。
在我們應(yīng)用的主 activity 中啟動服務(wù),并且創(chuàng)建一個 Intent 來把數(shù)據(jù)傳給服務(wù)。我們需要創(chuàng)建一個顯式的 intent,這是因?yàn)槲覀冎幌胛覀兊姆?wù)響應(yīng)該 intent。詳細(xì)請見 Intent Types。
為了創(chuàng)建一個顯式的 intent,需要為服務(wù)指定要用到的類名:FetchAddressIntentService.class
。在 intent 附加數(shù)據(jù)中傳入兩個信息:
下面的代碼介紹了如何啟動 intent 服務(wù):
public class MainActivity extends ActionBarActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
protected Location mLastLocation;
private AddressResultReceiver mResultReceiver;
...
protected void startIntentService() {
Intent intent = new Intent(this, FetchAddressIntentService.class);
intent.putExtra(Constants.RECEIVER, mResultReceiver);
intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);
startService(intent);
}
}
當(dāng)用戶請求查找地理地址時,調(diào)用上述的 startIntentService()
方法。例如,用戶可能會在我們應(yīng)用的 UI 上面點(diǎn)擊提取地址按鈕。在啟動 intent 服務(wù)之前,我們需要檢查是否已經(jīng)連接到 Google Play services。下面的代碼片段介紹在一個按鈕 handler 中調(diào)用 startIntentService()
方法。
public void fetchAddressButtonHandler(View view) {
// Only start the service to fetch the address if GoogleApiClient is
// connected.
if (mGoogleApiClient.isConnected() && mLastLocation != null) {
startIntentService();
}
// If GoogleApiClient isn't connected, process the user's request by
// setting mAddressRequested to true. Later, when GoogleApiClient connects,
// launch the service to fetch the address. As far as the user is
// concerned, pressing the Fetch Address button
// immediately kicks off the process of getting the address.
mAddressRequested = true;
updateUIWidgets();
}
如果用戶點(diǎn)擊了應(yīng)用 UI 上面的提取地址按鈕,那么我們必須在 Google Play services 連接穩(wěn)定之后啟動 intent 服務(wù)。下面的代碼片段介紹了調(diào)用 Google API Client 提供的 onConnected()) 回調(diào)函數(shù)中的 startIntentService()
方法。
public class MainActivity extends ActionBarActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
@Override
public void onConnected(Bundle connectionHint) {
// Gets the best and most recent location currently available,
// which may be null in rare cases when a location is not available.
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (mLastLocation != null) {
// Determine whether a Geocoder is available.
if (!Geocoder.isPresent()) {
Toast.makeText(this, R.string.no_geocoder_available,
Toast.LENGTH_LONG).show();
return;
}
if (mAddressRequested) {
startIntentService();
}
}
}
}
Intent 服務(wù)已經(jīng)處理完地理編碼請求,并用 ResultReceiver 將結(jié)果返回給發(fā)出請求的 activity。在發(fā)出請求的 activity 里,定義一個繼承于 ResultReceiver 的 AddressResultReceiver
,用于處理在 FetchAddressIntentService
中的響應(yīng)。
結(jié)果包含一個數(shù)字代碼(resultCode
)和一個包含結(jié)果數(shù)據(jù)(resultData
)的消息。如果反向地理編碼成功的話,resultData
會包含地址。如果失敗,resultData
包含描述失敗原因的文本。關(guān)于錯誤信息更詳細(xì)的內(nèi)容,請見把地址返回給請求端
重寫 onReceiveResult() 方法來處理發(fā)送給接收端的結(jié)果,如下所示:
public class MainActivity extends ActionBarActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
class AddressResultReceiver extends ResultReceiver {
public AddressResultReceiver(Handler handler) {
super(handler);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Display the address string
// or an error message sent from the intent service.
mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);
displayAddressOutput();
// Show a toast message if an address was found.
if (resultCode == Constants.SUCCESS_RESULT) {
showToast(getString(R.string.address_found));
}
}
}
}
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: