版)
⽬录
本⽂主要是给以下两个问题提供解决⽅案建议:
1、后台如何持续获取定位,包括处于以下环境:(1)应⽤已经切换到后台;(2)⼿机已经锁屏
2、成功获取到的⼀堆坐标点后应该如何加⼯处理,让坐标点尽可能准确地描绘⼿机的移动轨迹
⼀、后台如何持续获取定位
1、后台以及锁屏后持续定位异常的原因以及应对⽅案探索
关于App切换到后台并且息屏后,持续定位失效的探索:
(1)、在Android8.0 之后,⾕歌已经禁⽤了后台服务的功能,只允许使⽤前台服务,意味着app⼀旦看不见后,不管是主页键还是返回键导致的,⿊屏后,后台的功能都会即将或者⽴即进⼊冷冻状态,即服务是活着的,但是逻辑却没有正常运⾏,定位系统就会出现坐标不及时更新甚⾄不更新的情况。
(2)、针对此类型的系统管制,在参考了⽹上资料后,主要的有效资料来源于百度和⾼德的后台定位保活建议处理。百度的保活处理已经在app上得到应⽤,要提及的关键⼀点就是系统的电源锁机制。app可以通过获取锁,从影响甚⾄⼩程度的控制系统的电源运⾏情况,从⽽使⾃⾝app免于进⼊冷冻状态。但该锁在低端机出现明显的兼容问题,就是该保活处理在⾼端机或者中端机得到较好的成效,但在低端机例如⾼通骁龙4系的处理器平台上,保活失效,定位系统的更新频率会被系统压制到30秒更新⼀次,也就是说,app 的定位30秒才更新⼀次,会对⽤户带来⼀定的迷惑。推测原因可能是低端机的电源管控可能更加严格,或者⼀⼑切处理。
2、后台持续获取定位失败的应对⽅案
为什么说是应对⽅案了,因为这个⽅法不能保证⼀定起作⽤。⽅法主要是参考百度地图的鹰眼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() {
@Override
public void onCompletion(MediaPlayer mp) {
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.FileDescriptor(), StartOffset(), Length());
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.del.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.3f
private 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 = (0);
kalmanFilterList.add(lastLoc);
kalmanFilterList.add(lastLoc);
for (int i = 1; i < originlist.size(); i++) {
LatLng curLoc = (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;
发布评论