Android studio 百度地图开发(5)查询周边服务(加油站)
email:chentravelling@163.com
开发环境:win7 64位,Android Studio,请注意是Android Studio,使用的导航SDK版本:3.1.0。
- 百度地图应用(1):Android studio 百度地图开发(1)配置工程、显示地图
- 百度地图应用(2):Android studio 百度地图开发(2)地图定位
- 百度地图应用(3):Android studio 百度地图开发(3)地图导航
- 百度地图应用(4):Android studio 百度地图开发(4)触摸选点、地理编码
一.功能描述
大致功能为:通过百度地图定位获得当前位置,然后通过加油站数据接口(https://www.juhe.cn/docs/api/id/7)搜索周边的加油站信息,并在地图上显示。这里,我把访问加油站数据接口的部分放在了服务器上,因为后期还有一些其他功能,不可能都放在手机端去实现。
二.实现部分
1.加油站数据接口
需要先申请api_key,很简单,没有百度地图的API_KEY那么复杂,地址:https://www.juhe.cn/docs/api/id/7。
2.搭建服务器
这部分,本人不知道怎么去描述,因为有web基础,使用过SSM(spring+springMVC+Mybatis)架构,所以还是继续使用SSM。关于SSM架构的服务器环境搭建,资料很多,就不赘述了,而且,主要功能并不在于服务器上,换一个架构甚至“没有架构”都行。
在服务器端主要要实现对加油站数据的访问,按照官方的API说明,通过以下代码实现了数据访问:
package com.intveh.component.gas.controller;使用一下数据访问服务器即可查看获得的加油站信息:
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.intveh.component.car.model.Car;
@Controller
public class GasController {
public static final String DEF_CHATSET = "UTF-8";
public static final int DEF_CONN_TIMEOUT = 30000;
public static final int DEF_READ_TIMEOUT = 30000;
public static String userAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36";
/**
* 请求接口地址
*/
public static String url = "http://apis.juhe.cn/oil/local";
/**
* AppKey
*/
public static String key = "cbee44f8a21c371***********51e8b9";//改为自己的key
/**
* 查询附近的加油站
*
* @param 经纬度
*
* @return 查询结果 json
*/
@RequestMapping(value = "/gas/searchNear")
@ResponseBody
public void searchNear(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
//response.setHeader("Access-Control-Allow-Origin", "*");
//Car car = carDAO.getCar(carId);
//return car;
String result = "";
//System.out.println("开始搜索");
/**
* 纬度
*/
String latitude = request.getParameter("latitude");
/**
* 经度
*/
String longitude = request.getParameter("longitude");
/**
* 搜索范围
*/
String radius = request.getParameter("radius")==null?"1000":request.getParameter("radius");
/**
* 页数
*/
String page = "1";
/**
* 格式
*/
String format = "1";
//System.out.println(longitude+","+latitude+","+radius+","+page+","+format);
Map params = new HashMap();
if(latitude!=null&&longitude!=null&&!"".equals(latitude)&&!"".equals(longitude))
{
params.put("lon", longitude);
params.put("lat", latitude);
params.put("r", radius);
params.put("page", page);
params.put("format",format);
params.put("key", key);
JSONObject object = new JSONObject();
//System.out.println(longitude+","+latitude+","+radius+","+page+","+format);
try
{
result =net(url, params, "GET");
object = JSONObject.fromObject(result);
if(object.getInt("error_code")==0){
System.out.println(object.get("result"));
}else{
System.out.println(object.get("error_code")+":"+object.get("reason"));
}
response.setContentType("text/json; charset=UTF-8");
response.getOutputStream().write(object.get("result").toString().getBytes("UTF-8"));
//System.out.println(object.get("result"));
} catch (Exception e) {
e.printStackTrace();
}
//response.setHeader("Access-Control-Allow-Origin", "*");
//response.reset();
//response.setContentType(String);
//response.getOutputStream().write(object.toString().getBytes("UTF-8"));
}
}
/**
*
* @param strUrl 请求地址
* @param params 请求参数
* @param method 请求方法
* @return 网络请求字符串
* @throws Exception
*/
public static String net(String strUrl, Map params,String method) throws Exception {
HttpURLConnection conn = null;
BufferedReader reader = null;
String rs = null;
try {
StringBuffer sb = new StringBuffer();
if(method==null || method.equals("GET")){
strUrl = strUrl+"?"+urlencode(params);
}
URL url = new URL(strUrl);
System.out.println("url = "+url);
conn = (HttpURLConnection) url.openConnection();
if(method==null || method.equals("GET")){
conn.setRequestMethod("GET");
}else{
conn.setRequestMethod("POST");
conn.setDoOutput(true);
}
conn.setRequestProperty("User-agent", userAgent);
conn.setUseCaches(false);
conn.setConnectTimeout(DEF_CONN_TIMEOUT);
conn.setReadTimeout(DEF_READ_TIMEOUT);
conn.setInstanceFollowRedirects(false);
conn.connect();
/* if (params!= null && method.equals("POST")) {
try (DataOutputStream out = new DataOutputStream(conn.getOutputStream())) {
out.writeBytes(urlencode(params));
}
}*/
InputStream is = conn.getInputStream();
reader = new BufferedReader(new InputStreamReader(is, DEF_CHATSET));
String strRead = null;
while ((strRead = reader.readLine()) != null) {
sb.append(strRead);
}
rs = sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
reader.close();
}
if (conn != null) {
conn.disconnect();
}
}
return rs;
}
//将map型转为请求参数型
public static String urlencode(Map<String,Object>data) {
StringBuilder sb = new StringBuilder();
for (Map.Entry i : data.entrySet()) {
try {
sb.append(i.getKey()).append("=").append(URLEncoder.encode(i.getValue()+"","UTF-8")).append("&");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return sb.toString();
}
}
3.手机端实现
(对于我)稍微有点难度的还是在手机端功能的实现。
第一步:加油站信息类GasStationModel.java
为了更加方便的传递数据,写了一个加油站信息类,对数据实体化。同时实现了Serializable接口,因为后面需要利用intent传递list<GasStationModel>时,将list强制转化成serializable。
package intvehapp.intvehapp;
import java.io.Serializable;
import java.util.Map;
/**
* Created by chen on 2016/4/2.
*/
public class GasStationModel implements Serializable {
public GasStationModel(
String area,
String fwlsmc,
String address,
String areaname,
String distance,
String exhaust,
String discount,
String lon,
String brandname,
String type,
Map<String,String> price,
Map<String,String> gastprice,
String name,
String id,
String lat,
String position)
{
this.area = area;
this.address = address;
this.areaname = areaname;
this.brandname = brandname;
this.discount = discount;
this.distance = distance;
this.exhaust = exhaust;
this.fwlsmc = fwlsmc;
this.gastprice = gastprice;
this.id = id;
this.price = price;
this.lon = lon;
this.type = type;
this.name = name;
this.lat = lat;
this.position = position;
}
/**
* 邮编
*/
private String area;
/**
* 加油卡信息
*/
private String fwlsmc;
/**
* 加油站地址
*/
private String address;
/**
* 城市区域
*/
private String areaname;
/**
* 距离?
*/
private String distance;
/**
* 尾气排放标准
*/
private String exhaust;
/**
* 是否打折加油站
*/
private String discount;
/**
* 百度地图经度
*/
private String lon;
/**
* 运营商类型
*/
private String brandname;
/**
* 加油站类型
*/
private String type;
/**
* 省控基准油价
*/
private Map<String,String> price;
/**
* 加油站油价
*/
private Map<String,String> gastprice;
/**
* 加油站名字
*/
private String name;
/**
* id
*/
private String id;
/**
* 谷歌地图坐标
*/
private String position;
/**
* 百度地图纬度
*/
private String lat;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getFwlsmc() {
return fwlsmc;
}
public void setFwlsmc(String fwlsmc) {
this.fwlsmc = fwlsmc;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getAreaname() {
return areaname;
}
public void setAreaname(String areaname) {
this.areaname = areaname;
}
public String getDistance() {
return distance;
}
public void setDistance(String distance) {
this.distance = distance;
}
public String getDiscount() {
return discount;
}
public void setDiscount(String discount) {
this.discount = discount;
}
public String getExhaust() {
return exhaust;
}
public void setExhaust(String exhaust) {
this.exhaust = exhaust;
}
public String getLon() {
return lon;
}
public void setLon(String lon) {
this.lon = lon;
}
public String getBrandname() {
return brandname;
}
public void setBrandname(String brandname) {
this.brandname = brandname;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Map<String, String> getPrice() {
return price;
}
public void setPrice(Map<String, String> price) {
this.price = price;
}
public Map<String, String> getGastprice() {
return gastprice;
}
public void setGastprice(Map<String, String> gastprice) {
this.gastprice = gastprice;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public String getLat() {
return lat;
}
public void setLat(String lat) {
this.lat = lat;
}
}
第二步:新建线程访问数据,通过handler与主线程通信。
一开始,直接在主线程中访问服务器,最终发现失败了,查阅了资料后才知道:android4.0以后的版本,主线程(UI线程)不在支持网络请求,原因大概是影响主线程,速度太慢,容易卡机,所以需要开启新的线程请求数据。
/**
* 通过handler与主线程通信
*/
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle data = msg.getData();
String err = data.getString("err");
/**
* 查询成功
*/
if("0".equals(err))
{
List<GasStationModel> listGasStation = (List<GasStationModel> )data.getSerializable("listGasStation");
Toast.makeText(BaiDuMapActivity.this, "查询成功"+listGasStation.size(), Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(BaiDuMapActivity.this, "附近没有加油站信息", Toast.LENGTH_LONG).show();
}
//Log.i("mylog", "请求结果为-->" + val);
// TODO
// UI界面的更新等相关操作
}
};
/**
* 开启一个新的线程,访问服务器
* 当有数据返回时,通过handler传递消息
*/
new Thread(new Runnable() {
@Override
public void run() {
//请求数据
List<GasStationModel> listGasStation = ConnectServer.searchNearGas(params);
//发送消息
Message message = new Message();
Bundle bundle = new Bundle();
if(listGasStation!=null)
{
bundle.putSerializable("listGasStation", (Serializable) listGasStation);
bundle.putString("err","0");//成功
}
else
bundle.putString("err","1");//失败
message.setData(bundle);
handler.sendMessage(message);
}
}).start();
第三步:ConnectServer.java访问服务器类
考虑到后期对服务器访问还会很多,所以就单独建了一个访问服务器的类。
实现对服务器的访问,搜索周边加油站。
/**
* Created by chen on 2016/4/2.
*/
public class ConnectServer {
public static List<GasStationModel> searchNearGas(Map<String, String> params)
{
/**
* 请求地址
*/
String path = serverURL+"gas/searchNear?key="+key+"&";
/**
* 添加参数
*/
List<GasStationModel> listGasStation = new ArrayList<GasStationModel>();
try {
StringBuilder url = new StringBuilder(path);
URL nURL = new URL("http://www.baidu.com");
//url.append("?");
for (Map.Entry<String, String> entry : params.entrySet()) {
url.append(entry.getKey()).append("=");
url.append(URLEncoder.encode(entry.getValue(), "UTF-8"));// 编码
url.append('&');
}
url.deleteCharAt(url.length() - 1);
HttpURLConnection connection = (HttpURLConnection) new URL(
url.toString()).openConnection();
//HttpURLConnection connection = (HttpURLConnection) nURL.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
String result = "";
InputStream in = connection.getInputStream();
try {
Thread.sleep(300);
result = read(connection.getInputStream());
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 解析JSON数据等等<span style="font-family:Microsoft YaHei;">操作</span>
*/
}
}catch (Exception e )
{
e.printStackTrace();
}
return listGasStation;
}
private static String read(InputStream in) throws Exception {
byte[] data;
int length = in.available();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[]buf = new byte[1024];
int len = 0;
while((len = in.read(buf))!=-1){
bout.write(buf, 0, len);
}
data = bout.toByteArray();
return new String(data,"UTF-8");
}
}
三.效果与分析
其实整个流程的思路还是很简单:定位获取当前数据->访问加油站数据接口获得周边加油站位置信息->回调,在地图上显示。但是,实现起来有些地方还是很费心,比如手机端接收数据不完整的情况。