Saving variable on application destroy doesn't work - java

I want to save some variables after exiting activity which are set during the lifetime of my android activity and show them when I launch activity again but I don't seem to manage to get things working. Here's how I do it:
I created an integer variable "test":
public class MainActivity extends Activity {
int test = 1;
Then I write a method to change this varibale's value by pressing a button:
public void changeValueTest(View view) {
this.test = 2;
}
Then I use onSaveInstanceState() method to save the changed value:
static final String TEST = "test variable";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt(TEST, this.test);
super.onSaveInstanceState(savedInstanceState);
}
Then in onCreate() method I put this code to get and show the changed "test" value:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
this.test = savedInstanceState.getInt(TEST);
TextView textView1 = (TextView)findViewById(R.id.textView1);
textView1.setText("Current test value is: " + test);
}
}
So I open the application, press the button to change the "test" value from 1 to 2, then exit the application, wait until it's properly removed from memory (when Application Manager doesn't show it in "Cached processes" window), start the application again and textView1 view shows 1 instead of 2. What am I doing wrong? Please help! Thanks!

What am I doing wrong?
Nothing. Instance state does not cover the scenario that you are describing.
Mostly, instance state is used in configuration changes (e.g., screen rotation). Secondarily, instance state is used if the user leaves your app by means other than "exit" (by which I assume you mean BACK) and returns to you via means like the recent-tasks list.

As documented in Developer.android , Saved instance only saves your Activity state, It will not save your variable as you wanted,
I would suggest you to use SharedPreference instead.

Related

Sum two variables in Android Studio Java

I'm new to Android Studio but I have programmed Java before.
I need to sum two numbers in one activity and then print the result in a new activity, the compiler it's ok but the app force close.
Where is my fault?
Here's my source:
public class MainActivity extends AppCompatActivity {
private Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OpenActivity2();
}
});
}
public void SumAB(TextView tva, TextView tvb, TextView tvsomma) {
double a, b, somma;
a = Double.parseDouble(tva.getText().toString());
b = Double.parseDouble(tvb.getText().toString());
somma = a + b;
tvsomma.setText(String.valueOf(somma));
}
public void OpenActivity2() {
TextView result = findViewById(R.id.textView4);
TextView var1 = findViewById(R.id.textView);
TextView var2 = findViewById(R.id.textView3);
Intent intent = new Intent(this, Activity2.class);
startActivity(intent);
SumAB(var1,var2,result);
}
}
You are calling SumAB() after you launch Activity2 with startActivity(intent); so when the tvsomma.setText() is executed it expects to find a tvsomma view but it's a part of the first activity which should be in background now.
I need to sum two numbers in one activity and then print the result in a new activity
You should remove SumAB() method and do the below behavior
Here your are trying to display the result at the same activity; if you want to display the result on the second activity, you've to send this sum using
double a, b, somma;
a = Double.parseDouble(tva.getText().toString());
b = Double.parseDouble(tvb.getText().toString());
somma = a + b;
intent.putExtra("SUM", somma);
startActivity(intent);
And receive this result at Activity2 using:
int somma = getIntet().getIntExtra("SUM", 0); // 0 is the default value
Then set this value to some TextView at Activity2 with
myTextView.setText(String.valueOf(somma));
You need to make changes in your code.
Create a separate java file for another activity.
setContentView(R.layout.activity_main) method inside onCreate() function in the
above mentioned Java file is used to set layout for activity one.
To set view for another activity, you need to create another java file say "sum.java". Here you need to mention again setContentView(R.layout.activity2_main) method inside onCreate() function. activity2_main represents the second activity where you want to print value of sum.
Instantiate all the UI widgets in a java files respectively.
In the java file, instantiate TextView var1, TextView var2 and button.
In the second java file, instantiate TextView result.
The aim here is, all the UI widgets that you are using in first activity should me
mentioned in first file and correspondingly second file should have widgets related to
second activity.
You also need to perform type casting while instantiating the variables like:
TextView var1 = (TextView)findViewById(R.id.textView); [Down casting is performed here].
You need to use following two functions if you want to send data from one activity to another.
putExtra() method is use for send the data, data in key value pair key is variable
name and value can be Int, String, Float etc.
getStringExtra() method is for get the data(key) which is send by above method.
according the data type of value there are other methods like getIntExtra(),
getFloatExtra()
Check out this link to get more insight on this topic- https://www.geeksforgeeks.org/android-how-to-send-data-from-one-activity-to-second-activity/

How to refer to a randomly generated button's elements

I need to randomly generate a button and then grab elements from it from the activity that the button opens. Specifically the universal ID of a "character" that I have specified in PersistentData. (That whole class works fine, though. The problem is between this activity and the next.) The child activity has multiple activities that can call it. (There are several activities with lists made from different characters from a pool, and each list has these buttons that are shown below, which all point to the same activity that loads information from PersistentData based on the data that its supposed to pull from said button.) The following 1 block of code is in the onCreate method for this activity. (Automatically generated, I just added this in after I called the layouts from the XML file)
for (fID = 0; fID < PersistentData.fName.size(); fID++) {
if (PersistentData.fName.get(fID) != null) {
Button[] Btn = new Button[PersistentData.fName.size()];
Btn[fID].setHeight(200);
Btn[fID].setWidth(200);
Btn[fID].setTextSize(40);
Btn[fID].setText(PersistentData.fName.get(fID));
Btn[fID].setTag(fID);
Layout.addView(Btn[fID]);
Btn[fID].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int btnID = fID;
gotoActivity(v);
}
});
} else continue;
}
Here is gotoActivity(). (In the class, not onCreate)
public void gotoActivity(View view) {
Intent intent = new Intent(this, TargetActivity.class);
startActivity(intent);
intent.putExtra("btnClicked", /*IDK WHAT TO PUT RIGHT HERE*/);
}
I have put several things there, actually. Mostly, they have been various ways of declaring a variable on the creation of the button.
Here's the TargetActivity.class
public class Fighter extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fighter);
Intent intent = getIntent();
Bundle bundle =
intent.getExtras(); //line ***
**
TextView viewName = (TextView) findViewById(R.id.fighterName);
viewName.setText(PersistentData.fName.get(fID));
}
}
What should I put in the other class that I should get here**? (fID) is the thing that I'm trying to get. I have tried putting and getting fID using the methods as described in the Create an Activity page from Android and it continued to give me a NullPointerException at line *** (I split the line to be precise.)
Any help is appreciated. I would not be surprised if there is a better way to do this, requiring me to scrap all my work and restart. I would prefer not to, though lol. I will be checking this post periodically throughout the day, so if you have any questions or require more detail, just post or message me. Thank you beforehand.
I think the reason you got a NullPointerException because you started the activity before persing the extra.
In the gotoActiviy, make sure the extra(s) method are called before you start the activity. that is
public void gotoActivity(View view) {
Intent intent = new Intent(this, TargetActivity.class);
intent.putExtra("btnClicked", "Strings");
startActivity(intent);
}
As you can see, a string was the extra, you can put any variable type as the extra just make sure at the activity being called, the same variable type gets the extra.
as an example
String getValue = getIntent().getExtras().getString("btnClicked");
Int getValue = getIntent().getExtras().getInt("btnClicked");
/** If integer was the value activity extra */

Android, call method in UI thread after restarting Activity due to configuration change (device rotation) does nothing

SITUATION:
An application with resources for portait and landscape, has a simulator that I keep after configuration changes (the user can switch orientation while the simulation is running).
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Installer.installApkData(this);
simulator = new Simulator(this);
MainActivity prevActivity = (MainActivity)getLastCustomNonConfigurationInstance();
if(prevActivity!= null) {
// So the orientation did change
// Restore some field for example
this.simulator = prevActivity.simulator;
//this.mNavigationDrawerFragment = prevActivity.mNavigationDrawerFragment;
//this.mTitle = prevActivity.mTitle;
Log.d("APP","Activity restarted: simulator recreated");
}
requestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.activity_main);
setProgressBarVisibility(true);
mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
.findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public Object onRetainCustomNonConfigurationInstance() {
//restore all your data here
return this;
}
...
There is a method in the activity that changes the selected section in the NavigationDrawer, in the UI thread because if not it crashes.
public void showHud() {
// TODO Auto-generated method stub
runOnUiThread( new Runnable() {
public void run() {
mNavigationDrawerFragment.select(1);
onSectionAttached(2);
restoreActionBar();
}
});
}
This method is used to go directly to display the simulation once the simulator has been connected.
PROBLEM:
All this system works except for when I connect the simulator after switching the orientation. It executes the runOnUiThread but it does nothing. I think the reason for that is that it loses the UI thread that created that view when the activity is restarted.
As you can see there are two lines commented in the reloading of the simulator where I also tried to save the NavigationDrawer object without success in the test: same behavior.
I also tried to save the prevActivity and in the method showHUD(), first asking if its null and if not, execute the method inside the prevActivity. Expecting that it will access the original UI Thread, but I was mistaken.
Is there any solution to keep this UI Thread during the restarting of an activity? or maybe another type of solution?
Thanks a lot.
You should be checking your onSavedInstanceState in your Activity. This is how the Android OS is designed to handle this. You are trying to do this yourself, when you should be relying on the OS supplied functionality.
Quite a few examples of this (if you search SO):
Android: Efficient Screen Rotation Handling
Handle screen rotation without losing data - Android
If you want to save configuration, you need to save specific things. You can do this in the onPause() or on onSaveInstanceState().
If onCreate() is called after your configuration change, you can get what you need back out of the bundle. when you get it back out, you can then set what you need.
See this: http://developer.android.com/training/basics/activity-lifecycle/recreating.html
I am correctly retaining the data object but after a device rotation the function in UI thread has no effect, a function to change the selected section in the NavigationDrawer. I thought it was because I was losing the correct UI thread but actually, what I was losing is this NavigationDrawerFragment.
Just by adding the setRetainInstance(true) line in the OnCreate() of the NavigationDrawerFragment solves the problem:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
...

Can't reach my (final) button inside my OnClickListener function

My final goal of this snippet is to:
call a Dialog(Interface) from a button.
let the end user select an option (in a list of 5 options)
change the button text to the selected option
Currently I have this:
public void onCreate(Bundle savedInstanceState) {
setLayoutState();
// rest of code omitted
}
then the setLayoutState() that instatiates the button
public void setLayoutState() {
setContentView(R.layout.main);
Button rate = (Button) findViewById(R.id.ratebutton);
rate.setOnClickListener(onRatePress);
}
So here: setOnClickListener calls to a separate function (to keep things clean, the Activity has got a lot of buttons)
private final View.OnClickListener onRatePress = new View.OnClickListener() {
public void onClick(View v) {
final ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
context, R.array.rates, android.R.layout.select_dialog_item );
adapter.setDropDownViewResource(android.R.layout.select_dialog_item);
new AlertDialog.Builder(context).setTitle("Rate this item!")
.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Common.makeToast(context,adapter.getItem(which) + "", 3000);
Button rate = (Button) findViewById(R.id.ratebutton);
rate.setText(adapter.getItem(which)+"");
// TODO: user specific action
dialog.dismiss();
}
}).create().show();
}
};
While this works fine I was wondering if there is a way I can pull this off without redeclaring the Button rate inside the Dialog's onClick
I've already tried declaring the button as final in the top part, but that won't let me call the button in the Dialog's onClick.
A variable in Java has a scope. It is always available inside the block (a pair of braces {} ) that contains its declaration, and in any block contained by this block. But not ouside.
Thus if you declare your button inside a method, it is not accessible outside of this method.
You button is only accessible inside drupappSetUploadLayout.
if you want it to be accessible to all methods, then put it inside the class body directly. Such a variable is called a field and fields are accessible to all methods.
public class A {
private Button b;
public void foo() {
b=null;
}
}
b can be accessed by all methods.
Read more about the basics of java, you should consider making small J2SDK programs before starting with android.
The View v that is a parameter refers to the button that was clicked... so you could remove the redeclaration of the button and use
( (Button) v).setText(adapter.getItem(which)+"");

How do I handle screen orientation changes when a dialog is open?

I have an android app which is already handling changes for orientation, i.e. there is a android:configChanges="orientation" in the manifest and an onConfigurationChange() handler in the activity that switches to the appropriate layout and preps it. I have a landscape / portrait version of the layout.
The problem I face is that the activity has a dialog which could be open when the user rotates the device orientation. I also have a landscape / portrait version of the dialog.
Should I go about changing the layout of the dialog on the fly or perhaps locking the activity's rotation until the user dismisses the dialog.
The latter option of locking the app appeals to me since it saves having to do anything special in the dialog. I am supposing that I might disable the orientation when a dialog opens, such as
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
and then when it dismisses
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
Would that be a sensible thing to do? If the screen orientation did change while it was locked, would it immediately sense the orientation change when it was unlocked?
Are there alternatives?
I would recommend not turning off the screen rotation, instead of this handle the configuration changes for the Dialog. You could use one of these two approach for this:
The first one is using a flag variable in onSaveInstanceState(outState) method, and restore the dialog onCreate(bundle) method:
in this example my flag variable is called 'isShowing Dialog', when the onCreate method is called by the android System for first time, the bundle argument will be null and nothing happens. However when the activity it's recreated by a configuration change (screen rotation), the bundle will have the boolean value isShowing Dialog, previously saved by the inSaveInstanceState(...) method, so if the variable gets true the dialog is created again, the trick here is set the flag in true when the dialog get showing, and false when it's not, is a little but simple trick.
Class MyClass extends Activity {
Boolean isShowingDialog = false;
AlertDialog myDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
if(savedInstanceState!=null){
isShowingDialog = savedInstanceState.getBoolean("IS_SHOWING_DIALOG", false);
if(isShowingDialog){
createDialog();
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("IS_SHOWING_DIALOG", isShowingDialog);
super.onSaveInstanceState(outState);
}
#Override
protected void onPause() {
if(myDialog!=null && myDialog.isShowing()) {
myDialog.dismiss();
}
}
private void createDialog() {
AlertDialog.Builder dialog_builder = new AlertDialog.Builder(this);
dialog_builder.setTitle("Some Title"):
... more dialog settings ...
myDialog = dialog_builder.create();
myDialog.show();
isShowingDialog = true;
}
private void hideDialog(){
myDialog.dismiss();
isShowingDialog = false;
}
}
The second approach is to use the ability of the fragments components to retain its states, the main idea is create the dialog inside a fragment, there is the problem about detach and reattach the fragment during the configuration changes (because you need dismiss and show the dialog correctly), but the solution is very similar to the first approach. The advantage of this approach is that if you have an AlertDialog with a couple of configurations, when the fragment is recreated there is not needed to create and setting up the dialog again, only make it show() and the AlertDialog state is maintained by the fragment.
I hope this helps.
I suggest your Dialog should override onSaveInstanceState() and onRestoreInstanceState(Bundle) to save its state into a Bundle.
You then override those methods in your Activity, checking if the Dialog is shown and if so - calling the dialog's methods to save and restore it's state.
If you are displaying this dialog from a fragment, you will want to override OnActivityCreated(Bundle) instead of OnRestoreInstanceState.
For a source example see the built-in clock app provided with Android, where the SetAlarm Activity handles the TimePickerDialog this way.
If you are handling orientation changes yourself, then here is an approach.
I won't claim that this is an elegant solution, but it works:
You can keep track of whether the dialog has an active instance inside the dialog class itself, by using a static variable activeInstance, and overriding onStart() to set activeInstance = this and onCancel() to set activeInstance = null.
Provide a static method updateConfigurationForAnyCurrentInstance() that tests that activeInstance variable and, if non-null, invokes a method activeInstance.reInitializeDialog(), which is a method that you will write to contain the setContentView() call plus the code that wires the handlers for the dialog controls (button onClick handlers, etc. - this is code that would normally appear in onCreate()). Following that, you would restore any displayed data to those controls (from member variables in your dialog object). So, for example, if you had a list of items to be viewed, and the user were viewing item three of that list before the orientation change, you would re-display that same item three at the end of updateConfigurationForAnyCurrentInstance(), right after re-loading the controls from the dialog resource and re-wiring the control handlers.
You would then call that same reInitializeDialog() method from onCreate(), right after super.onCreate(), and place your onCreate()-specific initialization code (e.g., setting up the list of items from which the user could choose, as described above) after that call.
This will cause the appropriate resource (portrait or landscape) for the dialog's new orientation to be loaded (provided that you have two resources defined having the same name, one in the layout folder and the other in the layout-land folder, as usual).
Here's some code that would be in a class called YourDialog:
ArrayList<String> listOfPossibleChoices = null;
int currentUserChoice = 0;
static private YourDialog activeInstance = null;
#Override
protected void onStart() {
super.onStart();
activeInstance = this;
}
#Override
public void cancel() {
super.cancel();
activeInstance = null;
}
static public void updateConfigurationForAnyCurrentInstance() {
if(activeInstance != null) {
activeInstance.reInitializeDialog();
displayCurrentUserChoice();
}
}
private void reInitializeDialog() {
setContentView(R.layout.your_dialog);
btnClose = (Button) findViewById(R.id.btnClose);
btnClose.setOnClickListener(this);
btnNextChoice = (Button) findViewById(R.id.btnNextChoice);
btnNextChoice.setOnClickListener(this);
btnPriorChoice = (Button) findViewById(R.id.btnPriorChoice);
btnPriorChoice.setOnClickListener(this);
tvCurrentChoice = (TextView) findViewById(R.id.tvCurrentChoice);
}
private void displayCurrentUserChoice() {
tvCurrentChoice.setText(listOfPossibleChoices.get(currentUserChoice));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
reInitializeDialog();
listOfPossibleChoices = new ArrayList<String>();
listOfPossibleChoices.add("One");
listOfPossibleChoices.add("Two");
listOfPossibleChoices.add("Three");
currentUserChoice = 0;
displayCurrentUserChoice();
}
#Override
public void onClick(View v) {
int viewID = v.getId();
if(viewID == R.id.btnNextChoice) {
if(currentUserChoice < (listOfPossibleChoices.size() - 1))
currentUserChoice++;
displayCurrentUserChoice();
}
}
else if(viewID == R.id.btnPriorChoice) {
if(currentUserChoice > 0) {
currentUserChoice--;
displayCurrentUserChoice();
}
}
Etc.
Then, in your main activity's onConfigurationChanged() method, you would just invoke YourDialog.updateConfigurationForAnyCurrentInstance() whenever onConfigurationChanged() is called by the OS.
Doesn't seem the title was ever resolved (Google Necro Direct).
Here is the solution, matching the request.
When your activity is created, log the screen orientation value.
when onConfiguration change is called on your activity, compare the orientation values. if the values don't match, fire off all of your orientation change listeners, THEN record the new orientation value.
Here is some constructive code to put in your activity (or any object that can handle configuration change events)
int orientation; // TODO: record orientation here in your on create using Activity.this.getRequestedOrientation() to initialize!
public int getOrientation(){return orientation;}
public interface OrientationChangeListener {
void onOrientationChange();
}
Stack<OrientationChangeListener> orientationChangeListeners = new Stack<>();
public void addOrientationChangeListener(OrientationChangeListener ocl){ ... }
public void removeOrientationChangeListener(OrientationChangeListener ocl){ ... }
That's the basic environment. Here's your executive:
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (orientation != newConfig.orientation)
for (OrientationChangeListener ocl:orientationChangeListeners) ocl.onOrientationChange();
orientation = newConfig.orientation;
}
In YOUR code model, you may need to send the new configuration, with the event, or the two orientation values with the event. However, Activity.this.getOrientation() != Activity.this.getRequestedOrientation() during event handling (because we are in a logical state of change between two logical values).
In review of my post, i have determined that there could be some synchronization issues, with multiple events! This is not a fault of this code, but a fault of "Android Platform" for not having defacto orientation sense handlers on every window, thusly trashing the polymorphic benefits of using java in the first place..
See the answer from Viktor Valencia above. That will work perfectly with the slight adjustment that you move the createDialog() to onResume.
#Override
protected void onResume() {
super.onResume();
if(isShowingDialog){
createDialog();
}
}
Fetch the boolean isShowingDialog value at onCreate, as suggested, but wait for onResume to display the dialog.

Categories

Resources