I'm working on a project that has a lower level activity called RecordView to display record details such as an image, the date and time it was taken, and the latitude/longitude information. Rather than trying to manipulate the camera to geotag and access exif data I'm trying to implement a location listener to get the location where the image is first taken (on the button press). This approach works - I get my location to display and update the record in the database correctly (returning to the view later displays the location from the beginning). However, if I back out of the current RecordView and then enter two more (any combination) the program will crash with the error InstanceCountViolation (full error reprinted below). When I override the lifetime methods of RecordView to display when each is called we find that it is destroyed before being called again. That is to say, it doesn't appear that more than a single RecordView exists at any given time.
So my question boils down to this: where is that error coming from and how can I fix it?
Is something lying about being destroyed? Is the LocationListener sitting around somewhere and causing problems? Is it something unrelated that might be providing a bogus error?
Also, should I do the horrible hard coded fix and just up the limit of RecordView instances allowed? Or continue hunting for another approach (as an example I'm attempting to request a single update using a PendingIntent.getBroadcast(...) call)?
For reference this error has appeared on the emulator for 3.1 and on an actual tablet (a Xoom, 3.1). Commenting out the listener update code seems to avoid the crash (EDIT 2: I appear to have been wrong about that). The code as it relates to the listener is below (it can be found inside a public method updateLocation inside the RecordView class).
// Listener for the update request
LocationListener locListener = new LocationListener() {
// Store the currentRecord so the listener can update it after return
Record currentRecord = record;
GeoDatabase database = data;
#Override
public void onLocationChanged(Location location) {
if (location != null) {
myLocation = location;
Log.d(TAG, "Location pulled as " + myLocation);
String lat = Location.convert(myLocation.getLatitude(),
Location.FORMAT_SECONDS);
String lon = Location.convert(myLocation.getLongitude(),
Location.FORMAT_SECONDS);
// Update the record values
currentRecord.setRecordLatitude(lat);
currentRecord.setRecordLongitude(lon);
database.updateRecord(currentRecord);
Log.d(TAG, "Record values now listed as "+ record.getValues());
// Update the text boxes
latitude.setText(lat);
longitude.setText(lon);
Toast.makeText(getBaseContext(),"GPS location updated",
Toast.LENGTH_LONG).show();
} else {
Log.w(TAG, "Passed location is null!");
Toast.makeText(getBaseContext(),
"GPS error - unusable location",
Toast.LENGTH_LONG).show();
}
}
#Override
public void onProviderDisabled(String provider) {
Toast.makeText(getBaseContext(),
"GPS disabled", Toast.LENGTH_SHORT).show();
}
#Override
public void onProviderEnabled(String provider) {
Toast.makeText(getBaseContext(),
"GPS enabled", Toast.LENGTH_SHORT).show();
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
lm.requestSingleUpdate(LocationManager.GPS_PROVIDER, locListener, null);
The full error:
android.os.StrictMode$InstanceCountViolation:class [program path].RecordView; instances=3; limit=2
at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
EDIT:
Two things I've determined.
The first is that when Logcat shows the destruction of the RecordView activity the LocationListener is correctly disconnected (destroyed? It's null anyway). However the listener seems to update from beyond the grave as it were - that is, I sometimes see my Toast message about a GPS update on the higher level activities screen and the GPS information appears to have been updated.
The second is that it isn't exactly crashing per say - it appears to be Force Closing the RecordView rather than the entire app. The main chunk of the app seems to just basically be minimized.
EDIT 2:
We recently added a preference screen using a new activity and this has the same InstanceCountViolation error as the RecordView. We've verified that nothing has to be changed in the activity for the error to occur: it only needs to be opened a few times. An example of how we open our sub-activities from the main activities is below:
Intent intent = new Intent(this.getActivity()
.getApplicationContext(), RecordView.class);
Bundle extras = new Bundle();
extras.putString("tableName", "table1");
extras.putInt("id", mId);
extras.putBoolean("newRecord", false);
extras.putLong("folder_id", mFolderId);
extras.putString("type", recordList.get(mId).getTableName());
intent.putExtras(extras);
startActivity(intent);
So now I'm wondering if there's a problem in how the Intent is handling activity creation and deletion.
It seems like it shouldn't be needed, but have you tried calling
lm.removeUpdates(locListener);
to un-register the listener?
I know that this is old post. Just for guys who is looking for solution and explanation to this problem.
In case there is InstanceCountViolation exception it means that there can be real with Activity leak or problem which is related to how detectActivityLeaks check is implemented in Android SDK.
To identify if this is a problem I can recommend the following post: Detecting leaked Activities in Android. If you will see that there are objects holding a reference to this activity which don't related to Android Framework then you have a problem which should be fixed by you.
In case there are no objects holding a reference to this activity which don't related to Android Framework than it means that you encountered with the problem related to how detectActivityLeaks check is implemented. In this case to fix the problem with failed activity without turning off detectActivityLeaks you can simply run System.gc() before starting activity in debug configuration like in the following example:
if (BuildConfig.DEBUG)
{
System.gc();
}
Intent intent = new Intent(context, SomeActivity.class);
this.startActivity(intent);
More information are available in this answer.
Related
I am developing an android application in which I need to get my current Location. I have successfully wrote the code and I am getting my current location using Google Play Service.
The problem is sometimes it gives me the location after a long time. I have noticed that it was only for first use of the app.
Any way to avoid this problem and get the current location fast? Is it related to the version of google play service in my code? (I am not using the last one in fact I am using version 9.8.0.)
As #tahsinRupam said, avoid using getLastLocation as it has a high tendency to return null. It also does not request a new location, so even if you get a location, it could be very old, and not reflect the current location. You might want to check the sample code in this thread: get the current location fast and once in android.
public void foo(Context context) {
// when you need location
// if inside activity context = this;
SingleShotLocationProvider.requestSingleUpdate(context,
new SingleShotLocationProvider.LocationCallback() {
#Override public void onNewLocationAvailable(GPSCoordinates location) {
Log.d("Location", "my location is " + location.toString());
}
});
}
You might want to verify the lat/long are actual values and not 0 or something. If I remember correctly this shouldn't throw an NPE but you might want to verify that.
Here's another SO post which might help:
What is the simplest and most robust way to get the user's current location on Android?
I'm writing a simple app to send public photos from Dropbox public folder to Chromecast.
Instead of CastCompanion library I decided to write my own stuff to understand better the API.
According to Google Guidelines:
if the sender application becomes disconnected from the media route, such as when the user or the operating system kills the application without the user first disconnecting from the Cast device, then the application must restore the session with the receiver when the sender application starts again.
It seems to me that the same solution should apply to Activity recreation upon orientation change since it recreates the Activity from scratch.
My first question: Is my assumption correct? Both scenarios, orientation change and system kill, may use the same solution?
Given this assumption I wrote some code to restore session upon Activity restoration.
I'm considering the orientation change scenario, when Activity is recreated from scratch and I am supposed to restore route Id, Session Id and try to reconnect (I'm storing and retrieving both values from shared preferences).
I've been testing with and it's working fine.
That's what I do (based on Google Sender Guidelines code):
After discovering the ongoing Route Id and find the cast device I call this method:
private void connectToDevice(CastDevice castDevice) {
Log.d(TAG, "connecting to " + castDevice);
Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
.builder(castDevice, new CastListener());
Log.d(TAG, "apiClient is null ? " + (apiClient == null));
apiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptionsBuilder.build())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
Log.d(TAG, "apiClient connected? " + apiClient.isConnected());
Log.d(TAG, "apiClient connecting? " + apiClient.isConnecting());
apiClient.connect();
}
private class CastListener extends Cast.Listener {
#Override
public void onApplicationStatusChanged() {
if (apiClient != null) {
Log.d(TAG, "callback => " + this);
}
}
#Override
public void onVolumeChanged() {
}
#Override
public void onApplicationDisconnected(int errorCode) {
teardown();
}
}
After this method I call Cast.CastApi.joinApplication if I recognize a reconnection.
But once reconnected to Chromecast the log of onApplicationStatusChanged prints one different instance for every phone's rotation. E.g: if I rotate phone 3 times the log prints 3 times with 3 different pointer addresses. That makes me believe it is internally holding all callbacks instances.
How am I supposed to handle this situation since the Activity is being recreated and I need to create another instance of GoogleApiClient keeping the session?
Full source:
https://github.com/ivan-aguirre/chromecast_samples/blob/master/DropboxCast/app/src/main/java/com/dropboxcast/dropboxcast/MainActivity.java
IMHO, I believe the proper way (or at least a better way) to approach this is one of the following:
if you have only one activity and that is all you care about, then use a fragment that persists across configuration changes and put the stuff that you want to persist seamlessly, there. This way, rotation of the phone is not going to cause any disruption in your cast related stuff.
if you have more than a single activity, think about creating an object that lasts across all your activities and put the cast stuff there and then ask that object for the instance of CastApi whenever needed, etc.
In your case, do you really get disconnected when you rotate the phone? Since you are setting up a whole new connection, you might want to disconnect yourself first when configuration changes (assuming you don't want to go with my earlier proposed (1) or (2)).
I have a fragment:
public class myMap extends Fragment implements LocationListener
{
// do all the usual stuff
#Override
public void onResume()
{
super.onResume();
if(mIsGPSEnabled)
{
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 10, this);
}
// and of course a bit more of code I am skipping
}
#Override
public void onLocationChanged(Location location) {
Toast.makeText(getActivity(), "location changed " + location.getLatitude() + " " + location.getLongitude(), Toast.LENGTH_LONG).show();
}
I noticed the following behavior:
onResume gets always the last known location that works fine.
Upon start of the app on my notification bar I see this little icon that GPS is on
after a while I get the onLocationChanged called the first time (so far all is fine)
now the little GPS icon vanishes from the notification bar and onLocationChanged is never called again. The app is at all times in the forground
I never unregister (for now) my location change listener
I bring my app in the backround and start for example osmand (a navigation software) while this software is running I see my toast in onLocationChanged on every update, working as it should.
Anyone an idea?
Check if
mLocationManager != null
if is null you can requesteLocationUpdates otherwise avoids to call requests
Use Log and watch it using free logcat app or this to be sure that your location not updated:
Toast.makeText(getApplicationContext(), "location changed " + location.getLatitude() + " " + location.getLongitude(), Toast.LENGTH_LONG).show();
because i don't think that your default home screen is an activity! that's why toasts appear in osmand and your app and don't in home screen.
so i think you are in a famous android location api issue that i spent more than 30 days before knowing that there lot of peoples have the same problem!, than you can feel better if you take a look here https://code.google.com/p/android/issues/detail?id=57707
the problem have no limit no algorithm no special cases : >> get first location, stop for a while, work perfectly for two days and switch work n stop for a week... !!
I think you must buy new test phone!
To be clear: This is an activity in my Android application that is meant to pull the coordinates for the users location using the GPS_PROVIDER. The Activity contains a button which, when pressed, should initiate a method that obtains the coordinate data. The problem is that the application crashes when there is no previously known location information (ie if the phone was recently reset). If I open up the Maps application (for example) and pinpoint my location, then re-open my own application and run this method, it works as intended. My question is why is this crashing and/or how can I prevent this crash from occurring? Help is appreciated, thanks.
This method is run when the button is pressed - and an intent response is generated back to the calling activity when the coordinates are properly found:
protected void getCurrentLocation() {
Location location = null;
try {
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
} catch (IllegalArgumentException iae) { }
if (location != null) {
longV = location.getLongitude();
latV = location.getLatitude();
response(longV, latV);
} else {
getCurrentLocation();
}
I'm guessing that you are receiving a StackOverflowException, because if location is null you call the exact same function creating an indefinite loop...
If there is no last know location, you need to request a new location. (getLastKnownLocation() will not change on its own no matter how many times you call it.)
I'm having trouble getting a LocationListener to call the onLocationChanged() callback on my phone. When I run my code in the emulator, it works fine, the callback is called each time I do a geo fix.
When I run the application on my phone, nothing at all happens. The callback is never called. I have location enabled by both GPS and by Wireless in my settings. The application has all of the uses-permissions for location permissions.
Also, when I call getLastKnownLocation() on a LocationManager object, my application crashes. (Still, only on my stupid phone). Even if I try to catch an exception that's causing it to crash, it still just crashes, so I can't even get any information on what is causing it to crash. This is extremely frustrating.
LocationManager.getBestProvider() is returning GPS, and when I open google maps it finds my location in no time at all. What the heck is going on here? Is there some way I can figure out why it's crashing on my phone?
private void setupLocListener(){
Criteria c = new Criteria();
c.setAccuracy(Criteria.ACCURACY_FINE);
c.setAltitudeRequired(false);
c.setBearingRequired(false);
c.setSpeedRequired(false);
c.setCostAllowed(false);
lm.requestLocationUpdates(lm.getBestProvider(c,true), 0, 0, new LocationListener() {
#Override
public void onLocationChanged(Location arg0) {
map.setLocation(arg0);
}
public void onProviderDisabled(String arg0) {
}
public void onProviderEnabled(String arg0) {
}
public void onStatusChanged(String arg0, int arg1, Bundle arg2) { }
});
}
onLocationChanged() wont fire until you actually start receiving GPS coordinates.
By that I mean the chip has to warm up for about a minute or so from my experience before you start receiving data from it.
I usually start some other application and wait for it to prove that the GPS chip has warmed up before I go testing any of my GPS apps.
I know that you mentioned that it works properly in Google Maps but have you tried clearing your memory and restarting your application straight away afterwards?
Also getLastKnownLocation() is always null until you start receiving coords.
The Location framework pushes coordinates to your callback, when they become available. Depending on weather, etc. you may not get a "fix" initially. You should see the "GPS" indicator on the status bar when your listener is successfully registered.
getLastKnownPosition() works just fine (it may return null); and Google Maps uses that, while it is waiting for an initial fix from the location provider.
You may also want to see what other providers are available, e.g. cell-tower data, and attempt to obtain data from those (i.e. LKP), either instead of, or until, your "preferred" provider starts pushing data.
Also, don't assume any particular service exists, e.g. LocationManager (Context.getSystemService() can return null), or any suitable provider exists, (getBestProvider() can return null). Your code will fail as-is on the right device with the right settings. If the documentation says null you must check for it, or users will be uninstalling it because it FC's all over the place.