网站客服:zx81024163 |

来看看Android获取定位到底有几种方式?

2023-08-29 23:00:56发布    浏览637次    信息编号:71433

友情提醒:凡是以各种理由向你收取费用,均有骗子嫌疑,请提高警惕,不要轻易支付。

来看看Android获取定位到底有几种方式?

前言

目前,移动终端一般采用三种方式进行设备定位:GPS、基站、wifi。 本文将详细讲解这些定位方法和实现方法。

其中,我们一般用它们来获取位置信息,有四种:

最终=“”; 最终=“全球定位系统”; 最终=“”; 最终=“融合”​​;

区别如下:

(1):通过GPS获取地理位置的经纬度信息; 优点:获取地理位置信息的准确性高; 缺点:只能在室外使用,获取经纬度信息耗时耗电;

(2):通过移动网络或Wi-Fi的基站获取地理位置; 优点:只要有网络,就可以快速定位,室内室外; 缺点:准确度不高;

(3):被动接收更新的地理位置信息,而不自行请求地理位置信息。

返回的位置是由其他人生成的,您可以通过query()方法来确定位置更新的来源,这需要权限,但如果没有启用GPS,这可能只能返回粗略的位置匹配;

(4):这个已经废弃了,现在又用了(即api 31),但是依赖GMS,所以国内暂时不可用。

我们通常使用gps和这两种方式。

GPS定位

这个是最常用的,可以获取最后的位置,也可以监听变化,代码如下:

需要许可

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

GPS定位

var locManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
var loc = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
if(loc != null){
    Log.e("gpslocation", loc.toString())
    toast(loc.toString())
}
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0F, object : LocationListener{
    override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
    }
    override fun onProviderEnabled(provider: String?) {
    }
    override fun onProviderDisabled(provider: String?) {
    }
    override fun onLocationChanged(location: Location?) {
        Log.e("gpslocation", location.toString())
        toast(location.toString())
    }
})

位置

与GPS定位代码基本相同,但将改为.

AGPS定位

其实以上两种定位是结合起来的,具体原则如下:

AGPS手机首先通过网络将自己的基站地址传输到定位服务器;

位置服务器根据手机的大致位置,向手机发送与位置相关的GPS辅助信息(包括GPS星历、方位角和俯仰角等);

手机AGPS模块根据辅助信息接收原始GPS信号(提高GPS信号首次锁定时间的TTFF能力);

手机接收到原始GPS信号后,解调信号,计算出手机到卫星的伪距(伪距是受各种GPS误差影响的距离),并将相关信息传输到定位服务器通过网络;

位置服务器根据传入的GPS伪距信息和其他定位设备(如差分GPS参考站等)的辅助信息,完成对GPS信息的处理,估计出手机的位置;

位置服务器将手机的位置通过网络传输至定位网关或应用平台。

我的理解是通过网络定位和定位服务器来判断最好的卫星,这样就减少了获取卫星信号的时间。 由于快速获得网络位置,因此可以减少整体定位时间。

AGPS不是一种定位方法,而是一种优化方案。 代码与GPS相同,但在设置中将定位模式设置为AGPS。

其他方法

以上是内置的定位方法,我们还可以获取一些原始信息(如基站信息、wifi信息),通过公共接口获取位置信息。 以下方法是利用原始信息通过API获取位置信息。

基站定位

我们可以获取基站信息,然后通过相关的api接口获取经纬度,但是基站的定位精度很差。

基站信息包括以下内容:

得到这些信息后,我们可以通过一些公共API服务来获取经纬度,如下:

是的,post请求似乎被禁用了

目前可用且免费

例子:

:82/细胞/?mcc=4…

返回:

{"errcode":0, "lat":"40.00598145", "lon":"116.48539734", "radius":"937", "address":"北京市朝阳区来广营地区东湖渠;溪阳东路与屏翠东路路口东70米"}

接口参数:

名称 类型 必填 描述

MCC

整数

是的

mcc国家代码:中国代码460

跨国公司

整数

是的

mnc网络类型:0移动、1联通(电信对应sid),十进制

紫胶

整数

是的

lac(电信对应nid),十进制

词

整数

是的

(电信对应bid),小数

坐标

不

坐标类型(wgs84/gcj02/bd09),默认wgs84

不

返回格式(csv/json/xml),默认csv

另外,提供此类接口和数据的平台也有很多,自己搜索一下即可

代码如下所示:

需要许可

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_CARSE_LOCATION"/>

基站定位,这里只实现了GSM,CDMA略有不同

val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
if(telManager.cellLocation is GsmCellLocation) {
    val cellLoc = telManager.cellLocation as GsmCellLocation
    if (cellLoc != null) {
        val operator = telManager.networkOperator
        val mcc = operator.substring(0, 3).toInt()
        val mnc = operator.substring(3).toInt()
        val cid = cellLoc.cid
        val lac = cellLoc.lac
        ...
        doAsync {
            val result = URL(sb.toString()).readText()
            Log.e("tellocation", result)
        }
    }
}

无线网络位置

wifi定位是通过获取wifi信息来实现的,主要是wifi的BSSID(即mac地址)。然后通过一些API查询经纬度,比如

例子:

:82/wifi/?mac=0...

返回:

{"errcode":0, "lat":"39.950008", "lon":"116.230049", "radius":"222", "address":"北京市海淀区四季青镇益园文创基地c区9号楼;南平庄中路与西平庄路路口西北561米"}

接口参数:

名称 类型 必填 描述

苹果

整数

是的

WIFI热点的MAC地址(BSSID)

坐标

不

坐标类型(wgs84/gcj02/bd09),默认wgs84

不

返回格式(csv/json/xml),默认csv

代码如下所示:

需要许可

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

wifi定位

val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
if(wifiManager.isWifiEnabled) {
    var mac = wifiManager.connectionInfo.bssid
    if(TextUtils.isEmpty(mac)) {
        /**
         * 当未链接wifi时,可以使用扫描到的wifi列表中找一个信号强度最好的。这里没进行比较,直接使用第一个了
         * ScanReuslt有三个字段比较重要:SSID是wifi名称,BSSID是wifi的mac,level则是信号强度(负数)
         * 注意结果中同一个SSID可能会有多个,如果需要链接wifi可以通过信号强度过滤出最好的来链接
         */
        val scanlist = wifiManager.scanResults
        for(info in scanlist){
            Log.e("wifiinfo", info.toString())
        }
        if(scanlist.size > 0) {
            mac = wifiManager.scanResults[0].BSSID
        }
    }
    if(!TextUtils.isEmpty(mac)) {
        ...
        doAsync {
            val result = URL(sb.toString()).readText()
            Log.e("wifilocation", result)
        }
    }
}

混合定位

混合定位就是获取附近wifi列表信息(包括信号强度)和附近基站列表信息(包括信号强度),并通过一些API获取经纬度。

这种方法比单基站和wifi定位更准确。

获取附近wifi列表在WIFI定位中已经提到过。 该函数用于获取扫描到的wifi列表,其中level为信号强度,可能需要去重。

获取附近基站的列表有些问题。

官方提供了通过fo函数获取的方式,其中mRssi为信号强度。

需要许可

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

获取附近基站信息

val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val neighborCells = telManager.neighboringCellInfo
for(cell in neighborCells){
    //这里将rssi转化为dBm
    val level = -131 + 2 * cell.rssi
    Log.e("neighboringCellInfo", "cid:${cell.cid} lac:${cell.lac} rssi:$level")
}

但在实际使用中,发现cid和lac都为-1。

检验施工方法如下:

  public NeighboringCellInfo(int rssi, String location, int radioType) {
        // set default value
        mRssi = rssi;
        mNetworkType = NETWORK_TYPE_UNKNOWN;
        mPsc = UNKNOWN_CID;
        mLac = UNKNOWN_CID;
        mCid = UNKNOWN_CID;
        // pad location string with leading "0"
        int l = location.length();
        if (l > 8) return;
        if (l < 8) {
            for (int i = 0; i < (8-l); i++) {
                location = "0" + location;
            }
        }
        // TODO - handle LTE and eHRPD (or find they can't be supported)
        try {// set LAC/CID or PSC based on radioType
            switch (radioType) {
                case NETWORK_TYPE_GPRS:
                case NETWORK_TYPE_EDGE:
                    mNetworkType = radioType;
                    // check if 0xFFFFFFFF for UNKNOWN_CID
                    if (!location.equalsIgnoreCase("FFFFFFFF")) {
                        mCid = Integer.parseInt(location.substring(4), 16);
                        mLac = Integer.parseInt(location.substring(0, 4), 16);
                    }
                    break;
                case NETWORK_TYPE_UMTS:
                case NETWORK_TYPE_HSDPA:
                case NETWORK_TYPE_HSUPA:
                case NETWORK_TYPE_HSPA:
                    mNetworkType = radioType;
                    mPsc = Integer.parseInt(location, 16);
                    break;
            }
        } catch (NumberFormatException e) {
            // parsing location error
            mPsc = UNKNOWN_CID;
            mLac = UNKNOWN_CID;
            mCid = UNKNOWN_CID;
            mNetworkType = NETWORK_TYPE_UNKNOWN;
        }
    }

可以看到只处理了GPRS和EDGE网络,而3G和4G网络都是-1。 表示该方法不受支持且已过时。

官方还有另外一种方式,是通过函数获取。该函数要求你必须年满17岁以上

需要许可

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

获取基站信息,这里只处理LTE网络

val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val cells = telManager.allCellInfo
for(cell in cells){
    if(cell is CellInfoLte) {
        Log.e("celllist", "cid:${cell.cellIdentity.ci} lac:${cell.cellIdentity.tac} dbm:${cell.cellSignalStrength.dbm} isRegistered:${cell.isRegistered}")
    }
}

得到的信息如下:

E/: cid: lac: 4154 dbm: -87 : 真

E/: cid: lac: dbm:-101 : 假

E/: cid: lac: dbm:-102 : 假

E/: cid: lac: dbm:-108 : 假

E/: cid: lac: dbm:-101 : 假

第一个是我们正在使用的基站,可以看到正常返回信息,其余返回默认信息(.)

说明该方法只能获取当前使用的基站的信息。 而且根据网上的说法,当使用2G网络时,结果是NULL。

这样,目前还没有更好的方法来获取多个基站的信息。

当我们获取到附近的基站信息和wifi信息后,我们可以通过提供的混合定位接口查询位置信息,与上面类似,这里不再赘述。

总结

一般情况下,我们可以利用系统提供的位置信息来获取位置信息,方便简单。 如果我们有自己的基站或者wifi信息库,我们还可以通过接口获取相关源信息,实现个性化服务。

提醒:请联系我时一定说明是从星空网上看到的!