目录

一、后台如何持续获取定位

1、后台以及锁屏后持续定位异常的原因以及应对方案探索

2、后台持续获取定位失败的应对方案

二、对坐标点进行加工处理

(1)、为什么要加工处理

(2)、如何加工处理


本文主要是给以下两个问题提供解决方案建议:

1、后台如何持续获取定位,包括处于以下环境:(1)应用已经切换到后台;(2)手机已经锁屏

2、成功获取到的一堆坐标点后应该如何加工处理,让坐标点尽可能准确地描绘手机的移动轨迹

一、后台如何持续获取定位

1、后台以及锁屏后持续定位异常的原因以及应对方案探索

关于App切换到后台并且息屏后,持续定位失效的探索:
(1)、在Android8.0 之后,谷歌已经禁用了后台服务的功能,只允许使用前台服务,意味着app一旦看不见后,不管是主页键还是返回键导致的,黑屏后,后台的功能都会即将或者立即进入冷冻状态,即服务是活着的,但是逻辑却没有正常运行,定位系统就会出现坐标不及时更新甚至不更新的情况。

(2)、针对此类型的系统管制,在参考了网上资料后,主要的有效资料来源于百度和高德的后台定位保活建议处理。百度的保活处理已经在app上得到应用,要提及的关键一点就是系统的电源锁机制。app可以通过获取锁,从影响甚至小程度的控制系统的电源运行情况,从而使自身app免于进入冷冻状态。但该锁在低端机出现明显的兼容问题,就是该保活处理在高端机或者中端机得到较好的成效,但在低端机例如高通骁龙4系的处理器平台上,保活失效,定位系统的更新频率会被系统压制到30秒更新一次,也就是说,app 的定位30秒才更新一次,会对用户带来一定的迷惑。推测原因可能是低端机的电源管控可能更加严格,或者一刀切处理。

2、后台持续获取定位失败的应对方案

为什么说是应对方案了,因为这个方法不能保证一定起作用。方法主要是参考百度地图的鹰眼sdk文档中的服务存活做法建议,这个是百度的链接:百度鹰眼SDK后台保活的做法建议

百度的建议中最强的一点是:启用多媒体锁

什么是多媒体锁?可以自行百度,以下是百度参考建议中直接复制粘贴过来的内容:

为了确保MediaPlayer的承载的服务在系统睡眠的时候继续正常执行下去。Android为我们提供了一种唤醒锁(wake locks)的机制。它能够在系统睡眠时,依旧保持锁定硬件的正常工作。基于这种思路,可以在集成鹰眼SDK的APP中,使用Service继承MediaPlayer,播放一段无声音频文件,达到保活效果。确保在MediaPlayer执行的时候,哪怕系统睡眠了CUP也能正常执行。需要使用MediaPlayer.setWakeMode()为MediaPlayer设定唤醒锁。以下是setWakMode()的定义:setWakeMode(Context context, int mode)第一个參数是当前上下文,第二个參数为须要加锁的状态,被设定为int类型的常量,定义在PowerManager这个final类中。在这里仅仅须要设定为PARTIAL_WAKE_LOCK就可以。

// 设定CUP锁定 mediaPlayer = new MediaPlayer(); mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

一般对于锁而言。锁定了通常须要解锁。可是这里的唤醒锁与MediaPlayer关联,所以仅仅须要在使用完之后release()释放MediaPlayer就可以,无需显式的为其解锁。在使用setWakeMode设定唤醒锁的时候,还必须为应用赋予对应的权限: <uses-permission android:name="android.permission.WAKE_LOCK"/>

怎么使用这个多媒体锁呢?我是直接找了一个无声音的音频文件,然后在后台持续循环播放该音频文件,音频文件在文末云盘分享链接。

示例代码:

    /*** 新建这个播放器,主要是用来获取媒体锁,从而使服务不被系统杀掉,一般对于锁而言。锁定了通常须要解锁。* 可是这里的唤醒锁与MediaPlayer关联,所以仅仅须要在使用完之后release()释放MediaPlayer就可以,无需显式的为其解锁*/private MediaPlayer mMediaPlayer;//播放音频文件private void playMuteMusic() {mMediaPlayer = new MediaPlayer();setMusicSource();mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mMediaPlayer.start();mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {mMediaPlayer.reset();setMusicSource();mMediaPlayer.start();}});//一般对于锁而言。锁定了通常须要解锁。//可是这里的唤醒锁与MediaPlayer关联,所以仅仅须要在使用完之后release()释放MediaPlayer就可以,无需显式的为其解锁mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);}//设置音频文件路径private void setMusicSource() {AssetFileDescriptor fileDescriptor = null;try {fileDescriptor = getAssets().openFd("mute.mp3");//播放的是assets下的音频文件mMediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(), fileDescriptor.getLength());mMediaPlayer.prepare();} catch (IOException e) {e.printStackTrace();}}

其中以上代码起关键作用的是开启锁这一行,至于锁的其中缘由,不在本文探索范围内

MediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

以上代码需要放在后台服务类中,启动运行。

二、对坐标点进行加工处理

(1)、为什么要加工处理

坐标点拿回来的时候,会附带有误差值(即定位准确度,数字越小,越准),一般根据项目实际使用情况进行对应的筛选,例如本人负责项目会过滤误差在30米以上的坐标点,也就是大于30米误差的直接扔掉。这是一点,其次是坐标重复冗余的问题。当用户在原地不动的时候,GPS却是时刻保持工作的,但是返回来的坐标点不一定相同,如果这时候也将这些坐标点直接描绘在地图上,轨迹会显示得像马蜂窝那般杂乱无章。最后是视觉体验问题,在不影响轨迹准确性的情况下,将坐标点连接起来后,如何将轨迹显示得圆润些,没那么多直角或者折线。

(2)、如何加工处理

这里主要是个人处理加上高德开源的算法。

个人处理逻辑里,会先在准确度以及速度的前提下,将数据进行先过滤。例如GPS会返回速度,如果速度为0的情况下,坐标点就是无效的。其二,准确度,像刚刚说的那样,根据项目实际需要误差在30米以上的直接扔掉。

然后是依靠高德开源的算法,参考  高德轨迹平滑处理示例

我主要是使用了这个示例中的这个类  PathSmoothTool

以下是我修改后的类代码,主要是修改了3个值,(1)卡尔曼滤波程度(2)抽稀程度(3)去噪程度,三个值在代码注释里有对应解释,不在此赘述

import android.util.Log;import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.model.LatLng;import java.util.ArrayList;
import java.util.List;/*** 从高德开源工具那里复制过来* 轨迹优化工具类* Created by my94493 on 2017/3/31.* <p>* 使用方法:* <p>*     PathSmoothTool pathSmoothTool = new PathSmoothTool();*     pathSmoothTool.setIntensity(2);//设置滤波强度,默认3*     List<LatLng> mList = LatpathSmoothTool.kalmanFilterPath(list);*/public class PathSmoothTool {private int mIntensity = 5;    //卡尔曼滤波程度   默认值是3   这东西影响着轨迹地流畅性private float mThreshhold = 0.1f;  //抽稀程度  数字越大  冗余轨迹点越少  轨迹越不圆润  默认值是0.3fprivate float mNoiseThreshhold = 10;  //去噪程度,轨迹点漂移偏差大小的判断,默认值是10   就是以10为准,两点之间偏差大于10的话,丢弃那个坐标点public PathSmoothTool(){}public int getIntensity() {return mIntensity;}public void setIntensity(int mIntensity) {this.mIntensity = mIntensity;}public float getThreshhold() {return mThreshhold;}public void setThreshhold(float mThreshhold) {this.mThreshhold = mThreshhold;}public void setNoiseThreshhold(float mnoiseThreshhold) {this.mNoiseThreshhold = mnoiseThreshhold;}/*** 轨迹平滑优化* @param originlist 原始轨迹list,list.size大于2* @return 优化后轨迹list*/public List<LatLng> pathOptimize(List<LatLng> originlist){List<LatLng> list = removeNoisePoint(originlist);//去噪List<LatLng> afterList = kalmanFilterPath(list,mIntensity);//滤波List<LatLng> pathoptimizeList = reducerVerticalThreshold(afterList,mThreshhold);//抽稀
//        Log.i("MY","originlist: "+originlist.size());
//        Log.i("MY","list: "+list.size());
//        Log.i("MY","afterList: "+afterList.size());
//        Log.i("MY","pathoptimizeList: "+pathoptimizeList.size());return pathoptimizeList;}/*** 轨迹线路滤波* @param originlist 原始轨迹list,list.size大于2* @return 滤波处理后的轨迹list*/public List<LatLng> kalmanFilterPath(List<LatLng> originlist) {return kalmanFilterPath(originlist,mIntensity);}/*** 轨迹去噪,删除垂距大于20m的点* @param originlist 原始轨迹list,list.size大于2* @return*/public List<LatLng> removeNoisePoint(List<LatLng> originlist){return reduceNoisePoint(originlist,mNoiseThreshhold);}/*** 单点滤波* @param lastLoc 上次定位点坐标* @param curLoc 本次定位点坐标* @return 滤波后本次定位点坐标值*/public LatLng kalmanFilterPoint(LatLng lastLoc, LatLng curLoc) {return kalmanFilterPoint(lastLoc,curLoc,mIntensity);}/*** 轨迹抽稀* @param inPoints 待抽稀的轨迹list,至少包含两个点,删除垂距小于mThreshhold的点* @return 抽稀后的轨迹list*/public List<LatLng> reducerVerticalThreshold(List<LatLng> inPoints) {return reducerVerticalThreshold(inPoints,mThreshhold);}/********************************************************************************************************//*** 轨迹线路滤波* @param originlist 原始轨迹list,list.size大于2* @param intensity 滤波强度(1—5)* @return*/private List<LatLng> kalmanFilterPath(List<LatLng> originlist,int intensity) {List<LatLng> kalmanFilterList = new ArrayList<LatLng>();if (originlist == null || originlist.size() <= 2)return kalmanFilterList;initial();//初始化滤波参数LatLng latLng = null;LatLng lastLoc = originlist.get(0);kalmanFilterList.add(lastLoc);for (int i = 1; i < originlist.size(); i++) {LatLng curLoc = originlist.get(i);latLng = kalmanFilterPoint(lastLoc,curLoc,intensity);if (latLng != null) {kalmanFilterList.add(latLng);lastLoc = latLng;}}return kalmanFilterList;}/*** 单点滤波* @param lastLoc 上次定位点坐标* @param curLoc 本次定位点坐标* @param intensity 滤波强度(1—5)* @return 滤波后本次定位点坐标值*/private LatLng kalmanFilterPoint(LatLng lastLoc, LatLng curLoc, int intensity) {if (pdelt_x == 0 || pdelt_y == 0 ){initial();}LatLng kalmanLatlng = null;if (lastLoc == null || curLoc == null){return kalmanLatlng;}if (intensity < 1){intensity = 1;} else if (intensity > 5){intensity = 5;}for (int j = 0; j < intensity; j++){kalmanLatlng = kalmanFilter(lastLoc.longitude,curLoc.longitude,lastLoc.latitude,curLoc.latitude);curLoc = kalmanLatlng;}return kalmanLatlng;}/***************************卡尔曼滤波开始********************************/private double lastLocation_x; //上次位置private double currentLocation_x;//这次位置private double lastLocation_y; //上次位置private double currentLocation_y;//这次位置private double estimate_x; //修正后数据private double estimate_y; //修正后数据private double pdelt_x; //自预估偏差private double pdelt_y; //自预估偏差private double mdelt_x; //上次模型偏差private double mdelt_y; //上次模型偏差private double gauss_x; //高斯噪音偏差private double gauss_y; //高斯噪音偏差private double kalmanGain_x; //卡尔曼增益private double kalmanGain_y; //卡尔曼增益private double m_R= 0;private double m_Q= 0;//初始模型private void initial(){pdelt_x =  0.001;pdelt_y =  0.001;
//        mdelt_x = 0;
//        mdelt_y = 0;mdelt_x =  5.698402909980532E-4;mdelt_y =  5.698402909980532E-4;}private LatLng kalmanFilter(double oldValue_x, double value_x, double oldValue_y, double value_y){lastLocation_x = oldValue_x;currentLocation_x= value_x;gauss_x = Math.sqrt(pdelt_x * pdelt_x + mdelt_x * mdelt_x)+m_Q;     //计算高斯噪音偏差kalmanGain_x = Math.sqrt((gauss_x * gauss_x)/(gauss_x * gauss_x + pdelt_x * pdelt_x)) +m_R; //计算卡尔曼增益estimate_x = kalmanGain_x * (currentLocation_x - lastLocation_x) + lastLocation_x;    //修正定位点mdelt_x = Math.sqrt((1-kalmanGain_x) * gauss_x *gauss_x);      //修正模型偏差lastLocation_y = oldValue_y;currentLocation_y = value_y;gauss_y = Math.sqrt(pdelt_y * pdelt_y + mdelt_y * mdelt_y)+m_Q;     //计算高斯噪音偏差kalmanGain_y = Math.sqrt((gauss_y * gauss_y)/(gauss_y * gauss_y + pdelt_y * pdelt_y)) +m_R; //计算卡尔曼增益estimate_y = kalmanGain_y * (currentLocation_y - lastLocation_y) + lastLocation_y;    //修正定位点mdelt_y = Math.sqrt((1-kalmanGain_y) * gauss_y * gauss_y);      //修正模型偏差LatLng latlng = new LatLng(estimate_y,estimate_x);return latlng;}/***************************卡尔曼滤波结束**********************************//***************************抽稀算法*************************************/private List<LatLng> reducerVerticalThreshold(List<LatLng> inPoints,float threshHold) {if (inPoints == null) {return null;}if (inPoints.size() <= 2) {return inPoints;}List<LatLng> ret = new ArrayList<LatLng>();for (int i = 0; i < inPoints.size(); i++) {LatLng pre = getLastLocation(ret);LatLng cur = inPoints.get(i);if (pre == null || i == inPoints.size() - 1) {ret.add(cur);continue;}LatLng next = inPoints.get(i + 1);double distance = calculateDistanceFromPoint(cur, pre, next);if (distance > threshHold){ret.add(cur);}}return ret;}private static LatLng getLastLocation(List<LatLng> oneGraspList) {if (oneGraspList == null || oneGraspList.size() == 0) {return null;}int locListSize = oneGraspList.size();LatLng lastLocation = oneGraspList.get(locListSize - 1);return lastLocation;}/*** 计算当前点到线的垂线距离* @param p 当前点* @param lineBegin 线的起点* @param lineEnd 线的终点**/private static double calculateDistanceFromPoint(LatLng p, LatLng lineBegin,LatLng lineEnd) {double A = p.longitude - lineBegin.longitude;double B = p.latitude - lineBegin.latitude;double C = lineEnd.longitude - lineBegin.longitude;double D = lineEnd.latitude - lineBegin.latitude;double dot = A * C + B * D;double len_sq = C * C + D * D;double param = dot / len_sq;double xx, yy;if (param < 0 || (lineBegin.longitude == lineEnd.longitude&& lineBegin.latitude == lineEnd.latitude)) {xx = lineBegin.longitude;yy = lineBegin.latitude;
//            return -1;} else if (param > 1) {xx = lineEnd.longitude;yy = lineEnd.latitude;
//            return -1;} else {xx = lineBegin.longitude + param * C;yy = lineBegin.latitude + param * D;}return AMapUtils.calculateLineDistance(p,new LatLng(yy,xx));}/***************************抽稀算法结束*********************************/private List<LatLng> reduceNoisePoint(List<LatLng> inPoints, float threshHold) {if (inPoints == null) {return null;}if (inPoints.size() <= 2) {return inPoints;}List<LatLng> ret = new ArrayList<LatLng>();for (int i = 0; i < inPoints.size(); i++) {LatLng pre = getLastLocation(ret);LatLng cur = inPoints.get(i);if (pre == null || i == inPoints.size() - 1) {ret.add(cur);continue;}LatLng next = inPoints.get(i + 1);double distance = calculateDistanceFromPoint(cur, pre, next);if (distance < threshHold){ret.add(cur);}}return ret;}
}

这个工具类的主要使用方法是    pathOptimize(List<LatLng> originlist)

传入的坐标点是高德自身定义的 LatLng  类,但我觉得应该可以使用自定义的因为坐标点传进去,只是用在了高德自身的距离计算方法,该计算方法可以自己网上自行百度,当然,只是推测,大家可以试验下。

最后,pathOptimize 方法处理过后的数据就可以直接描绘在地图上了。

百度云盘链接:

链接:https://pan.baidu.com/s/1QaWASPI9-qaQ-dALVkpZqQ 
提取码:26bj

Android 基于高德地图的锁屏后定位和轨迹自动纠偏(离线版)相关推荐

  1. android 基于高德地图的轨迹回放

    android 基于高德地图的轨迹回放 前段时间公司项目有一个需求,就是需要看到设备上传之后的轨迹路线,并且可以实现回放的整个过程,功能包括路线回放.地图位置插点.回放之后的轨迹标记颜色.回放加速等功 ...

  2. 基于高德地图JsAPI进行浏览器精确定位,实现手机端考勤打卡功能

    前言: 由于项目需求需要在项目中实现手机端(基于网页)考勤打卡功能,最初考虑使用H5自身定位功能,但尝试过后,效果很不稳定.然后尝试使用百度地图JsAPI,百度家的稳定倒是很稳定,没想到的是定位位置和 ...

  3. Android基于高德地图实现多人实时共享位置

    自开发上一款智慧旅游产品后,发现一个很有意义而且很实用的功能,就是模仿微信的位置共享,可以看到对方的位置,一直想模仿做出这样的效果.最近闲下来之后终于实现了.下面就把我的实现过程和心得分享给大家. 步 ...

  4. android app锁屏后定位,如何能让app在锁屏后还继续发送定位请求继续运行程序

    现在我的定位版本还不能打包.用基座版本测试的. 发现 1.wifi 休眠是不影响的(我的小米2 可以设置 始终保持连接) 2.屏幕一旦休眠(不是锁屏) 应用程序 是 受干扰的.(时间间隔不等) 但基本 ...

  5. Android基于高德地图实时定位服务

    Service功能有:实时定位(后台进行) Service不会,去百度谷歌 功能有 实时定位(30秒间隔) 判断是否在规定的时间段内 判断距离是否大于规定的距离 判断服务是否在开启的状态 服务代码:L ...

  6. 只用一个marker 替换 高德_Android基于高德地图完全自定义Marker的实现方法

    前言 相信做地图社交类APP开发的大家都知道,一般情况下,为了整体的美观和用户体验度,我们需要定制化Marker的样式.本文中实现的方式都是基于高德地图的,百度地图也类似,大家可以照葫芦画瓢,废话不多 ...

  7. Android基于高德SDK的开发——自定义地图主题样式(悬浮按钮+底部弹窗)

    日常的地图使用中,平台一般只会给我们提供地图的标准样式,造成了一定程度上的审美疲劳,那么如何实现地图的自定义样式呢?本文使用Android Studio 4.1,给开发者提供了一个基于高德地图SDK进 ...

  8. 【Android】锁屏后应用保活、拉活、双进程守护

    最近在使用高德地图实时获取定位时遇到了个问题,锁屏后一段时间(5~10分钟左右)后程序会被系统杀死,为了保活,特研究了下进程保活机制. 0.基本操作和概念 针对root过的手机,可以通过下列命令查看内 ...

  9. 南邮Android实验报告三:基于高德地图的综合应用

    实验三 基于高德地图的综合应用 一.目的要求 1.学会安卓应用中涉及位置服务时的解决方案. 2.练习在使用第三方插件时,遇到版本不兼容时的处理步骤和方法. 二.实验环境 1.硬件配置:Intel Co ...

最新文章

  1. SQL提交数据三种类型
  2. Awk中调用shell命令
  3. Django基础—— 1.WEB框架介绍
  4. redis之闪电内幕
  5. 小鹏吃相难看,消费者难做“鹏”友
  6. c++并发操作mysql_文件数据库sqlite3 C++ 线程安全和并发
  7. Linux防火墙开关命令
  8. Apifox测试Excel文件上传
  9. 预告 | 互联网人吐槽大会系列漫画 要来啦~
  10. 如何监听安卓机返回键和苹果机微信的返回键事件
  11. 十进制如何转化为二进制和六进制
  12. 抓紧收藏,教你如何轻松快速度过冷启动时期?短视频新手必看
  13. python全栈指的是什么_python全栈指的是什么
  14. 计算机题库一级第四,计算机一级题题库,第四章Excel
  15. 人工智能站在风口 云计算指数大涨7.96%
  16. 小米电视系统服务器升级,小米电视怎么更新系统 升级步骤图文详解
  17. 初等数论四大定理(威尔逊定理,欧拉定理,费马小定理,中国剩余定理)
  18. 动态规划 背包算法 JS实现
  19. 【知识网络分析】作者合作网络(Co-authorship)
  20. 书单来了!大厂的技术牛人在读什么:字节跳动篇

热门文章

  1. MySQL 存储过程笔记
  2. 封装Vue组件的原则及技巧
  3. 知识付费直播间的实用售课技巧
  4. Python爬虫完整代码拿走不谢
  5. 【分享】ArcGIS 根据DEM生成等高线以及带高程转换为Auto CAD数据
  6. Axios源码深度剖析
  7. HAProxy 简介及配置文件详解
  8. ESD静电保护二极管应用行业举例
  9. 黑客攻击第一步你知道是啥吗?
  10. html中斜体样式怎么写,css font-style字体斜体样式