文/Htea(简书作者) 原文链接:http://www.jianshu.com/p/02c7508b2d1f
本篇主要介绍android基站定位的基本思路,解释了下用到的TelephonyManager的一些方法。最后介绍了google定位服务与android提供参数之间的联系。
通过手机信号获取基站信息,然后调用第三方服务的根据基站信息查找基站的经纬度值。
尽管根据基站网络制式不同(cdma或gsm)所获取的基站信息也不一样,但一般用谷歌等这样的第三方定位服务需要获得这些基站信息:
MCC,Mobile Country Code,移动国家代码(中国的为460)。
MNC,Mobile Network Code,移动网络号码(中国移动为0,中国联通为1,中国电信为2)。
LAC,Location Area Code,位置区域码。
CID,Cell Identity,基站编号。
BSSS,Base station signal strength,基站信号强度。
从系统服务中获取TelephonyManager:
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
TelephonyManager#getNetworkOperator方法获取目前注册网络MCC+MNC信息,一般是5-6位的字符串,前3位为MCC,后面的是MNC。
用户必须在该网络注册才能获取到信息,对于cdma网络而言可能会不靠谱,因此用TelephonyManager#getPhoneType来判断手机支持的网络制式。
String operator = telephonyManager.getNetworkOperator(); String mcc = operator.substring(0, 3); String mnc = operator.substring(3);
TelephonyManager#getSimOperator方法获取Sim卡的MCC+MNC信息
SM卡状态必须处于SIM_STATE_READY,用TelephonyManager#getSimState判断Sim卡状态。
TelephonyManager#getPhoneType 获取手机支持网络制式
一般就GSM、CDMA两种,如果没有获取到则是NONE。
TelephonyManager#getNetWorkType 获取网络类型
用以区分移动2g,电信4g等更具体的网络。
由于电信用的是cdma制式网络,移动和联通用的gsm网络,这两种网络基站信息封装类在android中是不同的,cdma要用CdmaCellLocation,gsm要用GsmCellLocation。
从TelephonManager获取基站定位信息CellLocation,其中封装了需要的CID和LAC等信息。
if(telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA){ CdmaCellLocation cdmaCellLocation = (CdmaCellLocation) telephonyManager.getCellLocation(); int cid = cdmaCellLocation.getBaseStationId(); //获取cdma基站识别标号 BID int lac = cdmaCellLocation.getNetworkId(); //获取cdma网络编号NID int sid = cdmaCellLocation.getSystemId(); //用谷歌API的话cdma网络的mnc要用这个getSystemId()取得→SID }else{ GsmCellLocation gsmCellLocation = (GsmCellLocation) telephonyManager.getCellLocation(); int cid = gsmCellLocation.getCid(); //获取gsm基站识别标号 int lac = gsmCellLocation.getLac(); //获取gsm网络编号 }
每个基站信息封装在具体CellInfo子类中,有CellInfoCdma、CellInfoGsm等。CellInfoGsm中又封装了2个函数,分别用来返回基站识别信息(CellIdentityGsm类)和基站信号强度信息(CellSignalStrengthGsm类)。
TelephonyManager#getAllCellInfo方法返回所有能检测到的基站信息(包括连接的基站信息),而且返回的信息更详细。而TelephonyManager#getNeighboringCellInfo方法不包括连接的基站信息,返回基站信息是经过处理的,不加以cdma或gsm区分。一般用getAllCellInfo方法。
List<CellInfo> infoLists = telephonyManager.getAllCellInfo(); for (CellInfo info : infoLists) { CellInfoCdma cellInfoCdma = (CellInfoCdma) info; CellIdentityCdma cellIdentityCdma = cellInfoCdma.getCellIdentity(); CellSignalStrengthCdma cellSignalStrengthCdma = cellInfoCdma.getCellSignalStrength(); int strength = cellSignalStrengthCdma.getCdmaDbm(); int cid = cellIdentityCdma.getBasestationId();// 处理 strength和id数据 }
CellSignalStrengthCdma类中封装了各种信号处理方法,用来返回不同标准的信号强度。CellIdentityCdma类封装了Cdma基站特有的识别信息,比如能通过CellIdentityCdma#getLongitude()获取Cdma基站的经纬度。CellIdentityGsm类封装Gsm基站信息,能获取MCC、MNC、CID和LAC。
PhoneStateListener类是一个监听类,重写其中方法实现对基站信息变化的监听。
private PhoneStateListener phoneStateListener; //定义监听器 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION); //注册监听器,设定不同的监听类型//设置监听器方法 private void setPhoneStateListener(){ phoneStateListener = new PhoneStateListener(){ @Override public void onCellLocationChanged(CellLocation location){} @Override public void onSignalStrengthsChanged(SignalStrength signalStrength) {} @Override public void onCellInfoChanged(List<CellInfo> cellInfo) {} } }
这里主要用到的三个回调函数:
onCellLocationChanged 当cellLocation变化时会调用,传入cellLocation类型,需要根据具体的网络制式向下转型(CdmaCellLocation或GsmCellLocation)。
onSignalStrengthsChanged 传入的是SignalStrength类型,这个类型和CellStrength类没关系,是一个重新写的类。其中有自定义的一些方法返回信号强度,有getCdmaDbm()、getGsmSignalStrength()。Coma和gsm类型都封装在一起,需要调用额外的方法isGsm()。
onCellInfoChanged 传入参数为CellInfo,维护一个CellInfo列表,有检测到的基站变动都会调用。方法跟TelephonyManager#getAllCellInfo()的使用类似。
Google Maps Geolocation API
Android参考文档 TelephonyManager