浅析Android地理定位(GPS)(1)

LocationManager 提供的位置提供器,默认为PASSIVE_PROVIDER,这是一个特殊的位置提供器,用来被动的接收位置信息,这个位置信息是由其他的服务提供的位置信息,而不是自己主动请求的,相当于共享了一个位置服务信息,不常用。使用比较多的还是GPS和网络定位,这两种定位方式各有特点,GPS定位精度高,但是比较耗电,而且室内很难获取到GPS经纬度。而网络定位虽然精度稍低,但耗电量比较小,而且在室内室外效果都很不错。

lm = (LocationManager) mContext.getApplicationContext()

.getSystemService(Context.LOCATION_SERVICE);

// 检查是否有相关定位权限

if(!Helper.checkPermission(mContext.getApplicationContext())){

mListener.onFail(“location no permission”);

return;

}

// 如果不传配置类,则按默认配置

if(mSiLoOption == null)

mSiLoOption = new SiLoOption();

mProvider = mSiLoOption.isGpsFirst ? Helper.getGPSProvider(lm) : Helper.getNetWorkProvider(lm);

if(mProvider == null)

mListener.onFail(“location provider no exist”);

else

getLocation(mProvider);

获取gps位置提供器的代码片段:

/**

  • gps 提供器

  • @param locationManager

  • @return

*/

public static String getGPSProvider(LocationManager locationManager) {

List prodiverlist = locationManager.getProviders(true);

// gps定位

if(prodiverlist.contains(LocationManager.GPS_PROVIDER))

return LocationManager.GPS_PROVIDER;

return null;

}

requestLocationUpdates


获取位置提供器成功后,就可以通过requestLocationUpdates 方法来请求经纬度了

下面是逻辑代码片段:

@Override

public void onLocationChanged(Location location) {

if(mProvider == null)

return;

latlng = location != null ? ConverHelper.loConverToLatlng(location) : latlng;

mListener.onSuccess(latlng);

if(mSiLoOption.isGpsFirst){

if(location != null){

if(mProvider == LocationManager.GPS_PROVIDER)

mCounter = 0;

else{

mCounter += mSiLoOption.time/1000;

if(mCounter > NET_TIME_SPACE){

mProvider = getLocation(Helper.getGPSProvider(lm));

mCounter = 0;

}

}

}else{

// gps无信号时,尝试network获取

mCounter += mSiLoOption.time/1000;

if(mCounter >= GPS_TIME_SPACE && mCounter < NET_TIME_SPACE){

mProvider = getLocation(Helper.getNetWorkProvider(lm));

}else if(mCounter > NET_TIME_SPACE){

// 只要最后一个net点失败,就抛出回调;前两个点,由于开始,可能为空,不做强制判断处理

mListener.onFail("location latlng = null , provider = "+ mProvider);

}

}

}else{

// 当network获取不到定位

if(location == null)

mListener.onFail("location latlng = null , provider = "+ mProvider);

}

}

Geocoder


Geocoder 的api很简单,如果国内手机在自定义系统时能够使用geooder的后端服务或已内置百度高德服务,那么使用Geocoder 反地理编码功能会很方便。所以在使用前,我们可以先检查服务是否可用,再决定使用哪家的web 反地理编码服务。

if(!Geocoder.isPresent()){

if(mListener != null)

mListener.onFail(ResponseConstant.GOOGLE_API_OUT_OF_CONDITON, “geocoder is out of condition”);

return;

}

Criteria 和 Address


根据Criteria 指定的条件,设备可以自动选择那种provider、是否要求海波、是否要求方向;设置电池消耗要求、设置方向的精准度等一系列的筛选条件。但这里需要注意的是,network一般根据wifi节点mac地址和基站地理位置来进行定位的,在精度上来说是比较差的,虽然有的手机返回的经纬度的精度级别非常高,但是其实际偏差却是非常大的,所以我们不能完全信赖net定位点。

Address 是反编码成功后的实体bean,并没有什么特殊的地方,只是存储了谷歌服务返回来的字段值罢了。例如我们平时常用的国家、城市、城市编码、区、街道、POi名称等信息。

实现代码


获取经纬度代码:LocationManager获取经纬度

import android.content.Context;

import android.location.Location;

import android.location.LocationListener;

import android.location.LocationManager;

import android.location.LocationProvider;

import android.os.Build;

import android.os.Bundle;

import androidx.annotation.Nullable;

import com.mjzuo.location.bean.Latlng;

import com.mjzuo.location.helper.ConverHelper;

import com.mjzuo.location.helper.Helper;

import com.mjzuo.location.util.LogUtil;

ic class GoogleGeocoding implements IGeocoding {

/** 当前系统定位manager*/

private LocationManager lm;

/** 位置提供器*/

private String mProvider;

/** 定位change监听*/

private MyLocationListener mChangeListener;

/** 响应用户的回调*/

private ISiLoResponseListener mListener;

/** 服务的参数配置类*/

private SiLoOption mSiLoOption;

/** 当前经纬度*/

private Latlng latlng;

/** 计数器*/

private int mCounter;

/** gps信号消失,尝试network定位*/

private static final int GPS_TIME_SPACE = 6;

/** 尝试network之后,同样获取不到定位,则错误回调,时间间隔 = NET_TIME_SPACE - GPS_TIME_SPACE*/

private static final int NET_TIME_SPACE = 12;

private Context mContext;

public GoogleGeocoding(Context context) {

this.mContext = context;

this.mChangeListener = new MyLocationListener();

}

public void setSimpleLocationOption(@Nullable SiLoOption siLoOption) {

this.mSiLoOption = siLoOption;

}

@Override

public void start(@Nullable ISiLoResponseListener listener) {

if(listener != null)

this.mListener = listener;

if(mListener == null)

return;

lm = (LocationManager) mContext.getApplicationContext()

.getSystemService(Context.LOCATION_SERVICE);

// 检查是否有相关定位权限

if(!Helper.checkPermission(mContext.getApplicationContext())){

mListener.onFail(“location no permission”);

return;

}

// 如果不传配置类,则按默认配置

if(mSiLoOption == null)

mSiLoOption = new SiLoOption();

mProvider = mSiLoOption.isGpsFirst ? Helper.getGPSProvider(lm) : Helper.getNetWorkProvider(lm);

if(mProvider == null)

mListener.onFail(“location provider no exist”);

else

getLocation(mProvider);

}

@Override

public void reStart() {

start(null);

}

@Override

public void stop() {

mProvider = null;

if(lm != null)

lm.removeUpdates(mChangeListener);

lm = null;

mListener = null;

mChangeListener = null;

}

@SuppressWarnings(“all”)

private String getLocation(String provider) {

if(provider == null)

return null;

lm.requestLocationUpdates(provider, mSiLoOption.time, mSiLoOption.distance, mChangeListener);

return provider;

}

class MyLocationListener implements LocationListener {

@Override

public void onLocationChanged(Location location) {

if(mProvider == null)

return;

latlng = location != null ? ConverHelper.loConverToLatlng(location) : latlng;

mListener.onSuccess(latlng);

if(mSiLoOption.isGpsFirst){

if(location != null){

if(mProvider == LocationManager.GPS_PROVIDER)

mCounter = 0;

else{

mCounter += mSiLoOption.time/1000;

if(mCounter > NET_TIME_SPACE){

mProvider = getLocation(Helper.getGPSProvider(lm));

mCounter = 0;

}

}

}else{

// gps无信号时,尝试network获取

mCounter += mSiLoOption.time/1000;

if(mCounter >= GPS_TIME_SPACE && mCounter < NET_TIME_SPACE){

mProvider = getLocation(Helper.getNetWorkProvider(lm));

}else if(mCounter > NET_TIME_SPACE){

// 只要最后一个net点失败,就抛出回调;前两个点,由于开始,可能为空,不做强制判断处理

mListener.onFail("location latlng = null , provider = "+ mProvider);

}

}

}else{

// 当network获取不到定位

if(location == null)

mListener.onFail("location latlng = null , provider = "+ mProvider);

}

}

@Override

public void onStatusChanged(String s, int state, Bundle bundle) {

if(mListener == null)

return;

if(Build.VERSION.SDK_INT > 28){

LogUtil.e(“after api 29 always AVAILABLE”);

}else{

switch (state){

case LocationProvider.OUT_OF_SERVICE:

case LocationProvider.TEMPORARILY_UNAVAILABLE:

LogUtil.e(“current provider out of condition”);

break;

}

}

}

@Override

public void onProviderEnabled(String s) {

}

@Override

public void onProviderDisabled(String s) {

}

}

public static class SiLoOption {

/**

  • gps 优先否

*/

private boolean isGpsFirst = false;

/**

  • 监听定位变化最短时间间隔,默认time s

*/

private int time = 2000;

/**

  • 监听变化的最小距离

*/

private int distance = 10;

public boolean isGpsFirst() {

return isGpsFirst;

}

public SiLoOption setGpsFirst(boolean gpsFirst) {

isGpsFirst = gpsFirst;

return this;

}

public int getTime() {

return time;

}

public SiLoOption setTime(int time) {

this.time = time;

return this;

}

public int getRequestTepe() {

return distance;

}

public SiLoOption setRequestTepe(int requestTepe) {

this.distance = requestTepe;

return this;

}

}

}

获取经纬度代码:定位类

import android.content.Context;

import androidx.annotation.Nullable;

import com.mjzuo.location.constant.Constant;

import com.mjzuo.location.helper.GeReFactory;

import com.mjzuo.location.location.GoogleGeocoding;

import com.mjzuo.location.location.IGeocoding;

import com.mjzuo.location.util.LogUtil;

ic class GeocodingManager {

private IGeocoding mGeocoding;

private IGeocoding.ISiLoResponseListener mListener;

private Context mContext;

public GeocodingManager(Context context) {

mContext = context;

mGeocoding = GeReFactory.getGeocodingType(mContext, Constant.LM_API);

}

public GeocodingManager(Context context, @Nullable GeoOption option) {

mContext = context;

mGeocoding = GeReFactory.getGeocodingType(mContext, option.getGeoType());

if(mGeocoding instanceof GoogleGeocoding)

((GoogleGeocoding) mGeocoding).setSimpleLocationOption(option.getOption());

}

public void start(IGeocoding.ISiLoResponseListener listener) {

if(listener != null)

mListener = listener;

if(mListener == null){

LogUtil.e(“simple location response listener null”);

}

mGeocoding.start(mListener);

}

public void reStart(){

start(null);

}

public void stop(){

mGeocoding = null;

mListener = null;

}

public static class GeoOption {

/**

  • 定位的类型

  • LocationManager或基站定位

*/

private int GeoType = Constant.LM_API;

/**

  • 这是locationManager的配置类

*/

private GoogleGeocoding.SiLoOption option;

public int getGeoType() {

return GeoType;

}

public GeoOption setGeoType(int geoType) {

GeoType = geoType;

return this;

}

public GoogleGeocoding.SiLoOption getOption() {

return option;

}

public GeoOption setOption(GoogleGeocoding.SiLoOption option) {

this.option = option;

return this;

}

}

}

百度API接口:

import android.content.Context;

import android.os.AsyncTask;

import androidx.annotation.Nullable;

import com.mjzuo.location.ReverseGeocodingManager;

import com.mjzuo.location.bean.Latlng;

import com.mjzuo.location.constant.UrlConstant;

import com.mjzuo.location.helper.Helper;

import com.mjzuo.location.net.NetUtil;

import com.mjzuo.location.util.CommonUtil;

import com.mjzuo.location.util.LogUtil;

import org.json.JSONObject;

import java.io.UnsupportedEncodingException;

import java.net.URLEncoder;

import java.util.LinkedHashMap;

import java.util.Map;

public class BaiduGeRe implements IReGe {

private String ak = “KHVTQZiP2UGuv7SkNbqYPKu4co7kbkS4”;

private String sk = “cTqIacm4uvDnQWpWWCZGElhbIx4Nxv3q”;

private String host = “/reverse_geocoding/v3/”;

private LinkedHashMap<String, String> paramsMap;

// 默认使用sn校验方式

private boolean isSn = true;

private MyAsyncTask task;

private IReGeListener mListener;

@Override

public void init(@Nullable Context context) {

paramsMap = new LinkedHashMap<>();

paramsMap.put(“ak”, ak);

paramsMap.put(“coordtype”, “wgs84ll”);

}

@Override

public void setOptions(@Nullable ReverseGeocodingManager.ReGeOption options) {

this.isSn = options.isSn();

if(options.getKey() != null && !options.getKey().isEmpty())

this.ak = options.getKey();

if(options.getSk() != null && !options.getSk().isEmpty())

this.sk = options.getSk();

}

@Override

public void reGeToAddress(Latlng latlng) {

paramsMap.put(“location”, latlng.getLatitude() + “,” + latlng.getLongitude());

paramsMap.put(“output”, “json”);

if(isSn) {

String paramsStr;

try {

paramsStr = toQueryString(paramsMap);

}catch (UnsupportedEncodingException e){

LogUtil.e(“error:”+e.getMessage());

return;

}

String wholeStr = new String(host + “?” + paramsStr + sk);

try {

wholeStr = URLEncoder.encode(wholeStr, “UTF-8”);

}catch (Exception e){

LogUtil.e(“error:”+e.getMessage());

return;

}

String sn = CommonUtil.MD5(wholeStr);

paramsMap.put(“sn”, sn);

}

task = new MyAsyncTask(Helper.toAppendUrl(paramsMap, UrlConstant.BAIDU_URL, host));

task.execute();

}

@Override

public void stop() {

if(paramsMap != null)

paramsMap.clear();

paramsMap = null;

task = null;

}

@Override

public void addReGeListener(IReGeListener listener) {

mListener = listener;

}

class MyAsyncTask extends AsyncTask<Void, Void, String>{

String mUrl;

public MyAsyncTask(String url) {

mUrl = url;

}

@Override

protected String doInBackground(Void… voids) {

return NetUtil.doHttpGet(mUrl);

}

@Override

protected void onPostExecute(String json) {

super.onPostExecute(json);

fromJson(json);

task = null;

}

}

/**

  • 对Map内所有value作utf8编码,拼接返回结果

  • @param data

  • @return

  • @throws UnsupportedEncodingException

*/

private String toQueryString(Map<?, ?> data)

throws UnsupportedEncodingException {

StringBuffer queryString = new StringBuffer();

for (Map.Entry<?, ?> pair : data.entrySet()) {

queryString.append(pair.getKey() + “=”);

queryString.append(URLEncoder.encode((String) pair.getValue(),

“UTF-8”) + “&”);

}

if (queryString.length() > 0) {

queryString.deleteCharAt(queryString.length() - 1);

}

return queryString.toString();

}

/**

  • 解析json

*/

private void fromJson(String json) {

LogUtil.e(“json:”+json);

if(json == null || json.isEmpty())

return;

if(mListener == null)

return;

JSONObject jsonObject = null;

try {

jsonObject = new JSONObject(json);

}catch (Exception e){

LogUtil.e(“error:” + e.getMessage());

return;

}

if(jsonObject.has(“status”)){

Integer state;

try {

state = (Integer) jsonObject.get(“status”);

// 0请求成功标志码

if(state != 0){

if(jsonObject.has(“message”))

mListener.onFail(state, jsonObject.getString(“message”));

return;

}

if(jsonObject.has(“result”)){

JSONObject obj;

JSONObject objAddress;

try{

obj = jsonObject.getJSONObject(“result”);

objAddress = obj.getJSONObject(“addressComponent”);

Latlng latlng = new Latlng();

latlng.setCountry(objAddress.getString(“country”));

latlng.setCountryCode(objAddress.getString(“country_code”));

latlng.setCity(objAddress.getString(“city”));

latlng.setSublocality(objAddress.getString(“district”));

latlng.setCityCode(obj.getInt(“cityCode”)+“”);

latlng.setAddress(objAddress.getString(“street”));

latlng.setName(obj.getString(“business”));

mListener.onSuccess(state, latlng);

}catch (Exception e){

LogUtil.e(“error:”+e.getMessage());

return;

}

}

}catch (Exception e){

LogUtil.e(“error:” + e.getMessage());

return;

}

}

}

}

反向地理编码:

/** 百度api返地理编码*/

public static final String BAIDU_URL = “https://api.map.baidu.com”;

/**特定经纬度的反向地理编码:*/

import android.content.Context;

import androidx.annotation.Nullable;

import com.mjzuo.location.bean.Latlng;

import com.mjzuo.location.constant.Constant;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-gQzXUiBt-1712014656508)]

[外链图片转存中…(img-IuTbTyqi-1712014656509)]

[外链图片转存中…(img-TGFFjJ7p-1712014656509)]

[外链图片转存中…(img-6ek1zmps-1712014656509)]

[外链图片转存中…(img-WxLEIxWA-1712014656509)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

[外链图片转存中…(img-Wanju82c-1712014656509)]

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值