NoSuchMethodError Bundle.getString() - android

I get some wierd logs, which obviously only occurs on Android-Devices with Versions below 3(checked with emulator).
When you change the orientation and onCreate() or onRestoreInstanceState() is called with an Bundle that is not null it crashed
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_menu);}
if (savedInstanceState != null) {
mSlug = savedInstanceState.getString(KEY_SLUG, null);
}
}#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_SLUG, mSlug);
}#Override
protected void onRestoreInstanceState(Bundle outState) {
super.onRestoreInstanceState(outState);
if (outState != null) {
mSlug = outState.getString(KEY_SLUG, mSlug);
}
}
The LogCat-Log looks like:
http://i.stack.imgur.com/WbivQ.png
Does anybody got clue what is happening here?

Bundle.getString with 2 arguments is only available in API level 12 and later. You have to specify the default value yourself, rather than passing it as an argument.
You can check this yourself in the future by clicking the "Filter by API level" dropdown and selecting the appropriate API level in the upper right hand of the documentation

Is simple to make
mSlug = savedInstanceState.getString(KEY_SLUG, null);
compatible with olders APIs, just replace it with
mSlug = getIntent().hasExtra(KEY_SLUG) ? savedInstanceState.getString(KEY_SLUG) : null;

Just use this function:
public static String getStringFromBundle(Bundle bundle, String key, String defaultValue){
if (Build.VERSION.SDK_INT < 12){
String returns = bundle.getString(key);
if(returns==null) returns = defaultValue;
return returns;
} else
return bundle.getString(key, defaultValue);
}

EDIT: Fixed, see comment below.
For anybody using MvvmCross, there is a bug as of 4.2.3 which is due to this. Just create your own FragmentCacheConfiguration and implement one of the workarounds in this thread. https://github.com/MvvmCross/MvvmCross/issues/1431

Related

Instance state serializable loses data

In one of Activities I have HashSet<Integer> mSelectedPositions. I want to save state of this set on screen rotation.
#Override
protected void onSaveInstanceState(#NotNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SELECTED_TYPES_POSITIONS, mSelectedPositions);
}
And restore it
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState!= null && savedInstanceState.containsKey(SELECTED_TYPES_POSITIONS)){
mSelectedPositions = (HashSet<Integer>) savedInstanceState.getSerializable(SELECTED_TYPES_POSITIONS);
}
...
}
The problem is, getSerializable(..) returns an empty HashSet, even then it wasn't empty in putSerializable(..).
What's even weirder, I have almost the same code (with other keys) in other Fragments, and it works fine.
Don't know if matters, but activity in question is a child of MainActivity.
Upd
Part of the problem is in selection flow. On destroy of activity action mode is finished.
#Override
protected void onDestroy() {
if(mActionMode != null){
mActionMode.finish();
}
super.onDestroy();
}
Which triggers
#Override
public void onDestroyActionMode(ActionMode mode) {
mAdapter.clearSelections();
mActivity.nullifyActionMode();
}
in SelectionCallback.
I think, next thing happens:
1. I put mSelectedPositions in outState Bundle, it stores reference
2. Activity is destroyed
3. SelectionCallback clears mSelectedPositions
4. Actual serialization happens with empty HashSet.
So I made some changes — new HashSet with copy of mSelectedPositions data
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SELECTED_TYPES_POSITIONS, new HashSet<>(mSelectedPositions));
}
And it works like it should.
Upd2
In Fragments I call mActionMode.finish() in onDetach(), which is not called on screen rotation, so mSelectedPositions there remains intact.
Try putting a Json instead of raw HashMap
#Override
protected void onSaveInstanceState(#NotNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(SELECTED_TYPES_POSITIONS,new Gson().toJson(mSelectedPositions));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState!= null && savedInstanceState.containsKey(SELECTED_TYPES_POSITIONS)){
String data = savedInstanceState.getString(SELECTED_TYPES_POSITIONS);
if(data != null){
mSelectedPositions = new Gson().fromJson(str, new TypeToken<HashSet<Integer>>() { }.getType())
}
}
...
}
The funny thing is that serialization of outState happens after onDestroy() call. And I was erasing data passed to outState in onDestroy.
So, there are two ways to fix that:
• pass copy of data to outState
• don't erase data in onDestroy
Opted for second option. Erasing might be required in some cases in onDetach() of Fragments, but it is not necessary with Activity.

How to receive an activity state instance(bundle) on request?

How to get the bundle that is given to this method on request (Bundle outState)?
#Override
protected void onSaveInstanceState(***Bundle outState***) {
super.onSaveInstanceState(outState);
// Only if you need to restore open/close state when
// the orientation is changed
if (adapter != null) {
adapter.saveStates(outState);
}
}
in Bundle instance you can pass the data and get it back in onCreate() method. like following
outState.putString("key1", "data1");
outState.putBoolean("key2", "data2");
outState.putInt("key3", "data3");
and in onCreate get it like following
if (savedInstanceState != null){
data_1 = savedInstanceState.getString("keys1");
data_2 = savedInstanceState.getBoolean("keys2");
data_3 = savedInstanceState.getInt("keys3");
}
You can get saved bundle back using below method:
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
// get your saved bundles back here
}
Just refer this developer page, you will get clear idea about this
It's the Bundle sent in the OnCreate method in your activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
savedInstanceState.getString("bla");
}
}
Note that you'll have to check for null, because the first time you create your activity it will be null, since there was no previous state.
see https://developer.android.com/guide/components/activities/activity-lifecycle.html#oncreate for more information

onSaveInstanceState not saving images on rotation

I want to save two images, so that they are still displaying in the imageviews after screen rotation. Somehow it does not work and I don't know why. onRestoreInstanceState dind't work either. Can someone help me?
Here is my code:
private byte[] mapImage;
private byte[] photo;
private Bitmap imageBitmap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_entry);
if(savedInstanceState !=null) {
photo = savedInstanceState.getByteArray(STATE_PHOTO);
viewPhoto.setImageBitmap(DbBitmapUtility.getImage(photo));
mapImage = savedInstanceState.getByteArray(STATE_MAP);
viewGPS.setImageBitmap(DbBitmapUtility.getImage(mapImage));
}
#Override
protected void onSaveInstanceState(Bundle outState) {
if(imageBitmap!=null) {
photo = DbBitmapUtility.getBytes(imageBitmap);
outState.putByteArray(STATE_PHOTO, photo);
}
if(mapImage !=null) {
outState.putByteArray(STATE_MAP, mapImage);
}
super.onSaveInstanceState(outState);
}
private void showMapView() {
Bundle getImageOfMap = getIntent().getExtras();
if (getImageOfMap != null) {
mapImage = (byte[]) getImageOfMap.get("map_image");
Bitmap showMapImage = DbBitmapUtility.getImage(mapImage);
viewGPS.setVisibility(View.VISIBLE);
viewGPS.setImageBitmap(showMapImage);
}
}
EDIT:
I put super.onSaveInstanceState(outState); on top of the onSaveInstanceState(Bundle outState) method and now mapImage is being shown after rotation but imageBitmap still isn't. I found out that the imageBitmap is saved but in onCreate it is null.
Does someone know a solution for this?
EDIT: mapImage is now somehow saved without savedInstanceState, because when I delete the mapImage part it is still there after rotation. It didn't use to be like this when I posted this question first. I give up...
I think I'll just use the not so pretty solution of the configChanges in the manifest. Thanks to everyone who tried to help!
Thanks!
IMO it is a threarding issue. Maybe your image size is more. Try saving the bytearray in some other thread. Better use an AsyncTask to save the byre array.
You have a typo. I think you should be checking for imageBitmap != null instead of photo != null:
protected void onSaveInstanceState(Bundle outState) {
if(imageBitmap!=null) {
photo = DbBitmapUtility.getBytes(imageBitmap);
outState.putByteArray(STATE_PHOTO, photo);
}
if(mapImage !=null) {
outState.putByteArray(STATE_MAP, mapImage);
}
super.onSaveInstanceState(outState);
}
EDIT:
Also, make sure that onCreate(), you initialize your viewPhoto (and other variables)
viewPhoto = (ImageView) findViewById(R.id.viewPhoto);
before access it inside the if block.
Unless your app is running on API21+ version of Android, your
public void onSaveInstanceState (Bundle outState, PersistableBundle outPersistentState);
will NOT be called as it simply does not exist on earlier versions of the platform than 21. To support pre API21 devices you must, instead of the above, override the following method:
public void onSaveInstanceState (Bundle outState);
This will work on API21+ as well, so you do not need to override both methods, of course (unless you know you need to deal with PersistableBundle the new one offers).
Can you once confirm about onSaveInstanceState calling means it's called or not?

How to save and recreate android activities

I'm trying to teach myself some android programming and I'm unable to get my app to save and load a users progress.
Currently I initialise my variables level and score in my activity. Then call this from my onCreate method:
if (savedInstanceState != null) {
// Restore value of members from saved state
playersScore = savedInstanceState.getInt(STATE_SCORE);
level = savedInstanceState.getInt(STATE_LEVEL);
} else {
playersScore = 0;
level = 0;
}
and finally I have a method for saving a bundle:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt(STATE_SCORE, playersScore);
savedInstanceState.putInt(STATE_LEVEL, level);
super.onSaveInstanceState(savedInstanceState);
}
For some reason my bundle wont save, any ideas on what I'm missing?
From the docs:
Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.
Have you looked at:
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
playersScore = savedInstanceState.getInt(STATE_SCORE);
level = savedInstanceState.getInt(STATE_LEVEL);
}
Also shouldn't:
savedInstanceState.putInt(STATE_SCORE, playersScore);
savedInstanceState.putInt(STATE_LEVEL, level);
be:
savedInstanceState.putInt("STATE_SCORE", playersScore);
savedInstanceState.putInt("STATE_LEVEL", level);
As putInt() requires a string and an int.

Saving string on orientation change FC's app if changed too quickly

I'm having trouble saving a string on orientation changed. I've tried using onSaveInstanceState()/onRestoreInstanceState() and onRetainNonConfigurationInstance()/getLastNonConfigurationInstance() with no luck.
I have:
#Override
public String onRetainNonConfigurationInstance(){
final String savedType = currentType;
return savedType;
}
and in onCreate() I have:
currentType = (String) getLastNonConfigurationInstance();
This hasn't worked for me yet and currentType is always null after an orientation change. Any suggestions?
Revision
So this is what I currently have and it's still not working:
#Override
protected void onSaveInstanceState(Bundle outState){
outState.putString("savedType", currentType);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
currentType = savedInstanceState.getString("savedType");
super.onRestoreInstanceState(savedInstanceState);
}
I've tried putting the super method calls at the beginning and end of each method in every combination I could think of but am still getting a NullPointerException for currentType when the view is created again.
Also
This is happening in a Dialog, I'm not sure if that has anything to do with it but thought that might be a useful bit of info.
Partial Solution/Partial New Question
So I got this working somewhat like I wanted. There was a statement buried in a method that set currentType to null towards the end of the life cycle when the variable wouldn't be needed again. That's now out of the way. The screen will successfully change orientations but if it's changed between the two successively too quickly it will FC. This is what I used to get it working in this state:
Class Field:
private String currentType;
At the end of onCreate I have:
currentType = (String) getLastNonConfigurationInstance();
And then:
#Override
public Object onRetainNonConfigurationInstance() {
final String s = currentType;
return s;
}
It works fine if the orientation doesn't change and then change back again quickly. I'd like to fix this though because department and retail stores are using/will use this and I can see people dropping it, which will cause an FC (dropped it on my couch to test). I'm only storing one string so what I'm storing doesn't take up much memory. Any suggestions for this new situation?
Have you tried putting it in a Bundle?
private static final String CURRENT_TYPE = "currentType";
private static final String DEFAULT_TYPE = "defaultType";
#Override
protected void onSaveInstanceState (Bundle outState) {
outState.putString(CURRENT_TYPE, currentType);
super.onSaveInstanceState(outState);
}
#Override
protected void onCreate (Bundle savedInstanceState) {
...
currentType = (savedInstanceState == null)? null :
savedInstanceState.getString(CURRENT_TYPE, DEFAULT_TYPE);
...
}
So I got this working somewhat like I wanted. There was a statement buried in a method that set currentType to null towards the end of the life cycle when the variable wouldn't be needed again. That's now out of the way. The screen will successfully change orientations but if it's changed between the two successively too quickly it will FC. This is what I used to get it working in this state:
Class Field:
private String currentType;
At the end of onCreate I have:
currentType = (String) getLastNonConfigurationInstance();
And then:
#Override
public Object onRetainNonConfigurationInstance() {
final String s = currentType;
return s;
}
It works fine if the orientation doesn't change and then change back again quickly. Asking new question about this new bug,

Categories

Resources