I am using Intents to switch between activities in my Android app. I am putting data in the Intent for use in the next activity. When I switch the phone between landscape and portrait modes, the values passed from the intent are lost and I get a NullPointerException.
Can someone please tell me what could be wrong.
There's a lot of code to post it entirely. But if someone needs to look at specific parts of code, I can post it here.
Edit
I solved the issue of state not being saved. But another problem I faced is that none of the buttons on the screen work after the orientation has been changed. On button press, I get this warning in LogCat
02-25 23:07:49.190: WARN/WindowManager(58): No window to dispatch pointer action 0
Please help.
When you switch orientation the activity is recreated and onCreate is recalled so you have to use the bundle to save your current state and restore after an orientation change. You can see this in action if you have just an app with a TextView and you enter text and change orientation. If you bundle your state for onCreate you can curb this. This is probably also why you have a NullPointer after the orientation changes. It is annoying as all hell but something we have to live with.
This link on a series of orientation tutorials and this first one in particular should help you understand exactly what is going on and how to successfully maintain your current state.
Update: There is also a post on SO Activity restart on rotation Android that deals with almost the same thing.
Edit for follow up question:
Did you re-attach your click handlers after the orientation change?
Write this in your manifest file..in which activity you want this--
android:configChanges="orientation|keyboardHidden"
Edited: Use this one for new APIs versions--
android:configChanges="orientation|keyboardHidden|screenSize"
Definitely it will work..
Try this:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(SOME_KEY, "blah blah blah");
}
#Override
public void onCreate(Bundle savedInstanceState) {
...
somevalue = savedInstanceState.getString(SOME_KEY);
...
}
It possible to declare an attribute android:configChanges with the value of "orientation", this will prevent the activity from being restarted. Instead, the activity remains running and its onConfigurationChanged() method is called.
Declare < android:configChanges="orientation|keyboardHidden"/> in your manifest. This allows you manage the change of Orientation/Keyboard visibility by yourself. Of course, You don't need to override the callback method for manage it.
Hi I also encountered this problem.
what fixed it for me was:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putString("Username", mUsername);
savedInstanceState.putString("Password", mPassword);
savedInstanceState.putString("UserID", mUserID);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
and then in onCreate():
if (savedInstanceState == null) {
Bundle extras = getIntent().getExtras();
if(extras == null) {
mUsername = "?";
mPassword = "?";
mUserID = "?";
} else {
mUsername = extras.getString("Username");
mPassword = extras.getString("Password");
mUserID = extras.getString("UserID");
}
} else {
mUsername = (String) savedInstanceState.getSerializable("Username");
mPassword = (String) savedInstanceState.getSerializable("Password");
mUserID = (String) savedInstanceState.getSerializable("UserID");
}
then you can be sure the objects are not null.
Related
I've set
android:configChanges="orientation"
android:screenOrientation="portrait"
On manifest and it works well... till keyboard appears. When keyboard is shown you can turn the orientation to landscape and my webview refresh the entire application making the user to log in again. I don't want (or need) a savestate, only block entire features that can refresh the app when changing orientation. I don't need or want the app to being able to turn on landscape. I'ts completely usable on portrait only and was designed to it.
It's supposed that android:configChanges="orientation" tells android that the app will take care about orientation, and android:screenOrientation="portrait" blocks the phone to enter landscape mode when running the app, but keyboard seems to override this config.
At this point i can't figure out how to achieve this workaround,
thanks
SOLVED:
I tried
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
and now it works. I found this line on the documentation but at first sight i thought that i may have to handle keyboard show/hide after setting keyboardHidden on the configChanges but it's working fine with this options, the keyboard appears when user interacts with an input field but it's not able to show in landscape or turn the app to landscape.
I've also set screenSize and screenLayout as documentation linked avobe recommends.
I'll mark this question as solved when i can. Thanks
save the state.
private static final String STATE_COUNTER = "counter";
private int mCounter;
private ArrayList<Item> mItems;
#Override
protected void onSaveInstanceState(Bundle outState) {
// Make sure to call the super method so that the states of our views are saved
super.onSaveInstanceState(outState);
// Save our own state now
outState.putInt(STATE_COUNTER, mCounter);
// Save our own state now
outState.putSerializable(STATE_ITEMS, mItems);
}
then restore the state again
private static final String STATE_COUNTER = "counter";
private TextView mCounterTextView;
private int mCounter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// If we have a saved state then we can restore it now
if (savedInstanceState != null) {
mCounter = savedInstanceState.getInt(STATE_COUNTER, 0);
}
// Display the value of the counter
mCounterTextView = (TextView) findViewById(R.id.counter_text);
mCounterTextView.setText(Integer.toString(mCounter));
...
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_COUNTER, mCounter);
}
hope you find the answer.
I have an app with a Button to show the answer of a question asked. And a TextView with a warning text and another empty one that displays the answer when the button is clicked. When the user clicks the button, I want the warning textView to disappear and the button to be "unclickable". I managed to achieve this and everything worked as intended, but the problem occurs when I rotate the screen, nothing stays the same.
The code below is in onCreate().
Button buShowAnswer = findViewById(R.id.buShowAnswer);
TextView tvShownAnswer = findViewById(R.id.tvShownAnswer);
TextView tvWarning = findViewById(R.id.tvWarning);
buShowAnswer.setOnClickListener((View v) -> {
String answer;
if (isAnswerTrue){
answer = getString(R.string.true_answer);
}else {
answer = getString(R.string.false_answer);
}
tvWarning.setVisibility(View.INVISIBLE);
buShowAnswer.setEnabled(false);
tvShownAnswer.setText(answer);
cheatState = true;
});
It is very simple. You can go with navylover or you do the following
Open your manifest file
add android:configChanges="keyboardHidden|orientation|screenSize" to your activity like
<activity android:name=".MainActivity"
android:configChanges = "keyboard|orientation|screenSize"
//and leave the rest of the code of your activity as it is. Just add this line and you are good to go
What Happen when you rotate your screen? actually your activity configuration changes and you may say your activity is created as new again. When you rotate your screen it actually destroy your activity and recreate it and everything reset back.
But this is not a best option to use this line of code. you can use viewModal. it saves your avtivity state and when your activity destroyed and recreated then the same viewModal Linked with that activity which helps in saving configuration and saving states
Hope this is Helping
You should use onSaveInstanceState to save the state of your local variable when the screen is rotated, then use the saved value in onCreate (which is called upon rotation to create the new rotated activity).
So add this function
private boolean cheatState = false;
private static final String CHEAT_STATE = "CHEAT_STATE";
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(CHEAT_STATE, cheatState);
// call superclass to save any view hierarchy
super.onSaveInstanceState(outState);
}
and in your onCreate you can then call
if (savedInstanceState != null) {
cheatState = savedInstanceState.getBoolean(CHEAT_STATE);
if( cheatState ) {
buShowAnswer.setEnabled(false);
tvWarning.setVisibility(View.INVISIBLE);
}
}
Oncreate() callback will be triggered when screen rotates, add below line in Androidmanifest.xml to prevent recreate activity.
android:configChanges="keyboardHidden|orientation|screenSize
The detailed explantion is here
My answer is similar to Tyler's above, but it saves and restores both visibility and enabled states.
private boolean isAnswerEnabled;
private int warningTextVisibility;
private static final String STATE_ANSWER = "state_cheat";
private static final String STATE_WARNING_VISIBILITY = "state_warning_visibility"
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(STATE_ANSWER, isAnswerEnabled);
oustState.putInt(STATE_WARNING_VISIBILITY, warningTextVisibility);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
isAnswerEnabled = savedInstanceState.getBoolean(STATE_ANSWER);
buShowAnswer.setEnabled(isAnswerEnabled);
warningTextVisibility = savedInstanceState.getInt(STATE_WARNING_VISIBILITY);
tvWarning.setVisibility(warningTextVisibility);
}
}
How do I simply just restart my ENTIRE app instead of trying to worry about saving the instance perfectly in onSaveInstanceState and reinitializing everything perfectly when resumed/restored in onRestoreInstanceState? (this can quickly become error prone)
UPDATE 10.1.16
I chose to do this in onCreate since onRestoreInstanceState behaves oddly sometimes.
This method is based on the fact that the onCreate(Bundle) is null unless the activity is being revived in which case it is whatever onSaveInstanceState(Bundle) set it to.
I set TWO flags. One in onSaveInstanceState in the Bundle so to know that it is a valid Bundle set by me. The other in the class itself to determine if onCreate was called because of recreation or rotation. And so in onCreate I checked to see if onSaveInstanceState is not null, check the Bundle flag, and check bInit (which defaults to false). If both flags are true then it means android dumped and destroyed our apps memory and the safest way to ensure everything is initialized again in a linear-style application is to just restart it and launch the beginning activity.
public class SomeMiddleActivity extends AppCompatActivity
{
private static boolean bInit = false; // only way it will be false again is if android cleared our memory and we are recreating
#Override
public void onSaveInstanceState(Bundle state)
{
// set a flag so that onCreate knows this is valid
state.putBoolean("StateSaved", true);
super.onSaveInstanceState(state);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
// this must be called first always for some reason
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
if (savedInstanceState.getBoolean("StateSaved", false) && !bInit)
{
// we were recreated... start app over
Intent intent = new Intent(getApplicationContext(), Startup.class);
startActivity(intent);
finish();
return;
}
}
bInit = true; // this will stay true until android has cleared our memory
.......
}
Hope this helps someone and although this has worked thus far, if anyone has a different suggestion let me know.
And FYI: the onSaveInstanceState(Bundle, PersistableBundle) version of onSaveInstanceState is never called ever so I dont know why they even implement it. (?)
REFERENCES:
ACCORDING TO ANDROID DOCUMENTATION
onCreate
Bundle: If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null.
Try implementing this way
private final String IS_RE_CREATED = "is_re_created";
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(IS_RE_CREATED, true);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.containsKey(IS_RE_CREATED)) {
boolean isRecreated = savedInstanceState.getBoolean(IS_RE_CREATED, false);
if (isRecreated) restartApplication(this);
}
}
public void restartApplication(Context context) {
String packageName = context.getPackageName();
PackageManager packageManager = context.getPackageManager();
// Intent to start launcher activity and closing all previous ones
Intent restartIntent = packageManager.getLaunchIntentForPackage(packageName);
restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(restartIntent);
// Kill Current Process
Process.killProcess(Process.myPid());
System.exit(0);
}
Note: It is not a recommended to forcefully restart application.
How do I simply just restart my app instead of trying to worry about saving the instance
You mean the current activity? Do nothing (Don't implement onSaveInstanceState and onRestoreInstanceState).
The activity gets created automatically when changes happen. If there is no saved instance state, the activity won't restore any data.
Edit:
I think I came across similar issue too few weeks earlier, where I've to kill all the activities in the back stack and open a fresh new activity.
// Start Main Activity
Intent intent = new Intent(this, MainActivity.class);
finishAffinity();
startActivity(intent);
Use finishAffinity(). This works on > API 16.
When you kill all the activities in the back stack and open the main activity, it is kind of similar to restarting your app.
If im staying in the app, this works fine
Click button to take me to new activity:
intent.putExtra("invite_id", invite_id);
startActivity(intent);
Receiving Activity:
Bundle b = getIntent().getExtras(); //invite id is in here
Now here is the weird part. If I am in the app, then click home button to leave the app and go to the native contacts app and save ANYTHING (like edit a name or number...the problem only occurs if I actually save something), then go to recent apps and open up my app from there... now if I click the button to launch my intent to take me to a new activity, the receiving activity returns a null bundle
Bundle b = getIntent().getExtras(); //returns null
Why could this be happening?
String b = getIntent().getStringExtra("invite_id");
Intent extras are always persisted across activity death and recreation. So, if you're stashing that extra value, it will continue to be there if you are resuming the app with the recent app switcher.
I would verify that you are stashing values in the intent.
simply that means that your activity being recreated so you have to options to avoid that:
Avoid recreating your activity by setting this to your Activity within the manifest
android:configChanges="keyboardHidden|orientation|screenSize|locale"
Or by saving the instance of the id you received by doing such:
#Override protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("myId",myId);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null) {
myId = getIntent().getExtras().getString("myId");
}
}
I'm confused when it comes down to saving a state. So I know that onSaveInstanceState(Bundle) is called when the activity is about to be destroyed. But how do you store your information in it and bring it back to its original state in onCreate(Bundle savedInstanceState)? I don't understand how this bundle will restore information. It would be helpful if someone can provide an example.
The Dev guide doesn't do a good job of explaining this.
public class Conversation extends Activity {
private ProgressDialog progDialog;
int typeBar;
TextView text1;
EditText edit;
Button respond;
private String name;
private String textAtView;
private String savedName;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.dorothydialog);
text1 = (TextView)findViewById(R.id.dialog);
edit = (EditText)findViewById(R.id.repsond);
respond = (Button)findViewById(R.id.button01);
if(savedInstanceState != null){
savedInstanceState.get(savedName);
text1.setText(savedName);
}
else{
text1.setText("Hello! What is your name?");
respond.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
name = edit.getText().toString();
text1.setText("Nice to meet you "+ name);
}
});
}
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString(savedName, name);
}
}
The Bundle is a container for all the information you want to save. You use the put* functions to insert data into it. Here's a short list (there are more) of put functions you can use to store data in the Bundle.
putString
putBoolean
putByte
putChar
putFloat
putLong
putShort
putParcelable (used for objects but they must implement Parcelable)
In your onCreate function, this Bundle is handed back to the program. The best way to check if the application is being reloaded, or started for the first time is:
if (savedInstanceState != null) {
// Then the application is being reloaded
}
To get the data back out, use the get* functions just like the put* functions. The data is stored as a name-value pair. This is like a hashmap. You provide a key and the value, then when you want the value back, you give the key and the function gets the value. Here's a short example.
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("message", "This is my message to be reloaded");
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String message = savedInstanceState.getString("message");
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
Your saved message will be toasted to the screen.
One major note that all new Android developers should know is that any information in Widgets (TextView, Buttons, etc.) will be persisted automatically by Android as long as you assign an ID to them. So that means most of the UI state is taken care of without issue. Only when you need to store other data does this become an issue.
From Android Docs:
The only work required by you is to
provide a unique ID (with the
android:id attribute) for each widget
you want to save its state. If a
widget does not have an ID, then it
cannot save its state
A good information: you don't need to check whether the Bundle object is null into the onCreate() method. Use the onRestoreInstanceState() method, which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null
Store information:
static final String PLAYER_SCORE = "playerScore";
static final String PLAYER_LEVEL = "playerLevel";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(PLAYER_SCORE, mCurrentScore);
savedInstanceState.putInt(PLAYER_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
If you don't want to restore information in your onCreate-Method:
Here are the examples: Recreating an Activity
Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(PLAYER_SCORE);
mCurrentLevel = savedInstanceState.getInt(PLAYER_LEVEL);
}
Basically onSaveInstanceState(Bundle outBundle) will give you a bundle.
When you look at the Bundle class, you will see that you can put lots of different stuff inside it. At the next call of onCreate(), you just get that Bundle back as an argument.
Then you can read your values again and restore your activity.
Lets say you have an activity with an EditText. The user wrote some text inside it.
After that the system calls your onSaveInstanceState().
You read the text from the EditText and write it into the Bundle via Bundle.putString("edit_text_value", theValue).
Now onCreate is called. You check if the supplied bundle is not null. If thats the case,
you can restore your value via Bundle.getString("edit_text_value") and put it back into your EditText.
This is for extra information.
Imagine this scenario
ActivityA launch ActivityB.
ActivityB launch a new ActivityAPrime by
Intent intent = new Intent(getApplicationContext(), ActivityA.class);
startActivity(intent);
ActivityAPrime has no relationship with ActivityA.
In this case the Bundle in ActivityAPrime.onCreate() will be null.
If ActivityA and ActivityAPrime should be the same activity instead of different activities,
ActivityB should call finish() than using startActivity().
If Data Is not Loaded From savedInstanceState use following code.
The problem is url call is not to complete fully so, check if data is loaded then to show the instanceState value.
//suppose data is not Loaded to savedInstanceState at 1st swipe
if (savedInstanceState == null && !mAlreadyLoaded){
mAlreadyLoaded = true;
GetStoryData();//Url Call
} else {
if (listArray != null) { //Data Array From JsonArray(ListArray)
System.out.println("LocalData " + listArray);
view.findViewById(R.id.progressBar).setVisibility(View.GONE);
}else{
GetStoryData();//Url Call
}
}