protected boolean isDestoryMap() {
return true;
}
}
GPSPlaybackActivity.java
这一部分主要是对于导航 SDK的交互和添加导航UI部分初始化工作。注意导航sdk一定要先算路,再开始导航。算路可以取得GPS文件的首行为起点,末行为终点。
用到的fields
private static final String LOG_TAG = “[GpsPlayback]”;
// gps 文件路径
private String mGpsTrackPath;
// gps 轨迹的起终点
private NaviPoi mFrom, mTo;
// 是否是84坐标系
private boolean isLocation84 = true;
因为在GPSPlaybackEngine已经进行了listener监听,所以需要对于导航SDK进行灌点
// 腾讯定位sdk的listener
private TencentLocationListener listener = new TencentLocationListener() {
@Override
public void onLocationChanged(TencentLocation tencentLocation, int error, String reason) {
if (error != TencentLocation.ERROR_OK || tencentLocation == null) {
return;
}
Log.d(LOG_TAG, "onLocationChanged : "
- “, latitude” + tencentLocation.getLatitude()
- ", longitude: " + tencentLocation.getLongitude()
- ", provider: " + tencentLocation.getProvider()
- ", accuracy: " + tencentLocation.getAccuracy());
// 将定位点灌入导航SDK
// mCarManagerSingleton是使用导航SDK的carNaviManager创建的单例,开发者可以自己实现
mCarManagerSingleton.get().updateLocation(ConvertHelper
.convertToGpsLocation(tencentLocation), error, reason);
}
@Override
public void onStatusUpdate(String provider, int status, String description) {
Log.d(LOG_TAG, "onStatusUpdate provider: " + provider
- ", status: " + status
- ", desc: " + description);
// 更新GPS状态.
mCarManagerSingleton.get().updateGpsStatus(provider, status, description);
}
};
onCreate方法初始化UI和添加callback
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 获取GPS文件轨迹路径,这里可以由开发者自己获取
mGpsTrackPath = getIntent().getStringExtra(“gpsTrackPath”);
if (mGpsTrackPath == null || mGpsTrackPath.isEmpty()) {
return;
}
initUi();
addTencentCallback();
new Handler().post(() -> {
// 目的获取每条轨迹的arraylist
ArrayList gpsLineStrs = readGpsFile(mGpsTrackPath);
if (gpsLineStrs == null || gpsLineStrs.isEmpty()) {
return;
}
// 获取起终点
getFromAndTo(gpsLineStrs);
if (mFrom == null || mTo == null) {
return;
}
final Handler handlerUi = new Handler(Looper.getMainLooper());
handlerUi.post(() -> searchAndStartNavigation());
});
}
private void initUi() {
mCarManagerSingleton.get().setInternalTtsEnabled(true);
final int margin = CommonUtils.dip2px(this, 36);
// 全览模式的路线边距
mCarNaviView.setVisibleRegionMargin(margin, margin, margin, margin);
mCarNaviView.setAutoScaleEnabled(true);
mCarManagerSingleton.get().setMulteRoutes(true);
mCarNaviView.setNaviMapActionCallback(mCarManagerSingleton.get());
// 使用默认UI
CarNaviInfoPanel carNaviInfoPanel = mCarNaviView.showNaviInfoPanel();
carNaviInfoPanel.setOnNaviInfoListener(() -> {
mCarManagerSingleton.get().stopNavi();
finish();
});
CarNaviInfoPanel.NaviInfoPanelConfig config = new CarNaviInfoPanel.NaviInfoPanelConfig();
config.setRerouteViewEnable(true); // 重算按钮
carNaviInfoPanel.setNaviInfoPanelConfig(config);
}
private void addTencentCallback() {
mCarManagerSingleton.get().addTencentNaviCallback(mTencentCallback);
}
private TencentNaviCallback mTencentCallback = new TencentNaviCallback() {
@Override
public void onStartNavi() { }
@Override
public void onStopNavi() { }
@Override
public void onOffRoute() { }
@Override
public void onRecalculateRouteSuccess(int recalculateType,
ArrayList routeDataList) { }
@Override
public void onRecalculateRouteSuccessInFence(int recalculateType) { }
@Override
public void onRecalculateRouteFailure(int recalculateType,
int errorCode, String errorMessage) { }
@Override
public void onRecalculateRouteStarted(int recalculateType) { }
@Override
public void onRecalculateRouteCanceled() { }
@Override
public int onVoiceBroadcast(NaviTts tts) {
return 0;
}
@Override
public void onArrivedDestination() { }
@Override
public void onPassedWayPoint(int passPointIndex) { }
@Override
public void onUpdateRoadType(int roadType) { }
@Override
public void onUpdateParallelRoadStatus(ParallelRoadStatus parallelRoadStatus) {
}
@Override
public void onUpdateAttachedLocation(AttachedLocation location) { }
@Override
public void onFollowRouteClick(String routeId, ArrayList latLngArrayList) { }
};
readGpsFile方法
private ArrayList readGpsFile(String fileName) {
ArrayList gpsLineStrs = new ArrayList<>();
BufferedReader reader = null;
try {
File file = new File(fileName);
InputStream is = new FileInputStream(file);
reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
gpsLineStrs.add(line);
}
return gpsLineStrs;
} catch (Exception e) {
Log.e(LOG_TAG, “startMockTencentLocation Exception”, e);
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (Exception e) {
Log.e(LOG_TAG, “startMockTencentLocation Exception”, e);
e.printStackTrace();
}
}
return null;
}
getFromAndTo方法,获取起终点为进行算路
private void getFromAndTo(ArrayList gpsLineStrs) {
final int size;
if ((size = gpsLineStrs.size()) < 2) {
return;
}
final String firstLine = gpsLineStrs.get(0);
final String endLine = gpsLineStrs.get(size - 1);
try {
final String[] fromParts = firstLine.split(“,”);
mFrom = new NaviPoi(Double.valueOf(fromParts[1]), Double.valueOf(fromParts[0]));
final String[] endParts = endLine.split(“,”);
mTo = new NaviPoi(Double.valueOf(endParts[1]), Double.valueOf(endParts[0]));
} catch (Exception e) {
mFrom = null;
mTo = null;
}
}
算路searchAndStartNavigation()
可以使用导航SDK的算路方法并且获取算路成功和失败的回调
private void searchAndStartNavigation() {
mCarManagerSingleton.get()
.searchRoute(new TencentRouteSearchCallback() {
@Override
public void onRouteSearchFailure(int i, String s) {
toast(“路线规划失败”);
}
@Override
public void onRouteSearchSuccess(ArrayList arrayList) {
if (arrayList == null || arrayList.isEmpty()) {
toast(“未能召回路线”);
return;
}
handleGpsPlayback();
}
});
}
调用GpsPlaybackEngine方法,进行listen定位,然后开始导航
private void handleGpsPlayback() {
// 与GpsPlaybackEngine 进行交互, 添加locationListener
GpsPlaybackEngine.getInstance().addTencentLocationListener(listener);
//与GpsPlaybackEngine 进行交互,开始定位
GpsPlaybackEngine.getInstance().startMockTencentLocation(mGpsTrackPath, isLocation84);
try {
mCarManagerSingleton.get().startNavi(0);
} catch (Exception e) {
toast(e.getMessage());
}
}
结束导航
@Override
protected void onDestroy() {
// 与GpsPlaybackEngine 进行交互, removelocationListener
mCarManagerSingleton.get().removeTencentNaviCallback(mTencentCallback);
//与GpsPlaybackEngine 进行交互,结束定位GpsPlaybackEngine.getInstance().removeTencentLocationListener(listener);
GpsPlaybackEngine.getInstance().stopMockLocation();
if (mCarManagerSingleton.get().isNavigating()) {
// 结束导航
mCarManagerSingleton.get().stopNavi();
}
super.onDestroy();
}
GPSPlaybackEngine.java
这一部分主要是对于GPS文件进行读取并且提供外界可用的add/removelistener方法,start/stopMockLocation方法 因为要让engine运行在自己的线程,所以使用runnable机制
public class GpsPlaybackEngine implements Runnable{
// 代码在下方
}
而使用到的fields
// Tencent轨迹Mock, TencentLocationListener需要利用腾讯定位SDK获取
private ArrayList mTencentLocationListeners = new ArrayList<>();
// 获取的location数据
private List mDatas = new ArrayList();
private boolean mIsReplaying = false;
private boolean mIsMockTencentLocation = true;
private Thread mMockGpsProviderTask = null;
// 是否已经暂停
private boolean mPause = false;
private double lastPointTime = 0;
private double sleepTime = 0;
关键方法
- listener相关
// 添加listener
public void addTencentLocationListener(TencentLocationListener listener) {
if (listener != null) {
mTencentLocationListeners.add(listener);
}
}
// 移除listener
public void removeTencentLocationListener(TencentLocationListener listener) {
if (listener != null) {
mTencentLocationListeners.remove(listener);
}
}
- 开始/关闭模拟轨迹
/*
- 模拟轨迹
- @param context
- @param fileName 轨迹文件绝对路径
*/
public void startMockTencentLocation(String fileName, boolean is84) {
// 首先清除以前的data
mDatas.clear();
// 判断是否是84坐标系
mIsMockTencentLocation = !is84;
BufferedReader reader = null;
try {
File file = new File(fileName);
InputStream is = new FileInputStream(file);
reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
mDatas.add(line);
}
if (mDatas.size() > 0) {
mIsReplaying = true;
synchronized (this) {
mPause = false;
}
// 开启异步线程
mMockGpsProviderTask = new Thread(this);
mMockGpsProviderTask.start();
}
} catch (Exception e) {
Log.e(TAG, “startMockTencentLocation Exception”, e);
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (Exception e) {
Log.e(TAG, “startMockTencentLocation Exception”, e);
e.printStackTrace();
}
}
}
/**
-
退出应用前也需要调用停止模拟位置,否则手机的正常GPS定位不会恢复
*/
public void stopMockTencentLocation() {
try {
mIsReplaying = false;
mMockGpsProviderTask.join();
mMockGpsProviderTask = null;
lastPointTime = 0;
} catch (Exception e) {
Log.e(TAG, “stopMockTencentLocation Exception”, e);
e.printStackTrace();
}
} -
runnable相关
@Override
public void run() {
for (String line : mDatas) {
if (!mIsReplaying) {
Log.e(TAG, “stop gps replay”);
break;
}
if (TextUtils.isEmpty(line)) {
continue;
}
try {
Thread.sleep(getSleepTime(line) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean mockResult;
mockResult = mockTencentLocation(line);
if (!mockResult) {
break;
}
try {
checkToPauseThread();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
使用到的private方法
private void checkToPauseThread() throws InterruptedException {
synchronized (this) {
while (mPause) {
wait();
}
}
}
private int getSleepTime(String line) {
try {
String[] parts = line.split(“,”);
double time = Double.valueOf(parts[6]);
time = (int) Math.floor(time);
if(lastPointTime != 0) {
sleepTime = time - lastPointTime; // 单位s,取整数
}
lastPointTime = time;
}catch (Exception e) {
}
return (int)sleepTime;
}
private boolean mockTencentLocation(String line) {
try {
String[] parts = line.split(“,”);
double latitude = Double.valueOf(parts[1]);
double longitude = Double.valueOf(parts[0]);
float accuracy = Float.valueOf(parts[2]);
float bearing = Float.valueOf(parts[3]);
float speed = Float.valueOf(parts[4]);
double altitude = Double.valueOf(parts[7]);
double time = Double.valueOf(parts[6]);
String buildingId;
String floorName;
if (parts.length >= 10) {
buildingId = parts[8];
floorName = parts[9];
} else {
buildingId = “”;
floorName = “”;
}
if (!mIsMockTencentLocation) {
double[] result = CoordinateConverter.wgs84togcj02(longitude, latitude);
longitude = result[0];
latitude = result[1];
}
GpsPlaybackEngine.MyTencentLocation location = new GpsPlaybackEngine.MyTencentLocation();
location.setProvider(“gps”);
location.setLongitude(longitude);
location.setLatitude(latitude);
location.setAccuracy(accuracy);
location.setDirection(bearing);
location.setVelocity(speed);
location.setAltitude(altitude);
location.setBuildingId(buildingId);
location.setFloorName(floorName);
location.setRssi(4);
location.setTime(System.currentTimeMillis());
// location.setTime((long) time * 1000);
for (TencentLocationListener listener : mTencentLocationListeners) {
if (listener != null) {
String curTime;
if (location != null && location.getTime() != 0) {
long millisecond = location.getTime();
Date date = new Date(millisecond);
SimpleDateFormat format = new SimpleDateFormat(“yyyy.MM.dd hh:mm:ss”);
curTime = format.format(date);
} else {
curTime = “null”;
}
Log.e(TAG, "time : " + curTime
- ", longitude : " + longitude
- " , latitude : " + latitude);
listener.onLocationChanged(location, 0, “”);
listener.onStatusUpdate(LocationManager.GPS_PROVIDER, mMockGpsStatus, “”);
}
}
} catch(Exception e) {
Log.e(TAG, “Mock Location Exception”, e);
// 如果未开位置模拟,这里可能出异常
e.printStackTrace();
return false;
}
return true;
}
CoordinateConverter.wg84togcj02
/**
- WGS84转GCJ02(火星坐标系)
- @param lng WGS84坐标系的经度
- @param lat WGS84坐标系的纬度
- @return 火星坐标数组
*/
public static double[] wgs84togcj02(double lng, double lat) {
if (out_of_china(lng, lat)) {
return new double[] { lng, lat };
}
double dlat = transformlat(lng - 105.0, lat - 35.0);
double dlng = transformlng(lng - 105.0, lat - 35.0);
double radlat = lat / 180.0 * pi;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[] { mglng, mglat };
}
内部类MyTencentLocation implements 定位sdk的接口
class MyTencentLocation implements TencentLocation {
/**
- 纬度
/
private double latitude = 0;
/* - 经度
/
private double longitude = 0;
/* - 精度
/
private float accuracy = 0;
/* - gps方向
/
private float direction = -1;
/* - 速度
/
private float velocity = 0;
/* - 时间
/
private long time = 0;
/* - 海拔高度
/
private double altitude = 0;
/* - 定位来源
/
private String provider = “”;
/* - GPS信号等级
最后
针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
信号等级
最后
针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。
[外链图片转存中…(img-85kcWyzk-1714687122014)]
[外链图片转存中…(img-gnkYQAOy-1714687122014)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!