I've got a question on making an navigation app more faster and more stable.
The basic layer of my app is a simple mapview, covered with several overlays (2 markers for start and destination and one for the route).
My idea is to implement a thread to display the route, so that the app won't hang up during the calculation of a more complex route (like it does right now).
After implementing the thread there are no updates shows any more, maybe you can help me with a short glance at an excerpt of my code below:
private class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location loc) {
posUser = new GeoPoint((int) (loc.getLatitude() * 1E6), (int) (loc
.getLongitude() * 1E6));
new Thread(){
public void run(){
mapView.invalidate();
// Erase old overlays
mapView.getOverlays().clear();
// Draw updated overlay elements and adjust basic map settings
updateText();
if(firstRefresh){
adjustMap();
firstRefresh = false;
}
getAndPaintRoute();
drawMarkers();
}
};
}
Some features have been summarized to a method like "drawMarkers()" or "updateText()"...(they don't need any more attention ;-))
When are you actually asking for the thread to run? I only see code for creating it. If you did, you'd discover that only the main (UI) thread is allowed to update, as RPond notes.
Instead, split off your work and post the results back to the main thread via a Handler.
You can only update the UI on the UI thread so I think that is the problem. Check out this article on threading for solutions to this problem.
I may be barking up the wrong tree here, but my guess is that it's something to do with the fact that you're making changes to the MapView on a thread that isn't the UI thread.
I'd expect this to result in one of these possibilities:
Your changes are throwing an exception that you're not seeing (again possibly because it's on another thread)
Your changes are being ignored because they're being made on the wrong thread.
The map's being updated but your UI thread doesn't know it needs to redraw the map.
Hope this helps - at least by pointing you in vaguely the right direction.
One would hope you're not using the Google MapView for rendering your navigation, since from my reading of the terms and conditions, navigation is not included in the acceptable use policy.
From the Android Maps APIs Terms of Service
Under this Section 8, you must not (nor may you permit anyone else to):
...
8.7. use the Service or Content with any products, systems, or applications for or in connection with (a) real time navigation or route guidance based on position input from a sensor (including but not limited to any visual or audible turn-by-turn route guidance);
...
Related
So Mapbox provides an awesome Navigation SDK for Android, and what I have been trying to do is create my own routes, representing each point as a Feature in a Geojson file, and then passing them on to the MapMatching module to get directions that I can then pass to the Navigation Engine.
My solution evolves into two main parts. The first one involves iterating through the points I want navigation to go through, by adding them as input to the .coordinates element of MapboxMapMatching.builder() and subsequently converting this to
.toDirectionRoute(); per Mapbox instructions and example here: https://www.mapbox.com/android-docs/java/examples/use-map-matching/
private void getWaypointRoute(List<Point> features) {
originPosition = features.get(0);
destinationPosition = features.get(features.size() - 1);
MapboxMapMatching.builder()
.accessToken(Mapbox.getAccessToken())
.coordinates(features)
.steps(true) // Setting this will determine whether to return steps and turn-by-turn instructions.
.voiceInstructions(true)
.bannerInstructions(true)
.profile(DirectionsCriteria.PROFILE_DRIVING)
.build().enqueueCall(new Callback<MapMatchingResponse>() {
#Override
public void onResponse(Call<MapMatchingResponse> call, Response<MapMatchingResponse> response) {
if (response.body() == null) {
Log.e(TAG, "Map matching has failed.");
return;
}
if (response.isSuccessful()) {
currentRoute = response.body().matchings().get(0).toDirectionRoute();
The second bit involves just passing 'currentRoute' to the NavigationLauncher as shown below:
NavigationLauncherOptions options = NavigationLauncherOptions.builder()
.origin(origin)
.destination(destination)
.directionsRoute(currentRoute)
.shouldSimulateRoute(simulateRoute)
.enableOffRouteDetection(false)
.build();
// Call this method with Context from within an Activity
NavigationLauncher.startNavigation(MainActivity.this, options);
An example of the route can be seen here Android Simulator Snapshot with Route . Each point across the route, is an intersection, and corresponds to a feature in my GeoJson file. The problem becomes when I launch the navigation. Every time, either in the simulator or on a real device, each point is interpreted as a destination so the voice command goes 'You have reached your first (second, third etc) destination'. I find this annoying as I would like to have a single route with a destination and that's it. I would just like to have this points so I have my own custom path, instead of the shortest path typically returned by routing applications. I try to avoid the problem by setting voiceInstructions off but then the system goes bananans and the navigation screen moves to lat, lng (0,0) which is pretty much somewhere West of Africa. Any help on how I could resolve this it would be greatly appreciated and I would be happy to buy a beer or two for the person that provides the right answer. I have reached out to Mapbox Support as well but we have not found an answer to the problem so I asked them to escalate it internally within their engineering team, as I believe, although the problem I am solving is not uncommon, it is still not very much tested by developers. Cheers!
So here I am and after the kind support of Mapbox Support and Rafa Gutierrez
I can now answer this post myself.
The problem has been arising due to MapboxMapMatching automatically setting .coordinates as waypoints. If instead, one edits explicitly the waypoints variable to have only two waypoints: origin and destination, then the system is able to process the input customised route without translating each input coordinate as a waypoint. The code example below hopefully clarifies the point described above:
MapboxMapMatching.builder()
.accessToken(Mapbox.getAccessToken())
.coordinates(lineStringRep.coordinates())
.waypoints(OD)
.steps(true)
.voiceInstructions(true)
.bannerInstructions(true)
.profile(DirectionsCriteria.PROFILE_DRIVING)
.build().enqueueCall(new Callback<MapMatchingResponse>()
where OD is an array of integers storing the first (origin) and last index (destination) of your coordinates
OD[0] = 0;
OD[1] = features.size() - 1;
Google Maps API on Android still has an issue where it takes a long time to load many markers/circles. In my case I am drawing 130+ circles with Lat/Lng to the Google Map. This process takes 20s to complete. Here is the code for adding the circles which is called after #Override public void onMapLoaded:
List<Crime> crimes = mCrimeManager.getListOfCrimes();
Log.d("CRIME SIZE TAG", " " + crimes.size());
for(Crime crime:crimes)
{
if(!(getLocationOfCrime(crime) == null))
{
Circle circle = googleMap.addCircle(new CircleOptions()
.clickable(true)
.radius(5)
.center(getLocationOfCrime(crime)));
circle.setTag(crime);
I know that this is the issue because removing this code causes everything to run smoothly with no delay.
I have looked at multiple questions in the past but most are using clustering or loading only with a certain range which can not be applied to my case. I have tried running this on AsyncTask with a runOnUiThread(); in doInBackground(); but that still yields same performance.
Okay guys here is the solution. It turns out adding the markers is not processor intensive. Geolocating is the issue. If you are running into this issue it is because you are geolocating on the main thread. The way I fixed my problem was putting geolocation calculations on AsyncTask (doInBackground();) then on onPostExecute adding the markers.
Still there is an issue where geolocation takes time(UI running smoothly but no markers), but this is a Google Maps API issue. I sorta fixed this by doing the geolocation once at startup then storing everything into SQLiteDataBase then reading in from there after each time.
Thanks
I am designing the map-based app and I want to do certain set of steps when user's location has been acquired (blue dot has been shown on the map). On some of the devices it may take up to 1 min due to location services ramp-up process.. So the question is how do I know when Android Map managed to acquire my location, so I can start doing the rest of initialisation process?
I was thinking about implementing my own LocationListener and trigger callback when I receive location, but this solution seems worthless since I need to only know the point when current location has been acquired and blue dot has been shown. I don't care about location updates.
If you are talking about the blue dot on a map, I suppose you add the MyLocationOverlay to your map. If it is so, there is an easy way to do what you want - MyLocationOverlay has runOnFirstFix(Rannable) interface, so provided Runnable will be run as soon as the map acquires your current location:
this.currentLocationOverlay.runOnFirstFix(new Runnable() {
#Override
public void run() {
//do your magic here
}
});
I am looking to figure out how I can replicate the follow current location feature that exists in Maps on Android (and iPhone) using the Google Maps external library. The way the feature works in Maps is that when you press the button it begins panning with your location until you touch (and/or move) the map. Using the following I have been able to get my app to keep the current location on the screen but not keep the current location in the center of the screen (it pans when the location gets near the edge of the screen whereas Maps keeps it in the center the whole time):
butMapCurrentLocation.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
runOnUiThread(new Runnable() {
public void run() {
mapController.animateTo(myLocationOverlay.getMyLocation());
}
});
}
});
I also tried setCenter rather than animateTo and they seem to have the same effect.
Additionally it is worth noting I need to be able to stop this function that keeps the location in the center (such as when another button is pressed or the map is touched). The Runnable above doesn't stop right away and I cannot get it to stop properly when requested even when using .cancel as discussed here: Android: How do I stop Runnable?
There is no official way yet to do that in Google Maps v2.
You can do what you want however. There is two things you have to do:
Register for location updates from some LocationProviders (eg gps) to get the user's location. When you get the location animate map to the new location.
Stop this when the user moved the map.
So doing 1. is easy, doing 2. is not possible officially with the Maps v2 api. Look at this issue for more details.
The workaround suggested there is to create your own MapView (extend the original MapView) and override it's dispatchTouchEvent method. There you can catch any touch events and can detect if the user touched/moved the map.
I'm working on a custom tween effect targeting Android 2.2. This is not a straight View animation (many things are happening based on the progress of the tween), so the Animation classes available in 2.2 aren't sufficient (apparently Animator does this but is not available).
I've implemented the basic logic (porting JS and AS tweening engines I'd written earlier), and it seems to work fine, but is a little slow. For example, running an interval of 25ms in JS or AS produces a smooth visual effect, but seems "chunky" in the Android implementation - reducing the interval to 10ms seemed to help some but it certainly isn't as smooth as the built-in animations.
I'm using Timer and Timer task - I've read that ScheduledThreadPoolExecutor is "preferred" but from what I've read the advantages seem to be more to do with exception handling and multiple tasks (I'll only ever have the one tween running).
Is Timer particularly slow? If ScheduledThreadPoolExecutor more efficient? Is there another, better alternative that I'm not aware of?
TYIA
for future searchers, the answer was to use just a straight Handler and sendMessage (without a delay).
after lots of experimentation, including Threads, Timers, Executors, etc, the best performance, most predictable result, and simplest code was basically just:
private Handler handler = new Handler() {
#Override
public void handleMessage(final Message message) {
switch (message.what) {
case TWEEN:
try {
double progress = timeKeeper.getEasedProgress(5);
float position = (float) originalValue + ((destinationValue - originalValue) * progress));
setValue(position);
if(!timeKeeper.atEnd()){
sendEmptyMessage(TWEEN);
}
} catch (Exception e) {
}
}
}
};
where originalValue, destinationValue and setValue are just arbitrary members to handle the delta of the tween. timeKeeper is a very simple class that just measures ellapsed time, and returns the delta (ellapsed / duration). getEasedProgress just applies some basic easing interpolation to that delta.
thanks to pskink from the google android developers mailing list for pointing me in the right direction.
You do not need a ScheduledThreadPoolExecutor because you do not need a ThreadPool, you only need a single thread to manage your animation. The slowdown is probably in the implementation of your animation engine. I'm not sure how effective Timer is.
The main advantage I see with ScheduledThreadPoolExecutor is you can pool number of threads, if one thread is somehow hungup other thread can be used from the defined pool. Here is interesting SO discussion on this topic.