在Android操作系统下,基站定位其实很简单,先说一下实现流程:
调用SDK中的API(TelephonyManager)获得MCC、MNC、LAC、CID等信息,然后通过google的API获得所在位置的经纬度,最后再通过google map的API获得实际的地理位置。(google真牛!)
有同学会问:MNC、MCC、LAC、CID都是些什么东西?google又怎么通过这些东西就获得经纬度了呢?
我们一起来学习一下:
MCC,Mobile Country Code,移动国家代码(中国的为460);
MNC,Mobile Network Code,移动网络号码(中国移动为00,中国联通为01);
LAC,Location Area Code,位置区域码;
CID,Cell Identity,基站编号,是个16位的数据(范围是0到65535)。
了解了这几个名词的意思,相信有些朋友已经知道后面的事了:google存储了这些信息,直接查询就能得到经纬度了。(至于google怎么得到移动、联通的基站信息,这就不得而知了,反正google免费提供接口,直接调用就是)
package com.android.demo;
import java.io.BufferedReader; import java.io.InputStreamReader; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONObject; import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.os.Bundle; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.view.View.OnClickListener; public class DemoActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); /** 为按钮绑定事件 */ Button btnGetLocation = (Button) findViewById(R.id.button1); btnGetLocation.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub onBtnClick(); } }); } /** 基站信息结构体 */ public class SCell{ public int MCC; public int MNC; public int LAC; public int CID; } /** 经纬度信息结构体 */ public class SItude{ public String latitude; public String longitude; } /** 按钮点击回调函数 */ private void onBtnClick() { /** 弹出一个等待状态的框 */ ProgressDialog mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage("正在获取中..."); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); mProgressDialog.show(); try { /** 获取基站数据 */ SCell cell = getCellInfo(); /** 根据基站数据获取经纬度 */ SItude itude = getItude(cell); /** 获取地理位置 */ String location = getLocation(itude); /** 显示结果 */ showResult(cell, location); /** 关闭对话框 */ mProgressDialog.dismiss(); } catch (Exception e) { /** 关闭对话框 */ mProgressDialog.dismiss(); /** 显示错误 */ TextView cellText = (TextView) findViewById(R.id.cellText); cellText.setText(e.getMessage()); Log.e("Error", e.getMessage()); } } /** * 获取基站信息 * * @throws Exception */ private SCell getCellInfo() throws Exception { SCell cell = new SCell(); /** 调用API获取基站信息 */ TelephonyManager mTelNet = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); GsmCellLocation location = (GsmCellLocation) mTelNet.getCellLocation(); if (location == null) throw new Exception("获取基站信息失败"); String operator = mTelNet.getNetworkOperator(); int mcc = Integer.parseInt(operator.substring(0, 3)); int mnc = Integer.parseInt(operator.substring(3)); int cid = location.getCid(); int lac = location.getLac(); /** 将获得的数据放到结构体中 */ cell.MCC = mcc; cell.MNC = mnc; cell.LAC = lac; cell.CID = cid; return cell; } /** * 获取经纬度 * * @throws Exception */ private SItude getItude(SCell cell) throws Exception { SItude itude = new SItude(); /** 采用Android默认的HttpClient */ HttpClient client = new DefaultHttpClient(); /** 采用POST方法 */ HttpPost post = new HttpPost("http://www.google.com/loc/json"); try { /** 构造POST的JSON数据 */ JSONObject holder = new JSONObject(); holder.put("version", "1.1.0"); holder.put("host", "maps.google.com"); holder.put("address_language", "zh_CN"); holder.put("request_address", true); holder.put("radio_type", "gsm"); holder.put("carrier", "HTC"); JSONObject tower = new JSONObject(); tower.put("mobile_country_code", cell.MCC); tower.put("mobile_network_code", cell.MNC); tower.put("cell_id", cell.CID); tower.put("location_area_code", cell.LAC); JSONArray towerarray = new JSONArray(); towerarray.put(tower); holder.put("cell_towers", towerarray); StringEntity query = new StringEntity(holder.toString()); post.setEntity(query); /** 发出POST数据并获取返回数据 */ HttpResponse response = client.execute(post); HttpEntity entity = response.getEntity(); BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent())); StringBuffer strBuff = new StringBuffer(); String result = null; while ((result = buffReader.readLine()) != null) { strBuff.append(result); } /** 解析返回的JSON数据获得经纬度 */ JSONObject json = new JSONObject(strBuff.toString()); JSONObject subjosn = new JSONObject(json.getString("location")); itude.latitude = subjosn.getString("latitude"); itude.longitude = subjosn.getString("longitude"); Log.i("Itude", itude.latitude + itude.longitude); } catch (Exception e) { Log.e(e.getMessage(), e.toString()); throw new Exception("获取经纬度出现错误:"+e.getMessage()); } finally{ post.abort(); client = null; } return itude; } /** * 获取地理位置 * * @throws Exception */ private String getLocation(SItude itude) throws Exception { String resultString = ""; /** 这里采用get方法,直接将参数加到URL上 */ String urlString = String.format("http://maps.google.cn/maps/geo?key=abcdefg&q=%s,%s", itude.latitude, itude.longitude); Log.i("URL", urlString); /** 新建HttpClient */ HttpClient client = new DefaultHttpClient(); /** 采用GET方法 */ HttpGet get = new HttpGet(urlString); try { /** 发起GET请求并获得返回数据 */ HttpResponse response = client.execute(get); HttpEntity entity = response.getEntity(); BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent())); StringBuffer strBuff = new StringBuffer(); String result = null; while ((result = buffReader.readLine()) != null) { strBuff.append(result); } resultString = strBuff.toString(); /** 解析JSON数据,获得物理地址 */ if (resultString != null && resultString.length() > 0) { JSONObject jsonobject = new JSONObject(resultString); JSONArray jsonArray = new JSONArray(jsonobject.get("Placemark").toString()); resultString = ""; for (int i = 0; i < jsonArray.length(); i++) { resultString = jsonArray.getJSONObject(i).getString("address"); } } } catch (Exception e) { throw new Exception("获取物理位置出现错误:" + e.getMessage()); } finally { get.abort(); client = null; } return resultString; } /** 显示结果 */ private void showResult(SCell cell, String location) { TextView cellText = (TextView) findViewById(R.id.cellText); cellText.setText(String.format("基站信息:mcc:%d, mnc:%d, lac:%d, cid:%d", cell.MCC, cell.MNC, cell.LAC, cell.CID)); TextView locationText = (TextView) findViewById(R.id.lacationText); locationText.setText("物理位置:" + location); } }
打开AndroidManifest.xml配置文件,在里面添加相应的配置信息:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
可能有的同学还是出现错误,没有成功:
█ 提示“www.google.com…”什么的错误
请确认你的手机能访问互联网,调用google的API是必须联网的。
█ 提示获取不到基站信息
你确定你是在手机上测试的吗?模拟器可不行哦。或者你的手机使用的CMDA网络?这个例子只支持GSM网络…
█ 获取不到经纬度
很有可能你中奖了,你所在的基站还没纳入google的数据库…(话说我之前也遇到过,怎么查就是查不出经纬度来,返回数据为空)
█ 获取到的地理地址不正确
这个可能程序出错了,可能google出错了?
其实google map API返回的数据中还包含了很多其他信息,我们可以用来开发一些更有趣的功能,如制作我们专属的地图软件、足迹记录软件等,充分发挥你的创造力:)
这个程序基本实现了基站定位功能,但还有很多问题,如:点击了按钮后界面会卡住(访问网络时阻塞了进程)、未对异常进一步处理、不兼容CMDA网络等。
另外这个程序的精度也不够,获得的位置实际上是基站的物理位置,与人所在的位置还有一定差距。在城市里面,一般采用密集型的小功率基站,精度一般在几百米范围内,而在郊区常为大功率基站,密度很小,精度一般在几千米以上。
关于google 基站信息API的官方说明---->google api
通过基站的基本信息,通过Google Gears获取对应的GPS经纬度。
private Location callGear(ArrayList cellID) { if (cellID == null) return null; DefaultHttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost( "http://www.google.com/loc/json"); JSONObject holder = new JSONObject(); try { holder.put("version", "1.1.0"); holder.put("host", "maps.google.com"); holder.put("home_mobile_country_code", cellID.get(0).mobileCountryCode); holder.put("home_mobile_network_code", cellID.get(0).mobileNetworkCode); holder.put("radio_type", cellID.get(0).radioType); holder.put("request_address", true); if ("460".equals(cellID.get(0).mobileCountryCode)) holder.put("address_language", "zh_CN"); else holder.put("address_language", "en_US"); JSONObject data,current_data; JSONArray array = new JSONArray(); current_data = new JSONObject(); current_data.put("cell_id", cellID.get(0).cellId); current_data.put("location_area_code", cellID.get(0).locationAreaCode); current_data.put("mobile_country_code", cellID.get(0).mobileCountryCode); current_data.put("mobile_network_code", cellID.get(0).mobileNetworkCode); current_data.put("age", 0); array.put(current_data); if (cellID.size() > 2) { for (int i = 1; i < cellID.size(); i++) { data = new JSONObject(); data.put("cell_id", cellID.get(i).cellId); data.put("location_area_code", cellID.get(i).locationAreaCode); data.put("mobile_country_code", cellID.get(i).mobileCountryCode); data.put("mobile_network_code", cellID.get(i).mobileNetworkCode); data.put("age", 0); array.put(data); } } holder.put("cell_towers", array); StringEntity se = new StringEntity(holder.toString()); Log.e("Location send", holder.toString()); post.setEntity(se); HttpResponse resp = client.execute(post); HttpEntity entity = resp.getEntity(); BufferedReader br = new BufferedReader( new InputStreamReader(entity.getContent())); StringBuffer sb = new StringBuffer(); String result = br.readLine(); while (result != null) { Log.e("Locaiton receive", result); sb.append(result); result = br.readLine(); } if(sb.length() return null; data = new JSONObject(sb.toString()); data = (JSONObject) data.get("location"); Location loc = new Location(LocationManager.NETWORK_PROVIDER); loc.setLatitude((Double) data.get("latitude")); loc.setLongitude((Double) data.get("longitude")); loc.setAccuracy(Float.parseFloat(data.get("accuracy").toString())); loc.setTime(GetUTCTime()); return loc; } catch (JSONException e) { return null; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; }
通过Google Map API根据GPS经纬度获取当前位置。
private String getLocation(Location itude) throws Exception { String resultString = ""; /** 这里采用get方法,直接将参数加到URL上 */ String urlString = String.format("http://maps.google.cn/maps/geo?key=abcdefg&q=%s,%s", itude.getLatitude(), itude.getLongitude()); Log.i("URL", urlString); /** 新建HttpClient */ HttpClient client = new DefaultHttpClient(); /** 采用GET方法 */ HttpGet get = new HttpGet(urlString); try { /** 发起GET请求并获得返回数据 */ HttpResponse response = client.execute(get); HttpEntity entity = response.getEntity(); BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent())); StringBuffer strBuff = new StringBuffer(); String result = null; while ((result = buffReader.readLine()) != null) { strBuff.append(result); } resultString = strBuff.toString(); /** 解析JSON数据,获得物理地址 */ if (resultString != null && resultString.length() > 0) { JSONObject jsonobject = new JSONObject(resultString); JSONArray jsonArray = new JSONArray(jsonobject.get("Placemark").toString()); resultString = ""; for (int i = 0; i < jsonArray.length(); i++) { resultString = jsonArray.getJSONObject(i).getString("address"); } } } catch (Exception e) { throw new Exception("获取物理位置出现错误:" + e.getMessage()); } finally { get.abort(); client = null; } return resultString; }