使用HttpURLConnection和AsyncTask请求解析地理位置的原理其实很简单,就是利用AsyncTask发起请求异步网络请求,然后通过HttpURLConnection解析JSON字符串。现在我们利用 Google Map 的开放API。通过HTTP调用传入经纬度的数值,然后对方返回一个JSON格式的地址信息字符串,通过解析JSON串就能得到具体的地址。
- 比如下面是访问 Google Map 提供的免费开发的 JSON API 中国北京天安门的 JSON 地址(地址中的纬度39.908722和经度116.397499在实际的代开发代码中可以用标志位{0},{1}来代替,并由设备的GPS或网络定位来获取当前所在的经纬度传入{0},{1}):
http://maps.google.cn/maps/api/geocode/json?latlng=39.908722,116.397499&sensor=true&language=zh-CN
访问后的JSON代码如下:
{
"results" : [
{
"address_components" : [
{
"long_name" : "天安门",
"short_name" : "天安门",
"types" : [ "premise" ]
},
{
"long_name" : "东城区",
"short_name" : "东城区",
"types" : [ "political", "sublocality", "sublocality_level_1" ]
},
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "locality", "political" ]
},
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "中国",
"short_name" : "CN",
"types" : [ "country", "political" ]
}
],
"formatted_address" : "中国北京市东城区天安门",
"geometry" : {
"bounds" : {
"northeast" : {
"lat" : 39.9088799,
"lng" : 116.3981847 },
"southwest" : {
"lat" : 39.9085116,
"lng" : 116.3967974 }
},
"location" : {
"lat" : 39.90869,
"lng" : 116.3975334
},
"location_type" : "ROOFTOP",
"viewport" : {
"northeast" : {
"lat" : 39.9100447302915,
"lng" : 116.3988400302915 },
"southwest" : {
"lat" : 39.9073467697085,
"lng" : 116.3961420697085 }
}
},
"place_id" : "ChIJEcuXlb9S8DURhgBLykaGiuE",
"types" : [ "premise" ]
},
{
"address_components" : [
{
"long_name" : "1",
"short_name" : "1",
"types" : [ "street_number" ]
},
{
"long_name" : "西长安街",
"short_name" : "西长安街",
"types" : [ "route" ]
},
{
"long_name" : "西城区",
"short_name" : "西城区",
"types" : [ "political", "sublocality", "sublocality_level_1" ]
},
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "locality", "political" ]
},
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "中国",
"short_name" : "CN",
"types" : [ "country", "political" ]
},
{
"long_name" : "100031",
"short_name" : "100031",
"types" : [ "postal_code" ]
}
],
"formatted_address" : "中国北京市西城区西长安街1号 邮政编码: 100031",
"geometry" : {
"location" : {
"lat" : 39.907871,
"lng" : 116.3929394
},
"location_type" : "ROOFTOP",
"viewport" : {
"northeast" : {
"lat" : 39.9092199802915,
"lng" : 116.3942883802915 },
"southwest" : {
"lat" : 39.9065220197085,
"lng" : 116.3915904197085 }
}
},
"place_id" : "ChIJO-uUspVS8DURL89OOAGFbjc",
"types" : [ "street_address" ]
},
{
"address_components" : [
{
"long_name" : "东城区",
"short_name" : "东城区",
"types" : [ "political", "sublocality", "sublocality_level_1" ]
},
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "locality", "political" ]
},
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "中国",
"short_name" : "CN",
"types" : [ "country", "political" ]
}
],
"formatted_address" : "中国北京市东城区",
"geometry" : {
"bounds" : {
"northeast" : {
"lat" : 39.9740365,
"lng" : 116.4523907 },
"southwest" : {
"lat" : 39.8586252,
"lng" : 116.3771342 }
},
"location" : {
"lat" : 39.92835300000001,
"lng" : 116.416357
},
"location_type" : "APPROXIMATE",
"viewport" : {
"northeast" : {
"lat" : 39.9740365,
"lng" : 116.4523907 },
"southwest" : {
"lat" : 39.8586252,
"lng" : 116.3771342 }
}
},
"place_id" : "ChIJLwKYcTNT8DUR8DZJ7J2CieA",
"types" : [ "political", "sublocality", "sublocality_level_1" ]
},
{
"address_components" : [
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "locality", "political" ]
},
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "中国",
"short_name" : "CN",
"types" : [ "country", "political" ]
}
],
"formatted_address" : "中国北京市",
"geometry" : {
"bounds" : {
"northeast" : {
"lat" : 41.0608157,
"lng" : 117.514625 },
"southwest" : {
"lat" : 39.442758,
"lng" : 115.4234112 }
},
"location" : {
"lat" : 39.90419989999999,
"lng" : 116.4073963
},
"location_type" : "APPROXIMATE",
"viewport" : {
"northeast" : {
"lat" : 40.2164962,
"lng" : 116.7829835 },
"southwest" : {
"lat" : 39.6612714,
"lng" : 116.0119343 }
}
},
"place_id" : "ChIJuSwU55ZS8DURiqkPryBWYrk",
"types" : [ "locality", "political" ]
},
{
"address_components" : [
{
"long_name" : "北京市",
"short_name" : "北京市",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "中国",
"short_name" : "CN",
"types" : [ "country", "political" ]
}
],
"formatted_address" : "中国北京市",
"geometry" : {
"bounds" : {
"northeast" : {
"lat" : 41.0608157,
"lng" : 117.514625 },
"southwest" : {
"lat" : 39.442758,
"lng" : 115.4234112 }
},
"location" : {
"lat" : 40.2373519,
"lng" : 116.2304616
},
"location_type" : "APPROXIMATE",
"viewport" : {
"northeast" : {
"lat" : 41.0608157,
"lng" : 117.514625 },
"southwest" : {
"lat" : 39.442758,
"lng" : 115.4234112 }
}
},
"place_id" : "ChIJuSwU55ZS8DURp8_dOLuVd8A",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"address_components" : [
{
"long_name" : "中国",
"short_name" : "CN",
"types" : [ "country", "political" ]
}
],
"formatted_address" : "中国",
"geometry" : {
"bounds" : {
"northeast" : {
"lat" : 53.56097399999999,
"lng" : 134.7728099 },
"southwest" : {
"lat" : 17.9996,
"lng" : 73.4994136 }
},
"location" : {
"lat" : 35.86166,
"lng" : 104.195397
},
"location_type" : "APPROXIMATE",
"viewport" : {
"northeast" : {
"lat" : 53.56097399999999,
"lng" : 134.7728099 },
"southwest" : {
"lat" : 17.9996,
"lng" : 73.4994136 }
}
},
"place_id" : "ChIJwULG5WSOUDERbzafNHyqHZU",
"types" : [ "country", "political" ]
}
],
"status" : "OK"
}
当访问到上面的JSON代码后,就从“results”标志位开始解析到“status”标志位结束,当解析到“formatted_address”标志位后就显示“formatted_address”标志位里的文本信息。
因为网络访问不能在主线程中进行,所以要结合AsyncTask与HttpURLConnection实现地址的异步获取。获取地址信息的任务代码如下:
- GetAddressTask.java逻辑代码如下:
package com.fukaimei.mapapi.task;
import android.location.Location;
import android.os.AsyncTask;
import android.util.Log;
import com.fukaimei.mapapi.http.HttpRequestUtil;
import com.fukaimei.mapapi.http.tool.HttpReqData;
import com.fukaimei.mapapi.http.tool.HttpRespData;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.MessageFormat;
public class GetAddressTask extends AsyncTask<Location, Void, String> {
private final static String TAG = "GetAddressTask";
private static String mAddressUrl = "http://maps.google.cn/maps/api/geocode/json?latlng={0},{1}&sensor=true&language=zh-CN";
private OnAddressListener mListener;
public GetAddressTask() {
super();
}
@Override
protected String doInBackground(Location... params) {
Location location = params[0];
String url = MessageFormat.format(mAddressUrl, location.getLatitude(), location.getLongitude());
HttpReqData req_data = new HttpReqData(url);
HttpRespData resp_data = HttpRequestUtil.getData(req_data);
Log.d(TAG, "return json = " + resp_data.content);
String address = "当前定位地址解析失败,请开启网络方可解析!";
if (resp_data.err_msg.length() <= 0) {
try {
JSONObject obj = new JSONObject(resp_data.content);
JSONArray resultArray = obj.getJSONArray("results");
if (resultArray.length() > 0) {
JSONObject resultObj = resultArray.getJSONObject(0);
address = resultObj.getString("formatted_address");
}
} catch (JSONException e) {
e.printStackTrace();
}
}
Log.d(TAG, "address = " + address);
return address;
}
@Override
protected void onPostExecute(String address) {
mListener.onFindAddress(address);
}
public void setOnAddressListener(OnAddressListener listener) {
mListener = listener;
}
public interface OnAddressListener {
void onFindAddress(String address);
}
}
- HttpRequestUtil.java逻辑代码如下:
package com.fukaimei.mapapi.http;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import android.graphics.BitmapFactory;
import android.util.Log;
import com.fukaimei.mapapi.http.tool.HttpReqData;
import com.fukaimei.mapapi.http.tool.HttpRespData;
import com.fukaimei.mapapi.http.tool.StreamTool;
public class HttpRequestUtil {
private static final String TAG = "HttpRequestUtil";
//设置http连接的头部信息
private static void setConnHeader(HttpURLConnection conn, String method, HttpReqData req_data)
throws ProtocolException {
conn.setRequestMethod(method);
conn.setConnectTimeout(5000);
conn.setReadTimeout(10000);
conn.setRequestProperty("Accept", "*/*");
//IE使用
// conn.setRequestProperty("Accept-Language", "zh-CN");
// conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C)");
//firefox使用
conn.setRequestProperty("Accept-Language", "zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0");
conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
if (req_data.content_type.equals("") != true) {
conn.setRequestProperty("Content-Type", req_data.content_type);
}
if (req_data.x_requested_with.equals("") != true) {
conn.setRequestProperty("X-Requested-With", req_data.x_requested_with);
}
if (req_data.referer.equals("") != true) {
conn.setRequestProperty("Referer", req_data.referer);
}
if (req_data.cookie.equals("") != true) {
conn.setRequestProperty("Cookie", req_data.cookie);
Log.d(TAG, "setConnHeader cookie=" + req_data.cookie);
}
}
private static String getRespCookie(HttpURLConnection conn, HttpReqData req_data) {
String cookie = "";
Map<String, List<String>> headerFields = conn.getHeaderFields();
if (headerFields != null) {
List<String> cookies = headerFields.get("Set-Cookie");
if (cookies != null) {
for (String cookie_item : cookies) {
cookie = cookie + cookie_item + "; ";
}
} else {
cookie = req_data.cookie;
}
} else {
cookie = req_data.cookie;
}
Log.d(TAG, "cookie=" + cookie);
return cookie;
}
//get文本数据
public static HttpRespData getData(HttpReqData req_data) {
HttpRespData resp_data = new HttpRespData();
try {
URL url = new URL(req_data.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
setConnHeader(conn, "GET", req_data);
conn.connect();
resp_data.content = StreamTool.getUnzipStream(conn.getInputStream(),
conn.getHeaderField("Content-Encoding"), req_data.charset);
resp_data.cookie = conn.getHeaderField("Set-Cookie");
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
resp_data.err_msg = e.getMessage();
}
return resp_data;
}
//post的内容放在url中
public static HttpRespData postUrl(HttpReqData req_data) {
HttpRespData resp_data = new HttpRespData();
String s_url = req_data.url;
if (req_data.params != null) {
s_url += "?" + req_data.params.toString();
}
Log.d(TAG, "s_url=" + s_url);
try {
URL url = new URL(s_url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
setConnHeader(conn, "POST", req_data);
conn.setDoOutput(true);
conn.connect();
resp_data.content = StreamTool.getUnzipStream(conn.getInputStream(),
conn.getHeaderField("Content-Encoding"), req_data.charset);
resp_data.cookie = conn.getHeaderField("Set-Cookie");
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
resp_data.err_msg = e.getMessage();
}
return resp_data;
}
//post的内容放在输出流中
public static HttpRespData postData(HttpReqData req_data) {
req_data.content_type = "application/x-www-form-urlencoded";
HttpRespData resp_data = new HttpRespData();
String s_url = req_data.url;
Log.d(TAG, "s_url=" + s_url + ", params=" + req_data.params.toString());
try {
URL url = new URL(s_url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
setConnHeader(conn, "POST", req_data);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.connect();
PrintWriter out = new PrintWriter(conn.getOutputStream());
out.print(req_data.params.toString());
out.flush();
resp_data.content = StreamTool.getUnzipStream(conn.getInputStream(),
conn.getHeaderField("Content-Encoding"), req_data.charset);
resp_data.cookie = getRespCookie(conn, req_data);
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
resp_data.err_msg = e.getMessage();
}
return resp_data;
}
//post的内容分段传输
public static HttpRespData postMultiData(HttpReqData req_data, Map<String, String> map) {
HttpRespData resp_data = new HttpRespData();
String s_url = req_data.url;
Log.d(TAG, "s_url=" + s_url);
String end = "\r\n";
String hyphens = "--";
try {
URL url = new URL(s_url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
setConnHeader(conn, "POST", req_data);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + req_data.boundary);
conn.setRequestProperty("Cache-Control", "no-cache");
conn.setDoOutput(true);
conn.setDoInput(true);
StringBuffer buffer = new StringBuffer();
Log.d(TAG, "map.size()=" + map.size());
Iterator<String> it = map.keySet().iterator();
while (it.hasNext()) {
String str = it.next();
buffer.append(hyphens + req_data.boundary + end);
buffer.append("Content-Disposition: form-data; name=\"");
buffer.append(str);
buffer.append("\"" + end + end);
buffer.append(map.get(str));
buffer.append(end);
Log.d(TAG, "key=" + str + ", value=" + map.get(str));
}
if (map.size() > 0) {
buffer.append(hyphens + req_data.boundary + end);
byte[] param_data = buffer.toString().getBytes(req_data.charset);
OutputStream out = conn.getOutputStream();
out.write(param_data);
out.flush();
}
conn.connect();
resp_data.content = StreamTool.getUnzipStream(conn.getInputStream(),
conn.getHeaderField("Content-Encoding"), req_data.charset);
resp_data.cookie = conn.getHeaderField("Set-Cookie");
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
resp_data.err_msg = e.getMessage();
}
return resp_data;
}
}
- layout/activity_http_request.xml界面布局代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/tv_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff000000"
android:textSize="18sp" />
</LinearLayout>
接着在Activity代码中启动该任务,并实现OnAddressListener接口的onFindAddress方法,即可在页面上添加详细的地址信息。
- HttpRequestActivity.java逻辑代码如下:
package com.fukaimei.mapapi;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.fukaimei.mapapi.http.tool.DateUtil;
import com.fukaimei.mapapi.task.GetAddressTask;
public class HttpRequestActivity extends AppCompatActivity implements GetAddressTask.OnAddressListener {
private final static String TAG = "HttpRequestActivity";
private TextView tv_location;
private String mLocation = "";
private LocationManager mLocationMgr;
private Criteria mCriteria = new Criteria();
private Handler mHandler = new Handler();
private boolean bLocationEnable = false;
private String mAddress = "";
// 位置监听器
private LocationListener mLocationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
setLocationText(location);
}
@Override
public void onProviderDisabled(String arg0) {
}
@Override
public void onProviderEnabled(String arg0) {
}
@Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
}
};
private Runnable mRefresh = new Runnable() {
@Override
public void run() {
if (bLocationEnable == false) {
initLocation();
mHandler.postDelayed(this, 1000);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_http_request);
// 申请定位的动态权限
locationPermissions();
initWidget();
initLocation();
mHandler.postDelayed(mRefresh, 100);
}
// 定义定位的动态权限
private void locationPermissions() {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{
android.Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{
android.Manifest.permission.ACCESS_COARSE_LOCATION}, 2);
}
}
/**
* 重写onRequestPermissionsResult方法
* 获取动态权限请求的结果,再开启定位
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initLocation();
} else {
Toast.makeText(this, "用户拒绝了权限", Toast.LENGTH_SHORT).show();
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
private void initWidget() {
tv_location = (TextView) findViewById(R.id.tv_location);
mLocationMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mCriteria.setAccuracy(Criteria.ACCURACY_FINE);
mCriteria.setAltitudeRequired(true);
mCriteria.setBearingRequired(true);
mCriteria.setCostAllowed(true);
mCriteria.setPowerRequirement(Criteria.POWER_LOW);
}
private void initLocation() {
String bestProvider = mLocationMgr.getBestProvider(mCriteria, true);
if (bestProvider == null) {
bestProvider = LocationManager.NETWORK_PROVIDER;
}
if (mLocationMgr.isProviderEnabled(bestProvider)) {
tv_location.setText("正在获取" + bestProvider + "定位对象");
mLocation = String.format("当前定位类型:%s", bestProvider);
beginLocation(bestProvider);
bLocationEnable = true;
} else {
tv_location.setText("\n" + bestProvider + "定位不可用");
bLocationEnable = false;
}
}
@Override
public void onFindAddress(String address) {
mAddress = address;
}
@SuppressLint("DefaultLocale")
private void setLocationText(Location location) {
if (location != null) {
String desc = String.format("%s" + "\n定位时间:%s" + "\n经度:%f,纬度:%f" +
"\n海拔高度:%d米,定位精度:%d米" + "\n定位地址:%s",
mLocation, DateUtil.getNowDateTime("yyyy/MM/dd HH:mm:ss"),
location.getLongitude(), location.getLatitude(),
Math.round(location.getAltitude()), Math.round(location.getAccuracy()), mAddress);
Log.d(TAG, desc);
tv_location.setText(desc);
GetAddressTask addressTask = new GetAddressTask();
addressTask.setOnAddressListener(this);
addressTask.execute(location);
} else {
tv_location.setText(mLocation + "\n暂未获取到定位对象");
}
}
private void beginLocation(String method) {
mLocationMgr.requestLocationUpdates(method, 300, 0, mLocationListener);
Location location = mLocationMgr.getLastKnownLocation(method);
setLocationText(location);
}
@Override
protected void onDestroy() {
if (mLocationMgr != null) {
mLocationMgr.removeUpdates(mLocationListener);
}
super.onDestroy();
}
}
- 注意:由于该程序需要到定位和访问互联网,因此还需要在清单文件AndroidManifest.xml文件中授权定位、访问互联网以及查看网络状态的权限:
<!-- 互联网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 查看网络状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 定位 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- Demo程序运行效果界面截图如下: