在前面的blog中讲述了怎样获取最后已知位置和接收位置更新后如何从location对象中获取用户的位置,包含经纬度的坐标。 虽然纬度和经度是用于计算距离或显示地图的位置,在许多情况下,位置的地址是更为有用的。 例如,如果你想让你的用户知道他们是在哪,或他们正在接近什么,所以街道地址比位置的地理坐标(纬度/经度)更有意义。
利用Geocoder在Android框架位置API类,您可以将地址转换为相应的地理坐标。 这个过程被称为地理编码 。 或者,您可以地理位置转换为一个地址。 地址查找功能也被称为反向地理编码 。
本课向您展示如何使用getFromLocation()方法来地理位置转换为一个地址。 该方法返回对应于给定的纬度和经度的估计的街道地址。
获取地理位置
该设备的最后已知位置是地址查找功能一个有用的起点。 关于课程获得最后一个已知的位置展示了如何使用getLastLocation()所提供的方法融合位置提供可以找到设备的最新位置。
要访问融合位置提供者,你需要创建谷歌Play服务API客户端的一个实例。 要了解如何连接您的客户端,请参阅连接到谷歌播放服务 。
为了使融合的位置提供检索精确的街道地址,应用清单来设置位置权限ACCESS_FINE_LOCATION ,如下面的例子:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
定义一个Intent服务,以获取地址
所述getFromLocation()由所提供的方法Geocoder类接受纬度和经度,并且返回地址的列表。 该方法是同步的,可能需要很长的时间来完成工作,所以你不应该从主,用户界面(UI),您的应用程序的线程中调用它。
该IntentService类 提供了在后台线程运行任务的结构。 使用这个类,你可以处理一个长时间运行的操作,而不会影响你的用户界面的响应速度。 需要注意的是AsyncTask类还允许您进行后台操作,但它专为短期操作。 一个AsyncTask当装置旋转时不应该参考保持到UI如果活性被重新创建,例如。 与此相反,一个IntentService不需要时的活性被重建被取消。
定义一个FetchAddressIntentService扩展类IntentService 。 这个类是你的地址查找服务。 这样做的目的服务异步处理的工作线程的意图,当它运行的工作停止本身。 意图额外提供由该服务所需要的数据,其中包括一个Location为变换对象到一个地址,和一个ResultReceiver对象来处理的地址查找的结果。 该服务使用Geocoder来获取的地址的位置,并把结果发送到ResultReceiver 。
定义意图服务于应用清单
将一个条目添加到您的应用清单定义的意图服务:
<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>
注: 在manifest元素并不需要包括意图过滤器,因为您的主要活动通过指定类的名称要使用的意图创建一个明确的意图。
创建地理编码
地理位置转换为地址的过程被称为反向地理编码 。 要执行的意图服务的主要工作,就是你的反向地理编码要求,落实onHandleIntent()的范围内FetchAddressIntentService类 。 创建一个Geocoder对象来处理反向地理编码。
区域设置代表一个特定的地理或语言区域。 区域对象用于调整的信息的呈现,诸如数字或日期,以适合的约定在由区域设置所表示的区域。 传递一个Locale对象的Geocoder对象,以确保最终的地址定位于用户的地理区域。
@Override
protected void onHandleIntent(Intent intent) {
Geocoder geocoder = new Geocoder(this, Locale.getDefault());
...
}
检索街道地址数据
下一步是检索来自解析器的街道地址,处理可能发生的任何错误,并把结果发送回请求的地址的活性。 要报告地理编码过程的结果,你需要两个数字常量指示成功或失败。 定义一个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";
}
要得到相应的地理位置一个街道地址,调用getFromLocation()它传递的纬度和经度从位置对象,你想返回的地址的最大数量。 在这种情况下,要只是一个地址。 地理编码器返回地址的数组。 如果没有发现地址匹配给定的位置,它返回一个空列表。 如果没有可用的后端地理编码服务,地理编码器返回null。
检查以下错误,如下面的代码示例所示。 如果出现错误,将在相应的错误信息errorMessage变量,这样你就可以将其发送回请求的活动:
没有提供位置数据 -其意图演员不包括Location的反向地理编码所需的对象。
无效的纬度或经度使用 -所提供的纬度和/或经度值Location的对象是无效的。
没有可用的地理编码器 -后台地理编码服务不可用,由于网络错误或IO异常。
很抱歉,没有找到地址 -地址解析器找不到地址为给定的纬度/经度。
为了得到一个地址对象的个别线路,使用getAddressLine()所提供的方法Address类。 再加入行到准备返回给请求地址的活动地址片段的列表。
将结果发送回请求活动 ,调用deliverResultToReceiver()方法(在定义的返回地址给请求者 )。 结果由前述的数字成功/失败的代码和一个串。 在一个成功的反向地理编码的情况下,字符串包含的地址。 在发生故障的情况下,该字符串包含错误消息,如下面的代码示例:
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));
}
}
返回地址给请求者
意图服务必须做的最后一件事是发送地址回ResultReceiver在活动启动该服务。 该ResultReceiver类允许你发送一个数字结果代码以及包含结果数据的消息。 数字代码是报告地址解析请求的成功或失败非常有用。 在一个成功的反向地理编码的情况下,该消息包含的地址。 在出现故障的情况下,该消息包含一些文本描述用于失败的原因。
你已经从地理编码检索到的地址,困可能出现的任何错误,并称为deliverResultToReceiver()方法。 现在,你需要定义deliverResultToReceiver()发送一个结果代码和消息捆绑到结果接收方法。
对于结果代码,使用已传递给值deliverResultToReceiver()的方法resultCode参数。 构造消息包,并置RESULT_DATA_KEY从恒定Constants类(定义在检索街道地址数据 ),并在该值message传递给参数deliverResultToReceiver()方法,如下面的示例中:
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);
}
}
启动服务意向
意图的服务,如在上一节中所定义,在后台运行并负责获取对应于给定地理区域的地址。 当您启动该服务,Android框架实例,如果它没有运行启动该服务,并在需要时创建一个进程。 如果该服务已经运行,那么它仍然运行。 因为把服务延伸IntentService ,什么时候所有的意图都被处理自动关闭。
开始从你的应用程序的主活动服务,并创建一个Intent将数据传递到该服务。 你需要一个明确的意图,因为你只想要你的服务的意图做出回应。 欲了解更多信息,请参阅意图类型 。
要创建一个明确的意图,指定类使用该服务的名称: FetchAddressIntentService.class 。 通过在意图额外两条信息:
一个ResultReceiver处理地址查找的结果。
一个Location包含要转换到一个地址的经纬度对象。
下面的代码示例演示如何启动的意图服务:
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);
}
}
调用上面startIntentService()当用户需要一个需要地理编码地址查找的操作方法。 例如,用户可以按你的应用程序的UI中获取地址按钮。 开始的意图服务之前,您需要检查到谷歌Play服务连接存在。 下面的代码片段展示了调用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();
}
当建立到谷歌播放服务的连接,如果用户已经点击了您的应用程序的UI按钮,您还必须启动的意图服务。 下面的代码片段展示了调用startIntentService()方法中onConnected()由谷歌API客户端提供的回调:
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();
}
}
}
}
获得地理编码结果
这样做的目的服务已处理的地址解析请求,并使用ResultReceiver的结果返回给发出请求的活性。 在发出请求的活动,定义一个AddressResultReceiver延伸ResultReceiver处理从响应FetchAddressIntentService 。
结果包括数字结果代码( resultCode ),以及含有该结果数据的消息( resultData )。 如果反向地理编码过程是成功的,该resultData包含地址。 在出现故障的情况下,该resultData包含描述失败原因文本。 对于可能出现的错误的详细信息,请参阅返回地址给请求者 。
覆盖onReceiveResult()方法来处理结果传递到结果接收器,如下面的代码示例:
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));
}
}
}
}