0

I apologize profusely right at the start, because I know that answering this question is going to be like nailing jello to a wall. I'll give as many details as I can, but there aren't many to give.

I'm developing a Blackberry app that uses GPS to get a user's location every xx minutes (could be 15, 30, or 60 depending on the preferences the user sets); it sends the location data to a web service.

It runs fine and reports the user's location fine, but only for about 16-18 hours, and then the app stops reporting and the user-interface no longer works. Doesn't throw any errors, it just won't respond to user selections anymore until the app is re-booted.

It's strange to me that the app crash is fairly predictable - it never crashes after, say, 8 hours, or 10 hours, it's always in that 16-18 hour window, which suggests to me that the crash is related to the GPS/WebService reporting.

Basically, if I look at it from the GPS/WebService standpoint, it looks as though the app can manage to report to the WebService 36 times (every half-hour for 18 hours), and then it dies.

I'll post a bit of the code here in the hopes that it will trigger an idea for someone.

private void updateGPSLocation(){
    gps_interval_timer.scheduleAtFixedRate(new TimerTask() {
       public void run() {
         try{

           getGPSLocation();
           if(longitude != 0.0 && latitude != 0.0)
           {
             updateSubscriberLocation();
           }

          }catch(Exception ex){                 
             }
      }
  }, 0, gps_interval);
}

And here's the code for the getGPSLocation() call referenced in the previous code:

public void getGPSLocation(){
     try{
          //GPS thread
           BlackBerryLocationProvider provider = (BlackBerryLocationProvider)LocationProvider.getInstance(new BlackBerryCriteria(GPSInfo.GPS_MODE_ASSIST));

          //geolocation thread
           BlackBerryLocationProvider provider1 = (BlackBerryLocationProvider)LocationProvider.getInstance(new BlackBerryCriteria(LocationInfo.GEOLOCATION_MODE_CELL));

        if (provider != null){
            provider.setLocationListener(new LocationListenerImpl(), _interval, 1, 1);
         }

         if(provider1 != null){
            provider1.setLocationListener(new LocationListenerImpl(), _interval, 1, 1);
         }
      }
      catch(Exception le){
       }        
}

Again, I'm sorry, I know this is kind of an open-ended question. Are there any known issues with the Blackberry GPS? Because I'm scheduling the interval-based reporting using a timer task, is it possible that I'm just sucking up all the available memory by putting scheduled timer tasks in the queue? Do I need to explicitly dispose of the timer task using the cancel() method? Something like:

gps_interval_timer.scheduleAtFixedRate(new TimerTask() {
       public void run() {
         try{

           getGPSLocation();
           if(longitude != 0.0 && latitude != 0.0)
           {
             updateSubscriberLocation();
           }

          }catch(Exception ex){                 
             }
          finally
          {
             this.cancel();
          }

      }
  }, 0, gps_interval);

Any help is most appreciated. Thank you!

UPDATED WITH LocationListener code

private class LocationListenerImpl implements LocationListener { 
    public void locationUpdated(LocationProvider provider, Location location) {
    if(location.isValid()) {
       longitude = location.getQualifiedCoordinates().getLongitude();
       latitude = location.getQualifiedCoordinates().getLatitude(); 
     }
   }
    public void providerStateChanged(LocationProvider provider, int newState) {

    }
}

The _interval variable used in getGPSLocation() is set at -1.

JMax2012
  • 373
  • 2
  • 6
  • 24
  • 1
    I would start by using the Blackberry EventLogger class to log any possible exceptions where you are catching them in the code you have shown us, maybe in conjunction with Thread.activeCount() to check that you are not starting too many threads which never terminate. Then report back here if you find any exceptions being thrown that you can't resolve. – paulkayuk Apr 10 '12 at 13:37

1 Answers1

1

I think you need to re-factor your code. You are using a timer to create a BlackBerryLocationProvider at a rate specified by gps_interval that will get positions at a rate specified by _interval and invoke the locationUpdated() method of LocationListenerImpl (which you create new for each BlackBerryLocationProvider. There are at least two potential problems here:

  1. I suspect _interval is related to gps_interval, in which case after gps_interval amount of time you create a location provider that waits _interval amount of time before providing a location to the listener. By this time your timer has probably expired again causing another location provider to be created... consuming more resources than necessary.
  2. You haven't shown us your LocationListener implementation. If you don't close off the BlackBerryLocationProvider and free up the LocationListenerImpl then each time the timer expires you will create new objects consuming more resources, without freeing the old ones.

One of the reasons the call to setLocationListener() has an interval argument is to support what you want to do, ie) provide a location periodically. Using this facility allows the OS to be a bit more intelligent about how it handles the GPS which is a significant battery drain if used improperly.

So, when you need positions create the provider, register you listener for the required interval and then process the locations in the listener. Be sure to connect to the back end web service on a separate thread. If you stall the event thread waiting for the server to respond you will cause more trouble for the system.

Edit:

This may be an implementation of getGPSLocation() that may meet your needs. It will try one set of criteria, then the other. It still relies on the timer for the interval but doesn't use the interval/callback properties of the location provider. Since it doesn't register a listener, once the provider goes out of scope it will be subject to garbage collection.

public void getGPSLocation(){
   try{
      //GPS thread
      BlackBerryLocationProvider provider =
         (BlackBerryLocationProvider)LocationProvider.getInstance(
         new BlackBerryCriteria(GPSInfo.GPS_MODE_ASSIST));

       if (provider != null){
          try {
             Location location = provider.getLocation(timeout);
             // process location
             return;
          } catch (LocationException le) {
          } catch (Exception e) {
          }
       }

       provider =
          (BlackBerryLocationProvider)LocationProvider.getInstance(
          new BlackBerryCriteria(LocationInfo.GEOLOCATION_MODE_CELL));

       if (provider != null){
          try {
             Location location = provider.getLocation(timeout);
             // process location
          } catch (LocationException le) {
          } catch (Exception e) {
          }
       }
    }
    catch(Exception le){
    }        
 }

Having said that a better way might be to replace both updateGPSPosition and getGPSPositioin with (from the API docs):

public void updateGPSPosition() {
   BlackBerryCriteria bbCriteria;
   BlackBerryLocationProvider bbProvider;

   try { //setup Criteria
      bbCriteria = new BlackBerryCriteria()); // use default mode on the device
      bbCriteria.enableGeolocationWithGPS();  // retrieve geolocation fix if GPS fix unavailable
   } catch (UnsupportedOperationException uoe) {}

   try { //setup LocationProvider
      bbProvider = (BlackBerryLocationProvider)LocationProvider.getInstance(bbCriteria);
      bbprovider.setLocationListener(new LocationListenerImpl(), interval, timeout, maxage);
   } catch (LocationException le) {}

}

You may wish to save bbCriteria and bbProvider someplace where you can get them later to change the interval, or just stop getting locations.

It can be difficult to find out what to do inside of providerStateChanged() this is what I usually use:

public void providerStateChanged(LocationProvider provider, int newState) {
    if (newState == LocationProvider.TEMPORARILY_UNAVAILABLE)
    {
        if (unavailableCount < 3) {
            System.err.println("TEMPORARILY_UNAVAILABLE Reset " + Integer.toString(unavailableCount));

            // de-register listener, reset the provider and re-register
            locationProvider.setLocationListener(null, -1, -1, -1);
            locationProvider.reset();
            locationProvider.setLocationListener(this, 1, -1, -1);
            unavailableCount++;
        } else {
            System.err.println("TEMPORARILY_UNAVAILABLE Suspend");
            locationProvider.setLocationListener(null, -1, -1, -1);
        }
    }
    else if (newState == LocationProvider.AVAILABLE)
    {
        unavailableCount = 0;
    }
    else {
        System.err.println("OUT_OF_SERVICE");
        locationProvider.setLocationListener(null, -1, -1, -1);
    }
}
Richard
  • 8,920
  • 2
  • 18
  • 24
  • I updated the question with addition code for the LocationListener() implementation. Are you suggesting that creating the BlackberryLocationProvider each time is consuming too many resources and this is what is causing the app to crash after 18 hours? – JMax2012 Apr 10 '12 at 18:10
  • Short answer: Yes. Long answer: It actually looks like you are crating two location providers and two location listeners each time your timer fires. So that could be one problem. BlackBerry devices are well known to be memory poor, but another issues is the number of Object references available to the system. If either runs out, and no other applications is able to give some up then the system starves. If is possible that some other issues is cause the problem, but from the code you have provided that is what I conclude. – Richard Apr 10 '12 at 18:25
  • Thanks for confirming that. I'm very new to Blackberry development, so here's my follow-up question: if I explicitly dispose of those BB provider objects, will that (likely) fix the crash problem? And what's the best way to dispose of those objects? (Like I said, I'm new to BB development.) – JMax2012 Apr 10 '12 at 18:58
  • This is very interesting. I probably should have mentioned that I inherited this code, so I'm also still trying to "reverse engineer" what it's doing. Correct me if I'm wrong, but it looks as though the current code is only implementing LocationListenerImpl() for the purpose of getting the longitude and latitude from the Location object. But in your code implementation, it looks like I can access that same information without even needing the LocationListenerImpl() - is that correct? – JMax2012 Apr 11 '12 at 13:18
  • That's a steep inheritance tax ;) There are two ways to get Location objects from a provider: 1) the getLocation() method which will block until it can get a location or the timeout expires; 2) using the listener which will call locationUpdated() periodically if it can get a position or providerStateChange() under conditions listed in the API docs. Either way is valid, and it really depends on what your requirements are. For periodic updates I recommend using the listener. The listener implementation you have does not handle GPS errors, I'll see if I can post an example. – Richard Apr 11 '12 at 15:25
  • I've re-wired the code to use your getLocation() implementation, and the phone is reporting accurately. I've speeded up the reporting interval to every 5 minutes, so I should know in a few hours if the changes worked, or if it's still going to crash after x number of location updates. – JMax2012 Apr 11 '12 at 16:06
  • Ok, my fingers are crossed. I have similar apps (all built around the same basic core code) that do essentially what you're doing so one way or the other I'm sure we'll get it sorted out. – Richard Apr 11 '12 at 17:55
  • Well, the app has been running for more than 48 hours without a hiccup, so I'd say we've got this problem licked. Thank you SO MUCH for your help! – JMax2012 Apr 13 '12 at 20:26