相当数量的app应用和游戏,都需要用到定位功能。主要有两类实现思路:一是使用Android自带的位置服务;二是接入第三方SDK,常见的有百度地图,高德地图,腾讯地图等等;
开门见山,先说结论: 在大陆上线运营的中小开发者,不要考虑Android自带的定位,这是个浪费时间的死胡同。接入第三方SDK,免费、简单、可以快速投入使用。
有想了解详细原因的看官,剖析如下:
2 Android自带位置服务
LocationManager系统服务是位置服务的核心组件,它提供了一系列方法来处理与位置相关的问题,比如查询上一个已知位置,定期更新设备的地理位置,或者当设备进入给定地理位置附近时,触发应用指定意图等;
2.1 获取LocationManager:
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
2.2 了解LocationProvider:
LocationProvider:它是位置信息提供者,系统提供三种方式获取地理位置信息:
(1)GPS_PROVIDER: 通过 GPS芯片利用卫星来获取地理位置信息; 优点:获取地理位置信息精确度高; 缺点:只能在户外使用,获取经纬度信息耗时,耗电;
(2)NETWORK_PROVIDER: 通过移动网络的基站和Wi-Fi节点的地址来获取地理位置;这种方式依赖于将基站或WIF节点信息翻译成位置信息的服务器的能力。 通过我们到附近3个不同基站的距离,就可以确定我们的坐标,这个是数学中三位定点的原理。 优点:只要有网络,就可以快速定位,室内室外都可; 缺点:精确度不高;
(3)PASSIVE_PROVIDER: 被动接收更新地理位置信息,而不用自己请求地理位置信息。意思就是捡现成的,当其他应用使用定位更新了定位信息,系统会保存下来,当前应用被动接收到定位信息后直接读取就可以了。 换而言之,此方法依赖于其他Provider,不能独立存在;
2.3 获取LocationProvider:
(1)getProvider(指定获取某个Provider)
String provider = mLocationManager.getProvider(LocationManager.GPS_PROVIDER);
(2)getProviders(获取所有Provider)
List<String> list = locationManager.getProviders(true); if (list.contains(LocationManager.GPS_PROVIDER)) { Log.i(TAG, "为GPS位置控制器"); } else if (list.contains(LocationManager.NETWORK_PROVIDER)) { Log.i(TAG, "为网络位置控制器"); } else if (list.contains(LocationManager.PASSIVE_PROVIDER)) { Log.i(TAG, "为被动位置控制器"); } else { Log.i(TAG, "其他"); }
(3)getBestProvider(根据一组条件来返回合适的Provider)
Criteria c = new Criteria();//Criteria类是设置定位的标准信息(系统会根据你的要求,匹配最适合你的定位供应商),一个定位的辅助信息的类 c.setPowerRequirement(Criteria.POWER_LOW);//设置低耗电 c.setAltitudeRequired(true);//设置需要海拔 c.setBearingAccuracy(Criteria.ACCURACY_LOW);//设置COARSE精度标准 c.setAccuracy(Criteria.ACCURACY_FINE);//设置低精度 //... Criteria 还有其他属性,就不一一介绍了 String provider = locationManager.getBestProvider(c, false); if (TextUtils.isEmpty(provider)) { Log.i(TAG, "找不到最适合的定位"); }else{ Log.i(TAG, "找到最适合的定位"); }
2.4 监听
LocationListener locationListener = new LocationListener() { @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public void onProviderEnabled(String provider) { } @Override public void onProviderDisabled(String provider) { } @Override public void onLocationChanged(Location location) { longitude = location.getLongitude(); latitude = location.getLatitude(); Log.d(TAG,"Location longitude:"+ longitude +" latitude: "+ latitude ); } };
2.5 获取位置
Location location = mLocationManager.getLastKnownLocation(serviceProvider);
或者
Location location = mLocationManager.getLocation(serviceProvider);
2.6 请求更新位置
mLocationManager.requestLocationUpdates(serviceProvider, 10000, 1, locationListener);
3 使用自带定位服务会遇到的问题举例:
常见问题1 LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)返回结果恒为false
原因分析: 大陆绝大多数手机厂商制造的手机,由于政策限制,有所裁剪,比如裁剪了一些google服务,所以官方的location manager库缺少network组件,大陆网络也不允许有服务器来做这个事情,所以总是返回false。
常见问题2 getLocation返回的location永远为NULL
网上有解决方案说,一开始location是很有可能是NULL的,这是因为程序还从来没有请求 过,只需要注册监听器,然后固定时间间隔一直请求更新location,就可以在onLocationChanged中接收更新后的location信息。其实也是走不通的,你会发现永远不会进入onLocationChanged。
原因分析: 这种情况都是发生在室内,因为GPS无法获取位置,而由于问题1的原因,NETWORK_PROVIDER无法正常工作,所以是获取不到位置的,只有一直返回NULL。
常见问题3 requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 1, locationListener); 永远失败。
在室内, requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 1, locationListener); 永远失败。
而换成requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 1, locationListener); 则可以成功。
这是因为GPS_PROVIDER在室内是无法定位的,在室外才能定位,在室内只能依赖NETWORK_PROVIDER。
这其实是个很多人都知道的常识,但是在办公室里做开发的时候,往往没有反应过来。
4 做出选择
综上所述,如果我们要使用安卓自带的位置服务,则必须接受:
1 要求用户必须打开GPS信号开关;
GPS信号比较耗电,用户也懒的操作打开GPS,又操作授权定位权限。
2 室内定位不准;
相信大多数用户在室内的时间都比在室外的时间长。
3 定位sdk目前多数有免费政策;
百度地图:http://lbsyun.baidu.com/products/location
高德地图:https://developer.amap.com/product/locate
腾讯地图:https://lbs.qq.com/location/
附:上述结论只适用于目前的国内市场,不契合海外市场的情况。
评论