can we use Intent extras to preserve UI ? - android

I was reading about different methods used to preserve UI data across configuration changes and system-initiated process death. i came across this paragraph that i coundn't understanad.
can anyone explain?
Additionally, when you open an activity from an intent, the bundle of extras is delivered to the activity both when the configuration changes and when the system restores the activity. If a piece of UI state data, such as a search query, were passed in as an intent extra when the activity was launched, you could use the extras bundle instead of the onSaveInstanceState() bundle.
Link: https://developer.android.com/topic/libraries/architecture/saving-states

What they are saying is that when an Activity is restored after "configuration changes" or "system-initiated process death" you have multiple sources you can use to retrieve data from to get it setup as it was before e.g.:
Whatever was stored in the savedInstanceState bundle just before the Activity was killed.
Whatever was stored in the Intent that was used to start the Activity in the first place. This same Intent is still available after an activity is killed and
restored.
For example say you had a UserProfile Activity that showed information about the current user. When you launch the UserProfileActivity from some other activity, you could include information about the user in the Intent:
// Create Intent to start the `UserProfileActivity`
final Intent intent = new Intent(this, UserProfileActivity.class);
intent.putExtra("first_name", "John");
intent.putExtra("last_name", "Doe");
// Launch the intent
startActivity(intent);
Then in the UserProfileActivity you can retrieve that info:
// Get the intent that was used to launch this activity
final Intent = getIntent();
// Get the required info from the intent
final String firstName = intent.getStringExtra("first_name");
final String lastName = intent.getStringExtra("last_name");
// Update the UI
TextView tvName = findViewById(R.id.userName);
tvName.setText(firstName + " " + lastName);
Now say the the UserProfileActivity had an "About You" EditText field and a "Save" button which will store the contents of the EditText in their profile.
The user is part way through writing stuff in the "About You" EditText when they decide to rotate the screen. The activity gets killed but they haven't hit the "Save" button yet! All the stuff they wrote gets lost = unhappy user!
How can we create a better user experience? When the UserProfileActivity is about to get killed, onSaveInstanceState() will get call, so save the current contents of the "About You" EditText in the savedInstanceState bundle, and restore it once the activity is recreated.
#Override
protected void onSaveInstanceState(Bundle outState) {
// Store relevant state variables
final EditText etAboutYou = findViewById(R.id.aboutYou);
outState.putString("about_you", etAboutYou.getText().toString());
super.onSaveInstanceState(outState);
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_profile);
// Restore saved state if required
if (savedInstanceState != null) {
final String aboutYou = savedInstanceState.getString("about_you");
final EditText etAboutYou = findViewById(R.id.aboutYou);
etAboutYou.setText(aboutYou);
}
}

Related

Intent returning null after leaving app and coming back

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");
}
}

save data while destroying and recreating activity

I've settings(called from onCreateOptionMenu) from my Activity which uses to update the UI on current Activity.
Starting Preferences on updating Preferences, Calling Activity needs to update UI on Preference basis.
Snippet how Preference called:-
Intent in = new Intent(this, PrefsSecondaryActivity.class);
in.putExtra("caller", "sx");
startActivityForResult(in, SECSETTINGS);
Catch to get the UI updates
if (requestCode == SECSETTINGS) {
Intent intent = getIntent();
finish();
startActivity(intent);
}
I used to Re-Create that activity with the above snippet. Inside of onCreate() of Activity. I checked the Preference Name-value Pair and update the UI which workd perfectly fine.
How to store the values which are inside that activity while destroying and recreating activity?
As I'm Destroying and Recreating activity which renders whole new Activity with no values inside of it.
I tried to set onSavedInstanceState() while calling Preferences and onRestoreInstanceState() is called in catch the onActivityResult()
Settings values in Preferences makes good change of SLOC. So it's not preferrable way right now.
Any suggestion would be welcome.
This is how I do that (I have some static variables declared in my Activity):
#Override
protected final void onRestoreInstanceState(final Bundle inState)
{
// Restore the saved variables.
isChartShown = inState.getBoolean("chart", false);
qIndex = inState.getInt("index");
scores = inState.getIntArray("scores");
}
#Override
protected final void onSaveInstanceState(final Bundle outState)
{
// Save the variables.
outState.putBoolean("chart", isChartShown);
outState.putInt("index", qIndex);
outState.putIntArray("scores", scores);
}
This code works for me. I use it for saving some state variables used to maintain the values upon rotation.
[EDIT]
Otherwise, if you force the app finishing, then you'd go for Sharedpreferences:
just save your values before finishing and reload them in onCreate.

Android: Store a destination activity in a bundle

So I'm working on this project, and its rather large in size. I've have put a kind of 'behind the scenes' security login system in.
Pretty much, all the activities extend the subActivity class, and the subActivity extents Activity. This has allowed me to make every form automatically .putExtra() when it starts a new activity, and on every onCreate() it will check the incoming data and determine whether it should force the user to login (push them to the LoginActivity).
So right now, the login activity, on successful login, it loads to the MainActivity. I want it to dynamically load the activity the user was last at...
This means, store the class of the activity that launched the LoginActivity in the extras, and then have the LoginActivity, on successful login, get the class from the extras and use it to start the Activity.
I'm looking for the simplest way possible, I was trying serializable, but was having a lot of issues and thought there must be another way to just pass a reference to the 'destination' class.
Hope this makes sense!
Cheers
You could use Java reflection API:
Call your login activity like this:
Intent intent = new Intent(this, LoginActivity.class);
intent.putExtra("activity", getClass().getName());
startActivity(intent);
Then parse the calling activity's name in login activity and go back to it after successful log in:
private String mActivity;
#Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
// ...
mActivity = "";
Bundle extras = getIntent().getExtras();
if (extras != null)
mActivity = extras.getString("activity");
}
}
#Override public void onClick(View view)
{
try {
Class<?> cls = Class.forName(mActivity);
startActivity(new Intent(this, cls));
}
catch (Exception e) {
e.printStackTrace();
}
}
In the LoginActivity, simply call finish() which will return to the next Activity on the Stack, in your case going back to the previous one.
You can start your login activity by startActivityForResult and then check result of login activity in your main activity. Look here http://developer.android.com/reference/android/app/Activity.html#StartingActivities

Saving view values while switching between activities in android

I'm working on a tabbed application in android. In the first tab I've a search engine with textviews, spinners.. and if I filled these params and switch to other tab or to the search results activity, and I return back to this tab (search engine tab) I found that all the fields are reinitialized. But I want to keep their value in order to remember what I searched for..
Is there a way to keep my data when I switch between activities ?
Thanks for your help.
You can override onSaveInstanceState (which will be called before you transition to a new activity) and onRestoreInstanceState (which will be called before your activity is resumed) e.g. :
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
super.onSaveInstanceState(savedInstanceState);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
I guess you are creating intent object to initiate your tab's activity with addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP). Just remove this function if you have done something like this. For example , create intent object like below,
Intent intent = new Intent(this, tabActivity.class);
Not like this,
Intent intent = new Intent(this, tabActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
If you do so , your will get the exact state of a tab as u left it before switching to another tab.

How to use onSavedInstanceState example please

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
}
}

Categories

Resources