Activity crashes while navigating back - android

All,
I am performing a navigation between 3 screens with dependency on each other.
Activity A is having a table with list of table rows. Each row has onclick listener event where in get the data from table row and passes to next screen Activity B.
Activity B starts with reading data from Intent and gets corresponding images & data etc and builds layout. It has a button where in reads data from edit text and passed to Activity C.
Activity C also builds layout based on data from Intent.
I acheived navigation among these activities with Parent activity configuration in Manifest file.
Now when I am doing back navigation in Activity C to Activity B, it's crashing because it could not find data from Intent as the control is coming from Child to Parent rather than Parent to Child.
I understand onCreate function of Activity B is executing again. So I kept onPause() and onResume() method in Activity B to hold the re-execution. But no luck.
Could some one advise how to handle this kind of scenario.
EDIT:
Activity B:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_activity);
getActionBar().setDisplayHomeAsUpEnabled(true);
source_activity = this.getIntent().getStringExtra("source_activity");
Bundle b = this.getIntent().getExtras();
if(b!=null)
DashBoardDisplay_l = b.getParcelable("obj");
Name_ref=DashBoardDisplay_l.getName();
}
Activity C:
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.activity_open_translate,R.anim.activity_close_scale);
setContentView(R.layout.point_log_activity);
Intent intent = getIntent();
String YourtransferredData = intent.getExtras().getString("ref_name");
Log.d(TAG, "ref name "+YourtransferredData);
Name=YourtransferredData;
Log.d(TAG, "name value "+Name);
getActionBar().setDisplayHomeAsUpEnabled(true);
new BackEndJob().execute();
}
Activity B contains some Intent which comes from prior screen Activity A. So while coming back to Activity B from Activity C, it could not find the data from Intent due to which it is resulting to null pointer exception.
So is there any option to display what is there in back navigation stack rather than recreating it with Savedinstance advised by Dalija.

From your description it looks like back navigation to Activity B should be prevented. So when user presses back button in Activity C it would go back to Activity A. If that is the case then you should finish Activity B when it passes the control to Activity C.
...
this.startActivity(intent); // start Activity C
finish();
}
Finishing activity will remove Activity B from navigation stack and completely destroy Activity B solving your back navigation issue.
However, if you do want to navigate back to Activity B then you should use onSaveInstanceState() and onRestoreInstanceState() in Activity B to save its state.
Recreating Activity
When you launch Activity C, Activity B is stopped. At that point system may decide to destroy Activity B at any time if it needs to clean up resources. You don't have any influence on that process and you cannot prevent it.
You can find more in Activity Lifecycle

Related

How to come back to my parent-activity without losing data?

I have an activity A with a "child activity" which is the activity B.
When I create an activity, like in this case in android, I choose for activity B, that his parent shall be activity A.
So, when I start my application, I have an icon in the appbar in the activity B, where I can go to the activity A.
In my case, the user for example edit-text and manipulate some variables in the activity A and call activity B with Intent.
NOW, in activity B, when I click on the "back" icon, I will go back to the parent activity in this case activity A and all manipulated data are "away", because the screen will be relaunched.
But when I dont click on the icon in the activity B but instead click "back" on my mobile phone, I will come back to activity A and all manipulated data will be still there.
SO my question is, is there a way, to come back to activity A by clicking the back icon in activity B, without that the screen will be relaunched. Because this icon was automatically added, when I say, which activity have to be the parent class. I would like, that I can go back with this icon, but not relaunch the screen, instead continue, where I was "last" time.
I show you a part of my manifest file for the activity B:
<activity
android:name=".strassenfuehrer_screen.VideoPlayerHandlungsleitfadenStrassenfuehrerActivity"
android:label="Straßenführer > Handlungsleitfaden > VideoPlayer"
android:parentActivityName=".strassenfuehrer_screen.MitHandlungsleitfadenStrassenfuehrerScreenActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="de.derdoenerdon.ressourcencockpit.strassenfuehrer_screen.MitHandlungsleitfadenStrassenfuehrerScreenActivity" />
</activity>
Here you can see the "back icon" in the activity B.
For help, I would be very thankfull, I am new in android and have difficulties with it.
Thanks a lot
In your manifest for Activity A add this attribute:
android:launchMode="singleTask"
This should solve your problem.
This makes sure that there is only one instance of Activity A in the BackStack. Therefore Activity A is only created once per Task.
Hope this helps.
for more information https://developer.android.com/guide/components/activities/tasks-and-back-stack
You can override onSaveInstanceState() into your Activity and save your data before leaving that Activity.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("KEY1", Value1);
savedInstanceState.putInt("KEY2", Value2);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
On coming back to that Activity you can get the saved data as bundle into you onCreate() method.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
if (savedInstanceState != null) {
// Restore value of members from saved state
int val1 = savedInstanceState.getInt("Key1");
int val2 = savedInstanceState.getInt("Key2");
} else {
// This is the case when you are openning this Activity for the for the first time
}
}

Wrong activity resumed after pressed back button

here is the brief of my activities actions :
Activity A is a launcher that call activity B with intent flag Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK.
When Activity A launch activity B, ondestroy is called in activity A, as expected.
When I press back button in activity B, ondestroy is called in activity B and it bring me to the hope launcher.
There are no alive activity at this moment, but the application is still visible and the task manager. When I click on it, it brings me to the activity A (maybe because it is the action MAIN in the manifest).
The behaviour I try to get is that when i press back button in activity B, it brings me to the home screen and I can return to is, as if it is root activity.
Can anyone help me for this ?
Thanks you !
If you click on the Back button in activity B, you will be able to navigate to the Home screen and return to the root activity(A) from the following sources.
why you used Intent.FLAG_ACTIVITY_CLEAR_TASK?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((Button)findViewById(R.id.btn_send)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Case 1.
//startActivity(new Intent(A.this, B.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
//Case 2.
startActivity(new Intent(A.this, B.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
}
});
}
FLAG_ACTIVITY_CLEAR_TASK
added in API level 11
int FLAG_ACTIVITY_CLEAR_TASK
If set in an Intent passed to Context.startActivity(), this flag will cause any existing task that would be associated with the activity to be cleared before the activity is started. That is, the activity becomes the new root of an otherwise empty task, and any old activities are finished. This can only be used in conjunction with FLAG_ACTIVITY_NEW_TASK.
Constant Value: 32768 (0x00008000)
Google Developer
You have to remove the flag FLAG_ACTIVITY_CLEAR_TASK, because as the android developer site here, the behavior will destroy your Activity A,
You need to remove that flag and then your activity will remain in the stack, and you will be able to back to activity A

How to detect if a parent activity is in the back stack and can go up without creating a new instance?

I have a system which has several Activity, Activity X, Activity A, Activity B, and Activity Y. Activity X consist of list of A. If we click on A, it will go to Activity A. Activity A consist of list of B. If we click on B, it will go to Activity B. Activity B will also consist of list of B. So, if we click B on Activity B, it will go to Activity B. It is possible to end up the stack like this: X -> A -> B -> B -> B -> B -> B. According to proper navigation by Android, if we click Up on Activity B, it should go to Activity A, no matter how deep the stack is. So, every B on the previous stack should end up on A. Up until now, it's simple. I just need to set the parent class of Activity B as Activity A.
The problem is I can go to Activity B from Activity Y. If I open the Activity B from Activity Y, this Activity B is not always know how to open Activity A because the parent of B can be another B or A. B only know his parent. The problem is how to detect that Activity A is reachable automatically without creating a new instance from Activity B? This way, I can make my code like the following on Activity B.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (canGoUpToActivityA()) { // this means this Activity B is opened from Activity A directly (A -> B) or indirectly (A -> B -> ... -> B), not from Activity Y
NavUtils.navigateUpFromSameTask(this);
return true;
} else {
Intent intent;
if (TYPE_B.equals(mParent.getType())) {
intent = new Intent(this, ActivityB.class);
} else {
intent = new Intent(this, ActivityA.class);
}
intent.putExtra(EXTRA_ITEM, mParent)
startActivity(intent);
return true;
}
default:
return super.onOptionsItemSelected(item);
}
}
EDIT
If anybody down voting me because they think the solution is as simple as singleTop to Activity A, that's mean you guys don't understand my problem. See the last sentence of my first paragraph. I don't have any problem with going up from Activity B to Activity A, no matter how deep it is if Activity B is opened from Activity A.
Without the above onOptionsItemSelected, I can go up to Activity A from Activity B if Activity B is opened from Activity A directly (A -> B) or indirectly (A -> B -> ... -> B). This is not my problem.
The problem comes when I opened Activity B from Activity Y. I can't just go back to Activity A because Activity A rely on information from Activity X. If I open Activity B from Activity Y, then I go up, I should go to the parent of Activity B, where it can be A or another B.
The above code without canGoUpToActivityA() part will solve my problem. With that code, when I open Activity B from Activity Y, then going up should always go to the parent of B. That's already correct (B only know its parent, which can be not A).
But, when I open Activity B from Activity A, then I go up, it will launch the parent of Activity B. If I open Activity B from Activity A directly (A -> B), it's is indeed what I want. But, when I open Activity B from another Activity B (A-> B -> ... -> B), that's the problem. Because I should go up to Activity A, not the parent of Activity B which can be another Activity B.
I'm still not clear on what exactly you want, so I'll break this up into snippets:
If you want to launch a parent of Activity B and ensure that it isn't "launching itself", you can use the singleTop flag as noted by others to ensure that only one instance of B is ever on the top. You mentioned you don't have an issue with that, but you've listed that as an example, so just keep that in mind.
You've also mentioned that you check if the parent is A before launching Activity A. If all you want to do is actually launch the previous activity in the stack, there is no need for an Intent. Just finish your activity and it will exit the stack, showing the previous activity.
If you have a set activity that you should launch when going back, you can simply launch it with an intent. At this point, it doesn't matter what the previous activity is, because you are explicitly starting an activity for a given class. Again, you may look at the FLAG_ACTIVITY_REORDER_TO_FRONT flag to ensure that an existing activity is not recreated.
Lastly, if you have some complex logic that requires you to know the full stack history, you can always pass data through your bundle. A simple example is an arraylist of the simple class name so you know everything in the current task.
Edit: Since you've also added that you depend on information from previous classes, I take it that you need more than just the class name.
Note that you can always pass data through the bundles so that they are retained through all subsequent activities. You can also pass all the current data by calling putExtras.
And if your entire flow is geared towards going to a child and then passing data back to a parent, consider using [startActivityForResult](https://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int)). That way, you can set result codes and data and finish your activity to directly pass information to the previous activity, without launching a new intent in a forward flow manner.

getIntent().getStringExtra() eventually returning null

So in Activity A I pass some data to activity B through an intent. Ok, everything is fine and getStringExtra returns what I expect. Then from activity B, I pass the same data to activity C. Then, when I hit the back button in the toolbar (because of getSupportActionBar().setDisplayHomeAsUpEnabled(true)), the getStringExtra in activity B is now null.
So the flow is A (passes a string)-> B(passes the same string) -> C (back button in toolbar) -> B and now the variable passes from A to B is null. How can I fix that?
Mark this a rule : Whenever you are using the toolbar back button, you should take care of specifying the launchMode of the parent activity.
In your case, what happens after you press back button in Activity C, depends on what launchMode have you specified for your activity B.
If you have't specified any launch mode, the default launch mode is standard. In this case, the parent activity (B) is popped off the stack, and a new instance of that activity is created on top of the stack to receive the intent.
If you have specified launch mode as singleTop, the parent activity is brought to the top of the stack, and receives the intent through its onNewIntent() method. That is, the previous activity is preserved.
Refer http://developer.android.com/training/implementing-navigation/ancestral.html#NavigateUp.
In your case, you want to preserve the variable, therefore you should use singleTop launchMode.
If you are extracting the data in onCreate method of Activity B then when you hit the back button in Activity C, the onCreate method of Activity B is not called again. To get more clarity on the life cycle of an Activity I really suggest you go through this developer.android.com/reference/android/app/Activity.html
Coming to your question when you start Activity B from Activity A, store the string that you sent from A to B in a global variable rather than a local one, and perform check like
public Class B extends Activity {
private String stringData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xml_layout);
if(getIntent().hasExtra(yourKeyName)) {
stringData = getIntent().getStringExtra(yourKeyName);
}
//Do other stuff
}
}
In your activity B, always check before getting the String -
if(getIntent().hasExtra(KEY_NAME))
String s = getIntent().getStringExtra(KEY_NAME);
Reason being that when you press the back button then Activity B is not launched with the same intent that you create in Activity A. Hence the extras are not present.
You should be string the string extra as a global variable and always use a hasExtra check.
I think it is different processes.
The first one, when you start activity B from activity A, you pass
the data from activity A to activity B. It is obvious, so the data
will be present.
The second one, then you start activity C from activity B, because of
activity B is not on the foreground, then your data will be erased.
You can persist your data on activity B by many ways.
One of them you can implement a static global variable.
Second way, you can save your variables on activity B by saving the
data onSaveInstanceState and retrieve from onRestoreInstanceState or
onCreate
Third way you can listen onActivityResult on activity B from activity
C. When you starting activity C, you must use startActivityOnResult
and passing the data that you want to pass back on activity B. After
you finish your activity C, you must return the data that originally
from activity B.
And so on...
But the better way to achieve that is the second way, use bundle onSaveInstanceState and retrieve the bundle data when onCreate.

How to start an activity twice?

I have an acitivty A. From there, you can click in the UI and go to activity B that has, among other things, a ListView. In the activity B I have a menu that takes me to activity C. Inside that activity C, I have another menu that can take me to a NEW activity B whose ListView will be populated depending on what I did in activity C.
There is my problem. I can't get the activity B to restart from activity C. The only thing it does is to go back to the PREVIOUS activity B (like the back button have been pushed in activity C).
To sum it up, I want to be able to have the following stack of activities:
A -> B > C -> B
However, I can't get to the second B. Everytime I try to start a new activity B from C, it simply takes me back to the first activity B and the listview is not filled.
Am I missing something?
Code from activity C to B
Intent i = new Intent(thisContext, B.class);
thisContext.startActivity(i);
PS: thisContext here is the context of the activity C, which I assign in the own constructor of the activity C, like this:
public C() {
this.thisContext = this;
}
u can use startActivityForResult() in activity B.... make C as subactivity...
nice example explained here

Categories

Resources