Calculate route using Executer instead of Thread

This commit is contained in:
max-klaus 2020-12-20 18:59:48 +03:00
parent cf916a5da7
commit c860f8eea4
3 changed files with 213 additions and 179 deletions

View file

@ -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();
} }
} }
} }

View file

@ -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;

View file

@ -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();
} }
} }
} }