I have the following Activity sequence in a folder picker part of an app:
Activity A starts B. B then starts instances of itself that stack as the user goes deeper into the folder tree. Whenever an instance of B is completed (folder chosen), it and all the preceding Bs should finish and A should be shown again, with the propagated result. If the user backtracks, the preceding Bs should not finish, since the user should be able to go back and forth between folder subtrees.
I'm trying to accomplish this by using startActivityForResult, both from A to B and then for each new instance of B. When the user finishes an instance B, I use setResult and want to check it in each preceding instance's onActivityResult method. If it's RESULT_OK, I set the result again and finish that instance, until all of them are finished in a cascading sequence and A is shown again with the result.
The sequence works as long as I go A => B(1) => B(2) => B(3) and finish, without backtracking in the B part.
However, if I go A => B(1) => B(2) => back to B(1) => B(3) and finish, onActivityResult is only called in B(1) when going back from B(2) the first time, not when I later go back to it when finishing from B(3). B(1) is simply resumed when going back from B(3) in this sequence. This breaks the cascading finish sequence.
Why is onActivityResult only called once for instance B(1)? Shouldn't it be called each time a child activity sets a result and finishes?
Here's some code:
=== Activity A:
Starting the first B instance:
Intent intent = new Intent(ActivityA.this, ActivityB.class);
startActivityForResult(intent, RequestCodes.ActivityB);
Receiving the final B result:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == RESULT_OK){
...
}
}
=== Activity B:
Starting new child B from itself:
Intent intent = new Intent(ActivityB.this, ActivityB.class);
startActivityForResult(intent, RequestCodes.ActivityB);
Setting the result when choosing a folder:
Intent result = new Intent();
result.putExtra(...);
setResult(Activity.RESULT_OK, result);
finish();
Receiving the child B result in each parent B instance (which is only called the first time in B(1) for some reason):
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == RESULT_OK){
setResult(Activity.RESULT_OK, data);
finish();
}
}
Thanks in advance.
This is a horrible architecture! You shouldn't create multiple instances of B when traversing a tree. Eventually you will run out of memory! You should use a single instance of your Activity and when the user moves up or down in the tree you should just change the data that you display in the Activity. You should have an internal data model that describes the tree and you only need to remember what node in the tree you are currently displaying. If you also want to support the user going back (by using the BACK button), you can create your own stack (not a stack of activities, but just a stack of tree node references) that you can use to determine which node of the tree to go back to from the current node. This is a simple data management problem and your architecture has forced this to become a View management problem.
Related
I have a few activities and can't figure out a way to make it work with the backstack.
Is there a way to do this:
MainActivity ->(intent) subActivity ->(intent)subsubActivity->(back press)subActivity->*(back press)MainActivity
*This is where I am having problems. Since I am coming from my subsubActivity, even thought I used android:noHistory="true" in the manifest, it doesn't go back to main activity.
Thanks for any help!
This is a perfect candidate for using a combination of startActivityForResult and overriding onActivityResult in the calling Activity.
Assuming you have Activity A, which starts Activity B (which we cannot move back to), which starts Activity C:
Activity A will call Activity B the way you are now. Activity B, however, will call startActivityForResult(Intent, int) instead of just startActivity(Intent). This way, when we return from Activity C, we can call finish() on Activity B and return to Activity A, like so:
public class ActivityB extends Activity {
private static final int REQUEST_CODE_ACTIVITY_C = 1001;
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == REQUEST_CODE_ACTIVITY_C) finish(); // If coming back from Activity C, finish()
}
private void openActivityC(){
Intent intent = new Intent(this, ActivityC.class);
startActivityForResult(intent, REQUEST_CODE_ACTIVITY_C);
}
}
Now when you call openActivityC(), you're ensuring that onActivityResult() will be called when returning from Activity C, thus allowing you to end the Activity and return back to Activity A.
You can provide even more specific actions (such as setting/checking the result code (e.g. if it canceled (Activity.RESULT_CANCELED) or successful (Activity.RESULT_OK)) for statuses, to better determine what that calling Activity should do.
Hopefully that helps explain it a bit.
Edit: Also an afterthought, if there's no chance you'll ever want to go back to Activity B, then #Kay 's solution of just calling finish() after firing the Intent for Activity C would be the simplest approach.
If i get what you want You can use startActivities with an array of intents which will be fired one after another activity1->activity2->activity3 -back-activity2 -back-acitivty1
Basically: Activity A calls startActivityForResult, which launches Activity B. At this point, Activity B SHOULD call back to Activity A's onActivityResult method. Instead, Activity B then incorrectly calls back to Activity C's onActivityResult method. Note that Activity B.getCallingActivity() returns Activity A, as expected.
In more detail:
I have three Activities:
EditModuleActivity (Activity A)
EditMotorActivity (Activity B)
ConfigurationActivity (Activity C)
Inside of EditModuleActivity (Activity A), I have this code:
private void launchIntentFromHere(){
int requestCode = 101;
Intent editMotorIntent = new Intent(context, EditMotorActivity.class);
editMotorIntent.putExtra(EditMotorActivity.EDIT_MOTOR_CONTROLLER, item);
editMotorIntent.putExtra("requestCode", requestCode);
setResult(RESULT_OK, editMotorIntent);
startActivityForResult(editMotorIntent, requestCode);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
DbgLog.error("*************************** why is this never getting called?");
}
Inside of EditMotorActivity (Activity B), I have this code:
private void doThisThing(Object item){
// blah blah build intent
DbgLog.error("calling activity: " + getCallingActivity().toString()); // this prints out Activity A, which is what I expect.
returnIntent.putExtra(EDIT_MOTOR_CONTROLLER, item);
setResult(RESULT_OK, returnIntent);
finish();
}
ConfigurationActivity (Activity C) has this in it:
private void launchIntent(Object item){
Intent editMotorIntent = new Intent(context, EditMotorActivity.class);
editMotorIntent.putExtra(EditMotorActivity.EDIT_MOTOR_CONTROLLER, item);
setResult(RESULT_OK, editMotorIntent);
startActivityForResult(editMotorIntent, 1);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// deals properly with the results
}
When I am in EditModuleActivity and I launch EditMotorActivity, ConfigurationActivity.onActivityResult gets called. Instead, When I am in EditModuleActivity and I launch EditMotorActivity, EditModuleActivity.onActivityResult should get called but that's not happening.
Questions
Why is the wrong onActivityResult getting called??
How do I call the right onActivityResult? Is my expectation incorrect? How else does this work?
If the wrong one gets called, how to I call the correct one?
Answers I've seen that didn't help
My code is the same as the accepted answer here: onActivityResult() not being called in activity
I do not have android:launchMode="singleInstance" in my Manifest
I don't know what a "fragment" is but I don't think I have that.
Note that I'm NOT calling two different Activities from one Activity, as in this question: Handling onActivityResult in Android app having more than one activity Instead, I'm calling the SAME activity from two different Activities.
A comment here says "If you call activity B from class A, it will always return codes to the class A That's the same if you call D from C." But I calling Activity B from Activity B and it returns to Activity C. WHY?? WRONG onActivityResult() being called
Like this:
B
/ \
A C
Not this:
X Z
\ /
Y
I was overriding the onStop method like this:
#Override
public void onStop(){
setResult(RESULT_CANCELED, new Intent());
finish();
}
Basically, I didn't understand how the Android Lifecycle worked. I thought onStop() would only be called when an Activity was KILLED, and I thought it wouldn't be killed if it was waiting for a response from another Activity.
So what was happening was this:
Activity C launched Activity A, which launched Activity B. When Activity B started, A.onStop() was called. Then, when Activity B finished, it (correctly) tried to return back to Activity A, but Activity A was already Stopped, so it returned all the way back to Activity C.
OK - most likely you have a reference to the wrong "context" or a static reference to an activity or something weird elsewhere in your code.
To directly answer your question(s):
Your code doesn't explain why - you need to post more code... if possible.
Your expectation is correct. You appear to clearly understand how it startActivityForResult works. YOur references also demonstrate that you know what you are doing. It does not "work" any other way... but that doesn't mean you don't have an error (see "1" above!)
If you are unable to figure out what went wrong, then you can always call the Activity that you want explicitly (startActivity) passing the intent that you want and then processing that intent. You may want to research onNewIntent if you still are getting weird results in rare cases...
If somebody else had a similar problem (when startActivityForResult() returns to wrong activity, not to the one which called it), take a look at this solution
In my android app, I have an activity that displays profile information. Then it opens a new activity to make changes to the activity. After you save changes, it closes the edit activity from the edit activity, then goes back to the profile displaying activity, there I need to restart this activity to refresh the data.
Is there a way I can restart the activity that opened the current activity?
There are three ways you can achieve what you want:
Start the profile activity by calling startActivityForResult() and refresh your data in onActivityResult()
Finish() the activity when start the profile activity and then override onBackPress() in the profile activity to start the previous activity and then call finish().
Override onBackPress() in the profile activity to start the previous activity with the flag Intent.FLAG_ACTIVITY_CLEAR_TOP and refresh your data in onNewIntent()
Use Android Activity Result Pattern that communticates parent and child activities:
In your profile activity (parent):
#Override
public void onClick(View v) {
startActivityForResult(
new Intent(this, NameActivity.class),
1); // 1 to identify caller activity.
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
updateProfile();
}
}
In your editor activity (child):
#Override
public void onClick(View v) {
setResult(
RESULT_OK);
finish();
}
You won't have to "restart" Activity A.
You can call startActivityForResult(Intent, int) to start your new Activity, and then receive extras via the onActivityResult(int, int, Intent) call.
http://developer.android.com/reference/android/app/Activity.html#StartingActivities
Alternatively, look into the onResume() method, which is called every time your screen becomes visible. Assuming you persisted some state in Activity B (e.g. a SharedPreference or a database record), in onResume() you can refresh Activity A.
Activity Display;
Activity Modification;
When you startActivity M in your Activity D,don't D.finish();
then, when you M.finish(),you don't need "restart" Activity D,because it is already there.
You have two choices to "refresh the data":
1.startActivityForResult as they said.I won't explain any more.
2.make a cache.
save data in some static variables or sqlite or even sharedPreference.Then in onResume() of Activity D,get those data and show them.
Try this: If A is calling B and when B ends you need to update A, then in A launch B with startActivityForResult(). When B returns, the method onActivityResult() of A will be called. In this method (of A) you can test, if necessary, that some stored values are changed. At this point you do something like:
finish();
startActivity(intent); //start the activity A
I have an Activity that starts another one for result. The fact is that I need to trigger another Activity for result from this one. Is this possible?
As an example Activity A calls Activity B for result, but Activity B needs to call Activity C for result to get the correct data.
I don't know if when I start Activity C for result, Activity B is going to send its (incomplete) result to the first calling activity (Activity A).
In case that it can be done, how should I handle the results from those activities?
Thanks!
This is fine. As long as Activity B is still running it will not trigger anything back to A until it finishes.
Launching A -> B -> C and then closing C will mean B's onActivityResult will trigger, then closing B will trigger A's onActivityResult.
So in Activity B you will need some logic like:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ACTIVITY_C && resultCode == RESULT_XXX) {
// Get the result from C, etc. Whatever you need to do
String stuff = data.getStringExtra("XXX");
Intent output = new Intent();
output.putExtra(XXX, stuff);
setResult(RESULT_XXX, output);
finish();
}
}
Then you can catch that back in Activity A.
Another option: if you find yourself needing to share data across a lot of activities and the logic seems to be getting complicated, you might like to consider using the Application class to store global state that you can access from all activities.
I have created custom button that have two states: create_state and login_state.
In create_state, if pressed, it should switch to another activity where user can fill profile creation form, then go back to main activity, and switch to state login_state, so pressing button logon user.
I have done that in my ProfileButton class. And now I see that there are some problems.
For example when I use:
Intent goToNextActivity = new Intent(this.getContext(), NewProfileActivity.class);
Activity a = (Activity)this.getContext();
int requestCode = 0;
a.startActivityForResult(goToNextActivity, requestCode);
my void onActivityResult(int requestCode, int resultCode, Intent data) {..} declared in ProfileButton class does not start after second activity finish().
So better way is to manage this in parent Activity of that button?
All click handlers should be in Activity?
From my understanding, I guess you have to manually set the result that you need to return before finishing the activity.
use setResult() method in NewProfileActivity just before finish() method is called