Calculate route using Executer instead of Thread
This commit is contained in:
parent
cf916a5da7
commit
c860f8eea4
3 changed files with 213 additions and 179 deletions
|
@ -10,11 +10,19 @@ import net.osmand.plus.settings.backend.ApplicationMode;
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.router.RouteCalculationProgress;
|
import net.osmand.router.RouteCalculationProgress;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION;
|
import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION;
|
||||||
|
|
||||||
class RouteRecalculationThreadHelper {
|
class RouteRecalculationHelper {
|
||||||
|
|
||||||
private static final int RECALCULATE_THRESHOLD_COUNT_CAUSING_FULL_RECALCULATE = 3;
|
private static final int RECALCULATE_THRESHOLD_COUNT_CAUSING_FULL_RECALCULATE = 3;
|
||||||
private static final int RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL = 2 * 60 * 1000;
|
private static final int RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL = 2 * 60 * 1000;
|
||||||
|
@ -22,17 +30,19 @@ class RouteRecalculationThreadHelper {
|
||||||
private final OsmandApplication app;
|
private final OsmandApplication app;
|
||||||
private final RoutingHelper routingHelper;
|
private final RoutingHelper routingHelper;
|
||||||
|
|
||||||
private Thread currentRunningJob;
|
private final ExecutorService executor = new RouteRecalculationExecutor();
|
||||||
|
private final Map<Future<?>, RouteRecalculationTask> tasksMap = new LinkedHashMap<>();
|
||||||
|
private RouteRecalculationTask lastTask;
|
||||||
|
|
||||||
private long lastTimeEvaluatedRoute = 0;
|
private long lastTimeEvaluatedRoute = 0;
|
||||||
private String lastRouteCalcError;
|
private String lastRouteCalcError;
|
||||||
private String lastRouteCalcErrorShort;
|
private String lastRouteCalcErrorShort;
|
||||||
private long recalculateCountInInterval = 0;
|
private long recalculateCountInInterval = 0;
|
||||||
private int evalWaitInterval = 0;
|
private int evalWaitInterval = 0;
|
||||||
private boolean waitingNextJob;
|
|
||||||
|
|
||||||
private RouteCalculationProgressCallback progressRoute;
|
private RouteCalculationProgressCallback progressRoute;
|
||||||
|
|
||||||
RouteRecalculationThreadHelper(@NonNull RoutingHelper routingHelper) {
|
RouteRecalculationHelper(@NonNull RoutingHelper routingHelper) {
|
||||||
this.routingHelper = routingHelper;
|
this.routingHelper = routingHelper;
|
||||||
this.app = routingHelper.getApplication();
|
this.app = routingHelper.getApplication();
|
||||||
}
|
}
|
||||||
|
@ -50,7 +60,14 @@ class RouteRecalculationThreadHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isRouteBeingCalculated() {
|
boolean isRouteBeingCalculated() {
|
||||||
return currentRunningJob instanceof RouteRecalculationThread || waitingNextJob;
|
synchronized (routingHelper) {
|
||||||
|
for (Future<?> future : tasksMap.keySet()) {
|
||||||
|
if (!future.isDone()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetEvalWaitInterval() {
|
void resetEvalWaitInterval() {
|
||||||
|
@ -58,24 +75,31 @@ class RouteRecalculationThreadHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopCalculation() {
|
void stopCalculation() {
|
||||||
Thread job = currentRunningJob;
|
synchronized (routingHelper) {
|
||||||
if (job instanceof RouteRecalculationThread) {
|
for (Entry<Future<?>, RouteRecalculationTask> taskFuture : tasksMap.entrySet()) {
|
||||||
((RouteRecalculationThread) job).stopCalculation();
|
taskFuture.getValue().stopCalculation();
|
||||||
|
taskFuture.getKey().cancel(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopCalculationIfParamsChanged() {
|
void stopCalculationIfParamsNotChanged() {
|
||||||
Thread job = currentRunningJob;
|
synchronized (routingHelper) {
|
||||||
if (job instanceof RouteRecalculationThread) {
|
boolean hasPendingTasks = tasksMap.isEmpty();
|
||||||
RouteRecalculationThread thread = (RouteRecalculationThread) job;
|
for (Entry<Future<?>, RouteRecalculationTask> taskFuture : tasksMap.entrySet()) {
|
||||||
if (!thread.isParamsChanged()) {
|
RouteRecalculationTask task = taskFuture.getValue();
|
||||||
thread.stopCalculation();
|
if (!task.isParamsChanged()) {
|
||||||
|
taskFuture.getKey().cancel(false);
|
||||||
|
task.stopCalculation();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (hasPendingTasks) {
|
||||||
if (isFollowingMode()) {
|
if (isFollowingMode()) {
|
||||||
getVoiceRouter().announceBackOnRoute();
|
getVoiceRouter().announceBackOnRoute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private OsmandSettings getSettings() {
|
private OsmandSettings getSettings() {
|
||||||
return routingHelper.getSettings();
|
return routingHelper.getSettings();
|
||||||
|
@ -130,8 +154,6 @@ class RouteRecalculationThreadHelper {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// trigger voice prompt only if new route is in forward direction
|
// trigger voice prompt only if new route is in forward direction
|
||||||
// If route is in wrong direction after one more setLocation it will be recalculated
|
// If route is in wrong direction after one more setLocation it will be recalculated
|
||||||
if (!wrongMovementDirection || newRoute) {
|
if (!wrongMovementDirection || newRoute) {
|
||||||
|
@ -144,18 +166,15 @@ class RouteRecalculationThreadHelper {
|
||||||
|
|
||||||
void startRouteCalculationThread(RouteCalculationParams params, boolean paramsChanged, boolean updateProgress) {
|
void startRouteCalculationThread(RouteCalculationParams params, boolean paramsChanged, boolean updateProgress) {
|
||||||
synchronized (routingHelper) {
|
synchronized (routingHelper) {
|
||||||
final Thread prevRunningJob = currentRunningJob;
|
|
||||||
getSettings().LAST_ROUTE_APPLICATION_MODE.set(getAppMode());
|
getSettings().LAST_ROUTE_APPLICATION_MODE.set(getAppMode());
|
||||||
RouteRecalculationThread newThread = new RouteRecalculationThread("Calculating route", params, paramsChanged);
|
RouteRecalculationTask newTask = new RouteRecalculationTask(this, params, paramsChanged);
|
||||||
currentRunningJob = newThread;
|
lastTask = newTask;
|
||||||
startProgress(params);
|
startProgress(params);
|
||||||
if (updateProgress) {
|
if (updateProgress) {
|
||||||
updateProgress(params);
|
updateProgress(params);
|
||||||
}
|
}
|
||||||
if (prevRunningJob != null) {
|
Future<?> future = executor.submit(newTask);
|
||||||
newThread.setWaitPrevJob(prevRunningJob);
|
tasksMap.put(future, newTask);
|
||||||
}
|
|
||||||
currentRunningJob.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +184,7 @@ class RouteRecalculationThreadHelper {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// do not evaluate very often
|
// do not evaluate very often
|
||||||
if ((currentRunningJob == null && System.currentTimeMillis() - lastTimeEvaluatedRoute > evalWaitInterval)
|
if ((!isRouteBeingCalculated() && System.currentTimeMillis() - lastTimeEvaluatedRoute > evalWaitInterval)
|
||||||
|| paramsChanged || !onlyStartPointChanged) {
|
|| paramsChanged || !onlyStartPointChanged) {
|
||||||
if (System.currentTimeMillis() - lastTimeEvaluatedRoute < RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL) {
|
if (System.currentTimeMillis() - lastTimeEvaluatedRoute < RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL) {
|
||||||
recalculateCountInInterval++;
|
recalculateCountInInterval++;
|
||||||
|
@ -234,11 +253,7 @@ class RouteRecalculationThreadHelper {
|
||||||
public void run() {
|
public void run() {
|
||||||
RouteCalculationProgress calculationProgress = params.calculationProgress;
|
RouteCalculationProgress calculationProgress = params.calculationProgress;
|
||||||
if (isRouteBeingCalculated()) {
|
if (isRouteBeingCalculated()) {
|
||||||
Thread t = currentRunningJob;
|
if (lastTask != null && lastTask.params == params) {
|
||||||
if (t instanceof RouteRecalculationThread && ((RouteRecalculationThread) t).params != params) {
|
|
||||||
// different calculation started
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
progressRoute.updateProgress((int) calculationProgress.getLinearProgress());
|
progressRoute.updateProgress((int) calculationProgress.getLinearProgress());
|
||||||
if (calculationProgress.requestPrivateAccessRouting) {
|
if (calculationProgress.requestPrivateAccessRouting) {
|
||||||
progressRoute.requestPrivateAccessRouting();
|
progressRoute.requestPrivateAccessRouting();
|
||||||
|
@ -268,23 +283,21 @@ class RouteRecalculationThreadHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showMessage(final String msg) {
|
private static class RouteRecalculationTask implements Runnable {
|
||||||
app.runInUIThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
app.showToastMessage(msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class RouteRecalculationThread extends Thread {
|
|
||||||
|
|
||||||
|
private final RouteRecalculationHelper routingThreadHelper;
|
||||||
|
private final RoutingHelper routingHelper;
|
||||||
private final RouteCalculationParams params;
|
private final RouteCalculationParams params;
|
||||||
private final boolean paramsChanged;
|
private final boolean paramsChanged;
|
||||||
private Thread prevRunningJob;
|
|
||||||
|
|
||||||
public RouteRecalculationThread(String name, RouteCalculationParams params, boolean paramsChanged) {
|
String routeCalcError;
|
||||||
super(name);
|
String routeCalcErrorShort;
|
||||||
|
int evalWaitInterval = 0;
|
||||||
|
|
||||||
|
public RouteRecalculationTask(@NonNull RouteRecalculationHelper routingThreadHelper,
|
||||||
|
@NonNull RouteCalculationParams params, boolean paramsChanged) {
|
||||||
|
this.routingThreadHelper = routingThreadHelper;
|
||||||
|
this.routingHelper = routingThreadHelper.routingHelper;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.paramsChanged = paramsChanged;
|
this.paramsChanged = paramsChanged;
|
||||||
if (params.calculationProgress == null) {
|
if (params.calculationProgress == null) {
|
||||||
|
@ -300,39 +313,26 @@ class RouteRecalculationThreadHelper {
|
||||||
params.calculationProgress.isCancelled = true;
|
params.calculationProgress.isCancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OsmandSettings getSettings() {
|
||||||
|
return routingHelper.getSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showMessage(final String msg) {
|
||||||
|
final OsmandApplication app = routingHelper.getApplication();
|
||||||
|
app.runInUIThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
app.showToastMessage(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized (routingHelper) {
|
|
||||||
routingHelper.resetRouteWasFinished();
|
|
||||||
currentRunningJob = this;
|
|
||||||
waitingNextJob = prevRunningJob != null;
|
|
||||||
}
|
|
||||||
if (prevRunningJob != null) {
|
|
||||||
while (prevRunningJob.isAlive()) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(50);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
synchronized (routingHelper) {
|
|
||||||
if (params.calculationProgress.isCancelled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentRunningJob = this;
|
|
||||||
waitingNextJob = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastRouteCalcError = null;
|
|
||||||
lastRouteCalcErrorShort = null;
|
|
||||||
RouteProvider provider = routingHelper.getProvider();
|
RouteProvider provider = routingHelper.getProvider();
|
||||||
OsmandSettings settings = getSettings();
|
OsmandSettings settings = getSettings();
|
||||||
RouteCalculationResult res = provider.calculateRouteImpl(params);
|
RouteCalculationResult res = provider.calculateRouteImpl(params);
|
||||||
if (params.calculationProgress.isCancelled) {
|
if (params.calculationProgress.isCancelled) {
|
||||||
synchronized (routingHelper) {
|
|
||||||
currentRunningJob = null;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final boolean onlineSourceWithoutInternet = !res.isCalculated() &&
|
final boolean onlineSourceWithoutInternet = !res.isCalculated() &&
|
||||||
|
@ -353,37 +353,54 @@ class RouteRecalculationThreadHelper {
|
||||||
params.resultListener.onRouteCalculated(res);
|
params.resultListener.onRouteCalculated(res);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evalWaitInterval = Math.max(3000, evalWaitInterval * 3 / 2); // for Issue #3899
|
evalWaitInterval = Math.max(3000, routingThreadHelper.evalWaitInterval * 3 / 2); // for Issue #3899
|
||||||
evalWaitInterval = Math.min(evalWaitInterval, 120000);
|
evalWaitInterval = Math.min(evalWaitInterval, 120000);
|
||||||
}
|
}
|
||||||
currentRunningJob = null;
|
|
||||||
}
|
}
|
||||||
|
OsmandApplication app = routingHelper.getApplication();
|
||||||
if (res.isCalculated()) {
|
if (res.isCalculated()) {
|
||||||
if (!params.inSnapToRoadMode && !params.inPublicTransportMode) {
|
if (!params.inSnapToRoadMode && !params.inPublicTransportMode) {
|
||||||
setNewRoute(prev, res, params.start);
|
routingThreadHelper.setNewRoute(prev, res, params.start);
|
||||||
}
|
}
|
||||||
} else if (onlineSourceWithoutInternet) {
|
} else if (onlineSourceWithoutInternet) {
|
||||||
lastRouteCalcError = app.getString(R.string.error_calculating_route)
|
routeCalcError = app.getString(R.string.error_calculating_route)
|
||||||
+ ":\n" + app.getString(R.string.internet_connection_required_for_online_route);
|
+ ":\n" + app.getString(R.string.internet_connection_required_for_online_route);
|
||||||
lastRouteCalcErrorShort = app.getString(R.string.error_calculating_route);
|
routeCalcErrorShort = app.getString(R.string.error_calculating_route);
|
||||||
showMessage(lastRouteCalcError); //$NON-NLS-1$
|
showMessage(routeCalcError);
|
||||||
} else {
|
} else {
|
||||||
if (res.getErrorMessage() != null) {
|
if (res.getErrorMessage() != null) {
|
||||||
lastRouteCalcError = app.getString(R.string.error_calculating_route) + ":\n" + res.getErrorMessage();
|
routeCalcError = app.getString(R.string.error_calculating_route) + ":\n" + res.getErrorMessage();
|
||||||
lastRouteCalcErrorShort = app.getString(R.string.error_calculating_route);
|
routeCalcErrorShort = app.getString(R.string.error_calculating_route);
|
||||||
showMessage(lastRouteCalcError); //$NON-NLS-1$
|
|
||||||
} else {
|
} else {
|
||||||
lastRouteCalcError = app.getString(R.string.empty_route_calculated);
|
routeCalcError = app.getString(R.string.empty_route_calculated);
|
||||||
lastRouteCalcErrorShort = app.getString(R.string.empty_route_calculated);
|
routeCalcErrorShort = app.getString(R.string.empty_route_calculated);
|
||||||
showMessage(lastRouteCalcError);
|
|
||||||
}
|
}
|
||||||
|
showMessage(routeCalcError);
|
||||||
}
|
}
|
||||||
app.getNotificationHelper().refreshNotification(NAVIGATION);
|
app.getNotificationHelper().refreshNotification(NAVIGATION);
|
||||||
lastTimeEvaluatedRoute = System.currentTimeMillis();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWaitPrevJob(Thread prevRunningJob) {
|
private class RouteRecalculationExecutor extends ThreadPoolExecutor {
|
||||||
this.prevRunningJob = prevRunningJob;
|
|
||||||
|
public RouteRecalculationExecutor() {
|
||||||
|
super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void afterExecute(Runnable r, Throwable t) {
|
||||||
|
super.afterExecute(r, t);
|
||||||
|
RouteRecalculationTask task = null;
|
||||||
|
synchronized (routingHelper) {
|
||||||
|
if (r instanceof Future<?>) {
|
||||||
|
task = tasksMap.remove(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t == null && task != null) {
|
||||||
|
evalWaitInterval = task.evalWaitInterval;
|
||||||
|
lastRouteCalcError = task.routeCalcError;
|
||||||
|
lastRouteCalcErrorShort = task.routeCalcErrorShort;
|
||||||
|
}
|
||||||
|
lastTimeEvaluatedRoute = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -54,7 +54,7 @@ public class RoutingHelper {
|
||||||
private OsmandSettings settings;
|
private OsmandSettings settings;
|
||||||
private final RouteProvider provider;
|
private final RouteProvider provider;
|
||||||
private final VoiceRouter voiceRouter;
|
private final VoiceRouter voiceRouter;
|
||||||
private final RouteRecalculationThreadHelper routeRecalculationHelper;
|
private final RouteRecalculationHelper routeRecalculationHelper;
|
||||||
private final TransportRoutingHelper transportRoutingHelper;
|
private final TransportRoutingHelper transportRoutingHelper;
|
||||||
|
|
||||||
private boolean isFollowingMode = false;
|
private boolean isFollowingMode = false;
|
||||||
|
@ -94,7 +94,7 @@ public class RoutingHelper {
|
||||||
settings = context.getSettings();
|
settings = context.getSettings();
|
||||||
voiceRouter = new VoiceRouter(this);
|
voiceRouter = new VoiceRouter(this);
|
||||||
provider = new RouteProvider();
|
provider = new RouteProvider();
|
||||||
routeRecalculationHelper = new RouteRecalculationThreadHelper(this);
|
routeRecalculationHelper = new RouteRecalculationHelper(this);
|
||||||
transportRoutingHelper = context.getTransportRoutingHelper();
|
transportRoutingHelper = context.getTransportRoutingHelper();
|
||||||
transportRoutingHelper.setRoutingHelper(this);
|
transportRoutingHelper.setRoutingHelper(this);
|
||||||
setAppMode(settings.APPLICATION_MODE.get());
|
setAppMode(settings.APPLICATION_MODE.get());
|
||||||
|
@ -505,7 +505,7 @@ public class RoutingHelper {
|
||||||
routeRecalculationHelper.recalculateRouteInBackground(currentLocation, finalLocation, intermediatePoints, currentGPXRoute,
|
routeRecalculationHelper.recalculateRouteInBackground(currentLocation, finalLocation, intermediatePoints, currentGPXRoute,
|
||||||
previousRoute.isCalculated() ? previousRoute : null, false, !targetPointsChanged);
|
previousRoute.isCalculated() ? previousRoute : null, false, !targetPointsChanged);
|
||||||
} else {
|
} else {
|
||||||
routeRecalculationHelper.stopCalculationIfParamsChanged();
|
routeRecalculationHelper.stopCalculationIfParamsNotChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
double projectDist = mode != null && mode.hasFastSpeed() ? posTolerance : posTolerance / 2;
|
double projectDist = mode != null && mode.hasFastSpeed() ? posTolerance : posTolerance / 2;
|
||||||
|
|
|
@ -13,37 +13,42 @@ import net.osmand.binary.BinaryMapIndexReader;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
import net.osmand.data.QuadRect;
|
import net.osmand.data.QuadRect;
|
||||||
import net.osmand.osm.edit.Node;
|
import net.osmand.osm.edit.Node;
|
||||||
import net.osmand.plus.settings.backend.ApplicationMode;
|
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.OsmandPlugin;
|
import net.osmand.plus.OsmandPlugin;
|
||||||
import net.osmand.plus.settings.backend.CommonPreference;
|
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.render.NativeOsmandLibrary;
|
import net.osmand.plus.render.NativeOsmandLibrary;
|
||||||
import net.osmand.plus.routing.RouteCalculationParams.RouteCalculationResultListener;
|
import net.osmand.plus.routing.RouteCalculationParams.RouteCalculationResultListener;
|
||||||
import net.osmand.plus.routing.RouteProvider.RouteService;
|
import net.osmand.plus.routing.RouteProvider.RouteService;
|
||||||
|
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||||
|
import net.osmand.plus.settings.backend.CommonPreference;
|
||||||
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.router.GeneralRouter;
|
import net.osmand.router.GeneralRouter;
|
||||||
|
import net.osmand.router.NativeTransportRoutingResult;
|
||||||
import net.osmand.router.RouteCalculationProgress;
|
import net.osmand.router.RouteCalculationProgress;
|
||||||
import net.osmand.router.RoutingConfiguration;
|
import net.osmand.router.RoutingConfiguration;
|
||||||
import net.osmand.router.TransportRoutePlanner;
|
import net.osmand.router.TransportRoutePlanner;
|
||||||
import net.osmand.router.TransportRouteResult;
|
|
||||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
|
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
|
||||||
import net.osmand.router.TransportRoutingContext;
|
import net.osmand.router.TransportRouteResult;
|
||||||
import net.osmand.router.TransportRoutingConfiguration;
|
import net.osmand.router.TransportRoutingConfiguration;
|
||||||
import net.osmand.router.NativeTransportRoutingResult;
|
import net.osmand.router.TransportRoutingContext;
|
||||||
import net.osmand.util.MapUtils;
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION;
|
import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION;
|
||||||
|
|
||||||
|
@ -53,10 +58,14 @@ public class TransportRoutingHelper {
|
||||||
|
|
||||||
private List<WeakReference<IRouteInformationListener>> listeners = new LinkedList<>();
|
private List<WeakReference<IRouteInformationListener>> listeners = new LinkedList<>();
|
||||||
|
|
||||||
private OsmandApplication app;
|
private final OsmandApplication app;
|
||||||
private ApplicationMode applicationMode = ApplicationMode.PUBLIC_TRANSPORT;
|
private ApplicationMode applicationMode = ApplicationMode.PUBLIC_TRANSPORT;
|
||||||
private RoutingHelper routingHelper;
|
private RoutingHelper routingHelper;
|
||||||
|
|
||||||
|
private final ExecutorService executor = new RouteRecalculationExecutor();
|
||||||
|
private final Map<Future<?>, RouteRecalculationTask> tasksMap = new LinkedHashMap<>();
|
||||||
|
private RouteRecalculationTask lastTask;
|
||||||
|
|
||||||
private List<TransportRouteResult> routes;
|
private List<TransportRouteResult> routes;
|
||||||
private Map<Pair<TransportRouteResultSegment, TransportRouteResultSegment>, RouteCalculationResult> walkingRouteSegments;
|
private Map<Pair<TransportRouteResultSegment, TransportRouteResultSegment>, RouteCalculationResult> walkingRouteSegments;
|
||||||
private int currentRoute = -1;
|
private int currentRoute = -1;
|
||||||
|
@ -64,11 +73,9 @@ public class TransportRoutingHelper {
|
||||||
private LatLon startLocation;
|
private LatLon startLocation;
|
||||||
private LatLon endLocation;
|
private LatLon endLocation;
|
||||||
|
|
||||||
private Thread currentRunningJob;
|
|
||||||
private String lastRouteCalcError;
|
private String lastRouteCalcError;
|
||||||
private String lastRouteCalcErrorShort;
|
private String lastRouteCalcErrorShort;
|
||||||
private long lastTimeEvaluatedRoute = 0;
|
private long lastTimeEvaluatedRoute = 0;
|
||||||
private boolean waitingNextJob;
|
|
||||||
|
|
||||||
private TransportRouteCalculationProgressCallback progressRoute;
|
private TransportRouteCalculationProgressCallback progressRoute;
|
||||||
|
|
||||||
|
@ -212,18 +219,14 @@ public class TransportRoutingHelper {
|
||||||
|
|
||||||
private void startRouteCalculationThread(TransportRouteCalculationParams params) {
|
private void startRouteCalculationThread(TransportRouteCalculationParams params) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
final Thread prevRunningJob = currentRunningJob;
|
|
||||||
app.getSettings().LAST_ROUTE_APPLICATION_MODE.set(routingHelper.getAppMode());
|
app.getSettings().LAST_ROUTE_APPLICATION_MODE.set(routingHelper.getAppMode());
|
||||||
RouteRecalculationThread newThread =
|
RouteRecalculationTask newTask = new RouteRecalculationTask(this, params,
|
||||||
new RouteRecalculationThread("Calculating public transport route", params,
|
|
||||||
app.getSettings().SAFE_MODE.get() ? null : NativeOsmandLibrary.getLoadedLibrary());
|
app.getSettings().SAFE_MODE.get() ? null : NativeOsmandLibrary.getLoadedLibrary());
|
||||||
currentRunningJob = newThread;
|
lastTask = newTask;
|
||||||
startProgress(params);
|
startProgress(params);
|
||||||
updateProgress(params);
|
updateProgress(params);
|
||||||
if (prevRunningJob != null) {
|
Future<?> future = executor.submit(newTask);
|
||||||
newThread.setWaitPrevJob(prevRunningJob);
|
tasksMap.put(future, newTask);
|
||||||
}
|
|
||||||
currentRunningJob.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,10 +253,7 @@ public class TransportRoutingHelper {
|
||||||
if (isRouteBeingCalculated()) {
|
if (isRouteBeingCalculated()) {
|
||||||
float pr = calculationProgress.getLinearProgress();
|
float pr = calculationProgress.getLinearProgress();
|
||||||
progressRoute.updateProgress((int) pr);
|
progressRoute.updateProgress((int) pr);
|
||||||
Thread t = currentRunningJob;
|
if (lastTask != null && lastTask.params == params) {
|
||||||
if (t instanceof RouteRecalculationThread && ((RouteRecalculationThread) t).params != params) {
|
|
||||||
// different calculation started
|
|
||||||
} else {
|
|
||||||
updateProgress(params);
|
updateProgress(params);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -268,7 +268,23 @@ public class TransportRoutingHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRouteBeingCalculated() {
|
public boolean isRouteBeingCalculated() {
|
||||||
return currentRunningJob instanceof RouteRecalculationThread || waitingNextJob;
|
synchronized (this) {
|
||||||
|
for (Future<?> future : tasksMap.keySet()) {
|
||||||
|
if (!future.isDone()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopCalculation() {
|
||||||
|
synchronized (this) {
|
||||||
|
for (Map.Entry<Future<?>, RouteRecalculationTask> taskFuture : tasksMap.entrySet()) {
|
||||||
|
taskFuture.getValue().stopCalculation();
|
||||||
|
taskFuture.getKey().cancel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setNewRoute(final List<TransportRouteResult> res) {
|
private void setNewRoute(final List<TransportRouteResult> res) {
|
||||||
|
@ -322,9 +338,7 @@ public class TransportRoutingHelper {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.endLocation = newFinalLocation;
|
this.endLocation = newFinalLocation;
|
||||||
if (currentRunningJob instanceof RouteRecalculationThread) {
|
stopCalculation();
|
||||||
((RouteRecalculationThread) currentRunningJob).stopCalculation();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCurrentLocation(LatLon currentLocation) {
|
private void setCurrentLocation(LatLon currentLocation) {
|
||||||
|
@ -335,15 +349,6 @@ public class TransportRoutingHelper {
|
||||||
recalculateRouteInBackground(currentLocation, endLocation);
|
recalculateRouteInBackground(currentLocation, endLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showMessage(final String msg) {
|
|
||||||
app.runInUIThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
app.showToastMessage(msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public QuadRect getTransportRouteRect(@NonNull TransportRouteResult result) {
|
public QuadRect getTransportRouteRect(@NonNull TransportRouteResult result) {
|
||||||
TransportRoutingHelper transportRoutingHelper = app.getTransportRoutingHelper();
|
TransportRoutingHelper transportRoutingHelper = app.getTransportRoutingHelper();
|
||||||
|
@ -403,7 +408,7 @@ public class TransportRoutingHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class WalkingRouteSegment {
|
private static class WalkingRouteSegment {
|
||||||
TransportRouteResultSegment s1;
|
TransportRouteResultSegment s1;
|
||||||
TransportRouteResultSegment s2;
|
TransportRouteResultSegment s2;
|
||||||
LatLon start;
|
LatLon start;
|
||||||
|
@ -436,18 +441,24 @@ public class TransportRoutingHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RouteRecalculationThread extends Thread {
|
private static class RouteRecalculationTask implements Runnable {
|
||||||
|
|
||||||
|
private final TransportRoutingHelper transportRoutingHelper;
|
||||||
|
private final RoutingHelper routingHelper;
|
||||||
private final TransportRouteCalculationParams params;
|
private final TransportRouteCalculationParams params;
|
||||||
private Thread prevRunningJob;
|
|
||||||
|
|
||||||
private final Queue<WalkingRouteSegment> walkingSegmentsToCalculate = new ConcurrentLinkedQueue<>();
|
private final Queue<WalkingRouteSegment> walkingSegmentsToCalculate = new ConcurrentLinkedQueue<>();
|
||||||
private Map<Pair<TransportRouteResultSegment, TransportRouteResultSegment>, RouteCalculationResult> walkingRouteSegments = new HashMap<>();
|
private Map<Pair<TransportRouteResultSegment, TransportRouteResultSegment>, RouteCalculationResult> walkingRouteSegments = new HashMap<>();
|
||||||
private boolean walkingSegmentsCalculated;
|
private boolean walkingSegmentsCalculated;
|
||||||
private NativeLibrary lib;
|
private final NativeLibrary lib;
|
||||||
|
|
||||||
public RouteRecalculationThread(String name, TransportRouteCalculationParams params, NativeLibrary library) {
|
String routeCalcError;
|
||||||
super(name);
|
String routeCalcErrorShort;
|
||||||
|
|
||||||
|
public RouteRecalculationTask(@NonNull TransportRoutingHelper transportRoutingHelper,
|
||||||
|
@NonNull TransportRouteCalculationParams params, @Nullable NativeLibrary library) {
|
||||||
|
this.transportRoutingHelper = transportRoutingHelper;
|
||||||
|
this.routingHelper = transportRoutingHelper.routingHelper;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.lib = library;
|
this.lib = library;
|
||||||
if (params.calculationProgress == null) {
|
if (params.calculationProgress == null) {
|
||||||
|
@ -459,9 +470,9 @@ public class TransportRoutingHelper {
|
||||||
params.calculationProgress.isCancelled = true;
|
params.calculationProgress.isCancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO Check if native lib available and calculate route there.
|
* TODO Check if native lib available and calculate route there.
|
||||||
|
*
|
||||||
* @param params
|
* @param params
|
||||||
* @return
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
@ -509,7 +520,6 @@ public class TransportRoutingHelper {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private RouteCalculationParams getWalkingRouteParams() {
|
private RouteCalculationParams getWalkingRouteParams() {
|
||||||
|
|
||||||
ApplicationMode walkingMode = ApplicationMode.PEDESTRIAN;
|
ApplicationMode walkingMode = ApplicationMode.PEDESTRIAN;
|
||||||
|
|
||||||
final WalkingRouteSegment walkingRouteSegment = walkingSegmentsToCalculate.poll();
|
final WalkingRouteSegment walkingRouteSegment = walkingSegmentsToCalculate.poll();
|
||||||
|
@ -517,13 +527,14 @@ public class TransportRoutingHelper {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OsmandApplication app = routingHelper.getApplication();
|
||||||
Location start = new Location("");
|
Location start = new Location("");
|
||||||
start.setLatitude(walkingRouteSegment.start.getLatitude());
|
start.setLatitude(walkingRouteSegment.start.getLatitude());
|
||||||
start.setLongitude(walkingRouteSegment.start.getLongitude());
|
start.setLongitude(walkingRouteSegment.start.getLongitude());
|
||||||
LatLon end = new LatLon(walkingRouteSegment.end.getLatitude(), walkingRouteSegment.end.getLongitude());
|
LatLon end = new LatLon(walkingRouteSegment.end.getLatitude(), walkingRouteSegment.end.getLongitude());
|
||||||
|
|
||||||
final float currentDistanceFromBegin =
|
final float currentDistanceFromBegin =
|
||||||
RouteRecalculationThread.this.params.calculationProgress.distanceFromBegin +
|
RouteRecalculationTask.this.params.calculationProgress.distanceFromBegin +
|
||||||
(walkingRouteSegment.s1 != null ? (float) walkingRouteSegment.s1.getTravelDist() : 0);
|
(walkingRouteSegment.s1 != null ? (float) walkingRouteSegment.s1.getTravelDist() : 0);
|
||||||
|
|
||||||
final RouteCalculationParams params = new RouteCalculationParams();
|
final RouteCalculationParams params = new RouteCalculationParams();
|
||||||
|
@ -547,8 +558,8 @@ public class TransportRoutingHelper {
|
||||||
float p = Math.max(params.calculationProgress.distanceFromBegin,
|
float p = Math.max(params.calculationProgress.distanceFromBegin,
|
||||||
params.calculationProgress.distanceFromEnd);
|
params.calculationProgress.distanceFromEnd);
|
||||||
|
|
||||||
RouteRecalculationThread.this.params.calculationProgress.distanceFromBegin =
|
RouteRecalculationTask.this.params.calculationProgress.distanceFromBegin =
|
||||||
Math.max(RouteRecalculationThread.this.params.calculationProgress.distanceFromBegin, currentDistanceFromBegin + p);
|
Math.max(RouteRecalculationTask.this.params.calculationProgress.distanceFromBegin, currentDistanceFromBegin + p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -571,7 +582,7 @@ public class TransportRoutingHelper {
|
||||||
params.resultListener = new RouteCalculationResultListener() {
|
params.resultListener = new RouteCalculationResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onRouteCalculated(RouteCalculationResult route) {
|
public void onRouteCalculated(RouteCalculationResult route) {
|
||||||
RouteRecalculationThread.this.walkingRouteSegments.put(new Pair<>(walkingRouteSegment.s1, walkingRouteSegment.s2), route);
|
RouteRecalculationTask.this.walkingRouteSegments.put(new Pair<>(walkingRouteSegment.s1, walkingRouteSegment.s2), route);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -619,26 +630,18 @@ public class TransportRoutingHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showMessage(final String msg) {
|
||||||
|
final OsmandApplication app = routingHelper.getApplication();
|
||||||
|
app.runInUIThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized (TransportRoutingHelper.this) {
|
app.showToastMessage(msg);
|
||||||
currentRunningJob = this;
|
|
||||||
waitingNextJob = prevRunningJob != null;
|
|
||||||
}
|
|
||||||
if (prevRunningJob != null) {
|
|
||||||
while (prevRunningJob.isAlive()) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(50);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
synchronized (TransportRoutingHelper.this) {
|
|
||||||
currentRunningJob = this;
|
|
||||||
waitingNextJob = false;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
List<TransportRouteResult> res = null;
|
List<TransportRouteResult> res = null;
|
||||||
String error = null;
|
String error = null;
|
||||||
try {
|
try {
|
||||||
|
@ -651,38 +654,52 @@ public class TransportRoutingHelper {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
}
|
}
|
||||||
if (params.calculationProgress.isCancelled) {
|
if (params.calculationProgress.isCancelled) {
|
||||||
synchronized (TransportRoutingHelper.this) {
|
|
||||||
currentRunningJob = null;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
synchronized (TransportRoutingHelper.this) {
|
synchronized (transportRoutingHelper) {
|
||||||
routes = res;
|
transportRoutingHelper.routes = res;
|
||||||
TransportRoutingHelper.this.walkingRouteSegments = walkingRouteSegments;
|
transportRoutingHelper.walkingRouteSegments = walkingRouteSegments;
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
if (params.resultListener != null) {
|
if (params.resultListener != null) {
|
||||||
params.resultListener.onRouteCalculated(res);
|
params.resultListener.onRouteCalculated(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentRunningJob = null;
|
|
||||||
}
|
}
|
||||||
|
OsmandApplication app = routingHelper.getApplication();
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
setNewRoute(res);
|
transportRoutingHelper.setNewRoute(res);
|
||||||
} else if (error != null) {
|
} else if (error != null) {
|
||||||
lastRouteCalcError = app.getString(R.string.error_calculating_route) + ":\n" + error;
|
routeCalcError = app.getString(R.string.error_calculating_route) + ":\n" + error;
|
||||||
lastRouteCalcErrorShort = app.getString(R.string.error_calculating_route);
|
routeCalcErrorShort = app.getString(R.string.error_calculating_route);
|
||||||
showMessage(lastRouteCalcError);
|
showMessage(routeCalcError);
|
||||||
} else {
|
} else {
|
||||||
lastRouteCalcError = app.getString(R.string.empty_route_calculated);
|
routeCalcError = app.getString(R.string.empty_route_calculated);
|
||||||
lastRouteCalcErrorShort = app.getString(R.string.empty_route_calculated);
|
routeCalcErrorShort = app.getString(R.string.empty_route_calculated);
|
||||||
showMessage(lastRouteCalcError);
|
showMessage(routeCalcError);
|
||||||
}
|
}
|
||||||
app.getNotificationHelper().refreshNotification(NAVIGATION);
|
app.getNotificationHelper().refreshNotification(NAVIGATION);
|
||||||
lastTimeEvaluatedRoute = System.currentTimeMillis();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWaitPrevJob(Thread prevRunningJob) {
|
private class RouteRecalculationExecutor extends ThreadPoolExecutor {
|
||||||
this.prevRunningJob = prevRunningJob;
|
|
||||||
|
public RouteRecalculationExecutor() {
|
||||||
|
super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void afterExecute(Runnable r, Throwable t) {
|
||||||
|
super.afterExecute(r, t);
|
||||||
|
RouteRecalculationTask task = null;
|
||||||
|
synchronized (TransportRoutingHelper.this) {
|
||||||
|
if (r instanceof Future<?>) {
|
||||||
|
task = tasksMap.remove(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t == null && task != null) {
|
||||||
|
lastRouteCalcError = task.routeCalcError;
|
||||||
|
lastRouteCalcErrorShort = task.routeCalcErrorShort;
|
||||||
|
}
|
||||||
|
lastTimeEvaluatedRoute = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue