Opening a sub-activity several times causes an InstanceCountViolation despite being destroyed
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).getTableNam开发者_Python百科e());
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.
精彩评论