I know that this topic has been already beaten enough, but I still don't understand completely if Android System has fine behavior in following case:
I created small app consists of two classes, here is the code:
Main.java
public class Main extends Activity {
private Button bv;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bv = (Button) findViewById(R.id.hello_txt);
bv.setOnClickListener(
new OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(Main.this, Main2.class);
startActivity(i);
}
}
);
}
}
Main2.java
public class Main2 extends Activity {
private TextView countOfActivities;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
countOfActivities = new TextView(this);
setContentView(countOfActivities);
countOfActivities.setText("Count of Activities: " + getInstanceCount());
}
}
When I clicked on the button from first activity several times, I get that even after pressing BACK button that should call second Activity's onDestroy() it's instance remains in the memmory.
Only after creating about 35 instances next click let me know, that GC cleared the memmory.
I just want to completely be sure that it is normal system's behavior.
Following pictures from Emulator and LogCat
Button clicked 10 times
LogCat output after clicked
Yes, the system works fine.
When you press the back button, your activity is removed from the activity stack.
onDestroy() may have been called, this doesn't mean that the instance was actually unallocated from the memory.
Related
I have tried every way possible to use SaveState in a prior post I was instructed to use finish() or use onBackPressed ok that saves the data on the MainActivity but I would like to understand how to use Bundle savedInstanceState below is my code arrangment Please comment what is wrong and offer suggested fixes My goal is to only restore one value in one of the EditText fields?
Yes I have looked at Adroid Developer and numerous other quality sites
public class MainActivity extends AppCompatActivity {
Button btnAdd;
Button brnNext;
EditText ETinput;
EditText ETans;
float X = (float) 10.0;
public final static String EXTRA = "com.dwight.thebigtest.pagetwo.MESSAGE";
public int sVarA;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
savedInstanceState.putInt("sVarA",sVarA);
btnAdd = (Button) findViewById(R.id.btnAdd);
brnNext = (Button) findViewById(R.id.btnNext);
ETans = (EditText) findViewById(R.id.ETans);
ETinput = (EditText) findViewById(R.id.ETinput);
addListenerOnButton_ADD();
}
#Override
protected void onRestoreInstanceState(Bundle InState) {
super.onRestoreInstanceState(InState);
if(InState != null) {
sVarA = InState.getInt("sVarA");
ETans.setText(String.valueOf(sVarA));
}
}
private void addListenerOnButton_ADD(){
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
float Y = Float.valueOf(ETinput.getText().toString().trim());
float Z = Y + X;
ETans.setText(String.valueOf(Z));
}
});
}
public void sendNext(View view) {
Intent intent = new Intent(MainActivity.this,PageTwo.class);
String message = ETans.getText().toString().trim();
intent.putExtra(EXTRA,message);
startActivity(intent);
}
}
This code makes the trip back from the secondActivity
private void addListenerOnButton_Back(){
btnBack.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(PageTwo.this,MainActivity.class);
startActivity(i);
//onBackPressed();// Less Screen Flash
}
});
}
}
This snippet caught my eye:
Intent i = new Intent(PageTwo.this,MainActivity.class);
startActivity(i);
//onBackPressed();// Less Screen Flash
You're making a new Intent to return to the MainActivity, but instead, it creates a new instance of MainActivity which is then added to the activity stack. When the new MainActivity is created, it has NO savedInstanceState: it's brand new.
onBackPressed and finish are the appropriate ways of finishing the current activity and returning to the previous one.
The purpose of savedInstanceState is to restore the state of an activity when it is destroyed by the system (for instance, due to lack of memory). If you simply return to the previous activity via the back button, savedInstanceState is not used because the activity is still in memory. No need to restore it.
If you enable "Don't keep activities" in your phone/emulator's Developer Options, Android will force-stop activities once abandoned. So when PageTwo is created and shown, since MainActivity is no longer in the foreground, it will be destroyed. This is where the instance state is saved. When you return to the same MainActivity (pressing back), the activity must be recreated using that instance state.
For more information, research about Android's activity lifecycle.
This probably demonstrates the most appalling lack of understanding of the activity life cycle, but please be sympathetic. I am ultimately going to want to invoke Activity B from Activity A a number of times, each time passing a different parameter to Activity B which is then responded to by the user and stores/sets various public variables. As a precursor to this, I just want to get my head round how Activity A sees the change to a public variable that Activity B has changed.
I have three very simple classes: Common.java that holds the public variables, the main activity MainActivity.java and the child activity Child.java. There is only one public variable right now; it's the string mess1 which is initialized to "***". All the code does at the moment is when mainbutton is clicked in MainActivity, it invokes Child. In Child, we immediately set mess1 to "Child here" then set the text in a Child-based TextView to mess1. On clicking the childbtn button in Child, we finish() the child activity (and of course the system returns us to MainActivity.
When this app is run, wee see the three stars displayed in MainActivity. When mainbutton is pressed we go to Child and see "Child here" displayed. When the childbtn is pressed, we return to MainActivity BUT, the three stars are still there although we know for sure that mess1 now holds "Child here".
My questions are:
1. Why, when we know mess1 has been changed, does MainActivity still display "***" on return from the Child activity?
2. What do I need to change in the code to get "Child here" to display?
Relevant code extracts follow. Thanks in advance for your help.
Common.java
public class Common
{
public static String mess1 = "***";
}
MainActivity.java
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mainbutton = (Button)findViewById(R.id.mainbutton);
TextView maintop = (TextView)findViewById(R.id.maintop);
mainbutton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
startActivity(new Intent(MainActivity.this, Child.class));
}
});
maintop.setText(Common.mess1);
}
Child.java
public class Child extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_child);
TextView childtext = (TextView) findViewById(R.id.childtext);
final Button childbtn = (Button) findViewById(R.id.childbtn);
Common.mess1 = "Child here";
childtext.setText(Common.mess1);
childbtn.setOnClickListener
(new View.OnClickListener()
{public void onClick(View v)
{finish();
}
}
);
}
Likely you are moving back on the back stack history and you are resuming the previous activity that was placed in a paused state and therefore the onCreate isn't being called but the onResume (of the initial activity)..
Using global state this way isn't advised but this should work if you place the appropriate code in the onResume method.
You should set the text in onResume() of MainActivity. When you get back from Child.java onResume() (not onCreate()) is invoked and, since maintop's text is set in onCerate() only, nothing changes it on return.
protected void onResume() {
super.onResume();
maintop.setText(Common.mess1);
}
Reference: Activity Lifecycle and Implementing the lifecycle callbacks
I have two activities A, B . Now from A i call B by pressing a button (using startActivity()) , then press Back key to go back to A . Now when i press Button again to go to B , fresh activity is called (as expected).
Now can someone tell me how to show old previous state of B ?
I have read this article
Saving Android Activity state using Save Instance State , but couldn't help myself :(
public class B extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main1);
if(savedInstanceState!=null){
EditText editText=(EditText)findViewById(R.id.editText1);
editText.setText(savedInstanceState.getString("EditBox"));
}
}
#Override
protected void onSaveInstanceState(Bundle onSaveInstanceState) {
System.out.println("B.onSaveInstanceState()");
super.onSaveInstanceState(onSaveInstanceState);
onSaveInstanceState.putString("EditBox","Hello");
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
System.out.println("B.onRestoreInstanceState()");
super.onRestoreInstanceState(savedInstanceState);
EditText editText=(EditText)findViewById(R.id.editText1);
editText.setText(savedInstanceState.getString("EditBox"));
}}
My Class A
public class A extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button=(Button)findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i=new Intent(StartActivityforresultActivity.this,B.class);
startActivity(i);
}
});
}
With what it sounds like you're trying to do you have two options:
1. Save the state of B when B's onDestroy or onBackPressed is called. You'll have to save this to memory or write it out using some sort of persistence (SharedPreferences, local file, etc). Then whenever B is started, check to see if that data exists and use it to load the state.
2. Override onBackPressed so that when it is pressed you aren't calling super.onBackPressed. Instead start an instance of activity A and set your intent's flags to be FLAG_ACTIVITY_REORDER_TO_FRONT before calling startActivity. So something like this:
Intent intent = new Intent(this, A.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
Now when you hit back, it should find the instance of A that is in your activity stack and just bring it to the front. You may have to add the same flag whenever you start B as well.
This looks very similar to my previous question because it's some sort of follow up. I was not very happy with the only solution given; also, the solution was for a problem slightly different from this one. So let me try to explain the problem again...
A notification is created at boot (with a BroadcastReceiver).
My app main activity is opened and the home button is pressed (the activity will be sent to the back stack).
I pull down the status bar and press on the notification previously created at boot.
That will start some activity, different from the main one.
I press the back button and the main activity is displayed.
This is not very different from my previous question... The thing is, "main activity" was just an example. I could have opened the app main activity and then opened the about activity through a menu option and pressed the home button. The back stack would now be MainActivity ยป AboutActivity. Which means that when the back button is pressed while in "some activity" (started by pressing the notification), we would be brought to the top of the back stack, that is, the about activity.
What basically want is to prevent any other activity to be opened when I press the back button while in "some activity" (again, started by pressing the notification). I want to be brought exactly where I was, that could be the desktop or some other app's activity, but not my app's MainActivity nor AboutAcitivity cause that's not where I was, those were in the back stack, "sleeping" in the background.
I have come up with a solution, but I don't think it's very elegant and I was looking for something more, well, elegant... If you have any other suggestion, please, let me know.
Anyway, this is my proposed solution:
// I use this class for public static (or public static final) members and
// methods
public final class AppHelper {
public static final String KEY_RESUME_FROM_NOTIFICATION = "resumeFromNotification";
private static boolean sResumeFromNotification = false;
public static boolean getResumeFromNotification() {
return sResumeFromNotification;
}
public static void setResumeFromNotification(boolean resumeFromNotification) {
sResumeFromNotification = resumeFromNotification;
}
}
public class MainActivity extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
(...)
}
#Override
protected void onResume() {
super.onResume();
if(AppHelper.getResumeFromNotification()) {
AppHelper.setResumeFromNotification(false);
moveTaskToBack(true);
}
}
}
public class AboutActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
(...)
}
#Override
protected void onResume() {
super.onResume();
if(AppHelper.getResumeFromNotification()) {
AppHelper.setResumeFromNotification(false);
moveTaskToBack(true);
}
}
}
public class SomeActivity extends Activity {
// This will be called when the notification is pressed and the activity is
// not opened yet
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
(...)
extractIntentExtras(intent);
}
// This will be called if the activity is already opened and the
// notification is pressed
#Override
protected void onNewIntent(Intent intent) {
extractIntentExtras(intent);
super.onNewIntent(intent);
}
private void extractIntentExtras(Intent intent) {
Bundle bundleExtras = intent.getExtras();
if(bundleExtras != null) {
// These intent extras are set on the Intent that starts this activity
// when the notification is pressed
AppHelper.setResumeFromNotification(bundleExtras.getBoolean(
AppHelper.KEY_RESUME_FROM_NOTIFICATION));
mRowId = bundleExtras.getLong(AgendaNotesAdapter.KEY_ROW_ID);
populateNoteUpdateFields();
}
}
}
I don't know, but this solution doesn't look very elegant to me (but it works as I expect it) and I'm looking for alternatives or for strong opinions on my proposed solution as an acceptable and good solution. Thoughts?
After doing some more reading perhaps this is the combination of flags you need:
Intent intent = new Intent(mContext, SomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivity(intent);
I think that should force your SomeActivity class to be launched in a completely new task.
When launching the Activity from the notification, you can control how the Activity you are about to open is put on the back stack, and what task it's associated with with Intent flags. You can try something like:
Intent intent = new Intent(mContext, SomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
If that doesn't work, try setting a few of the other flags until you get the desired behavior.
Do you ever want your MainActivity to stay in history? If not then my simple, crude solution is to finish the MainActivity when it is paused.
(Call this in your MainActivity)
#Override
public void onPause() {
finish();
}
This will ensure that your MainActivity is removed from history when you navigate away from it, and will never appear when the back button is pressed.
This could be used for AboutActivity as well.
I'm trying to make an app where you start at a menu, click a button and are brought to a list of items (which I later hope to make clickable). But I can't seem to make it call my next activity. Can anyone help?
Your main class / activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Menu Button
Button startNewActivity = (Button)findViewById(R.id.startnew);
startNewActivity.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent newActivityIntent = new Intent(YOUR-CLASS-NAME.this,NewActivity.class);
startActivity(newActivityIntent);
}
});
Your NewActivity Class:
public class NewActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.new);
}
}
Is the question "How do I call the next activity" ?
If so, it's pretty easy - Assuming the Activity you want to call is "SomeActivity", call this:
Intent someActivity = new Intent(getBaseContext(), SomeActivity.class);
startActivity(someActivity);
There's also a "startActivityForResult" method, if you want data back from the Activity you're calling. For reference, the Activity page of the API Documentation can be found here. Good luck!