I have 2 Activities: DetailsBuildingActivity and EditBuildingActivity. DetailsActivity has a button to go to the EditBuildingActivity, to edit properties of the building (like name, size, etc). After editing and saving, you the DetailsBuildingActivity opens up with the new adjusted data. Doing this a few times back and forth in 1 go, gives me the following problem:
When doing 5 times in a row, but then I press the backbutton, I have to press it 10 times before I can get to my previous Activity. (10 times - 5 times DetailsBuildingActivity and 5 times EditBuildingActivity). Is there a way to solve this? I constantly make an intent to open each other op.
You need to use the activity stack.
When you save in the EditBuildingActivity you need to call finish() and not create a new intent.
If you would like DetailsBuildingActivity to know about EditBuildingActivities status, you can use
startActivityForResult(intent, requestCode) to start the activity.
setResult() && finish() to complete the launched activity
and
--- ANDROID DOCS
protected void onActivityResult (int requestCode, int resultCode, Intent data)
Since: API Level 1 Called when an activity you launched exits, giving you the requestCode you started it with, the resultCode it returned, and any additional data from it. The resultCode will be RESULT_CANCELED if the activity explicitly returned that, didn't return any result, or crashed during its operation. You will receive this call immediately before onResume() when your activity is re-starting.
Remember that activities can have multiple instances, and every time you start a new one with a intent they are typically added to the top of your activity stack.
Related
I have an activity where you can click on a button that sends you to an activity where you can add pictures (using startActivityForResult). You can add a couple of pictures. Every picture you add, gets converted to a byte[] array, and gets stored as an extra in the result intent. When the second activity finishes, if i added one picture, and finish the activity, everything is fine, it returns to the previous activity with the button. If i add two or more pictures, and finish the second activity, the previous activity doesn't even get called. It takes me directly to an activity before the activity with the button, but first i see like a black screen that seems like the previous activity crashed, though no errors can be found in the logs. The onActivtiyResult in the previous activty does not get called as well. Any reason why this might be happening ?
Code sending intent to photo adding activity :
Intent addEditPicturesIntent = new Intent(getApplicationContext()
,AddOrEditPicturesOnProductActivity.class);
startActivityForResult(addEditPicturesIntent,33);
Code finishing the photo adding activity:
Intent returnIntent=new Intent();
if (hashMap.containsKey("one")){
Log.d(TAG,"Submit Button clicked and now putting extra in intent-->1");
returnIntent.putExtra("1",hashMap.get("one"));
}
if (hashMap.containsKey("two")){
Log.d(TAG,"Submit Button clicked and now putting extra in intent-->2");
returnIntent.putExtra("2",hashMap.get("two"));
}
if (hashMap.containsKey("three")){
Log.d(TAG,"Submit Button clicked and now putting extra in intent-->3");
returnIntent.putExtra("3",hashMap.get("three"));
}
setResult(Activity.RESULT_OK, returnIntent);
finish();
onActivityResult code in the activity with the button:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG,"onAcitivtyResult"); // <--- NOT EVEN THIS GETS CALLED when returning
if (//uninmportant stuff){
//Other uninporant stuff
else if(requestCode == 33){
Log.d(TAG,"[Test Return Code returned]!");
Log.d(TAG,data.getExtras().toString());
Bundle bundle=data.getExtras();
List<ProductImage> productImageList=new ArrayList<>();
for (Integer i=1;i<=3;i++){
if ( bundle.getByteArray(i.toString())!=null){
ProductImage productImage=new ProductImage();
productImage.setImageBytes(bundle.getByteArray(i.toString()));
productImageList.add(productImage);
}
}
addProductViewModel.fillUpMutableLiveData(productImageList);
addProductViewModel.updateCurrentId(0);
}
}
Every picture you add, gets converted to a byte[] array, and gets stored as an extra in the result intent.
That is a really bad idea.
though no errors can be found in the logs
You are probably getting "FAILED BINDER TRANSACTION" warnings.
Any reason why this might be happening ?
My guess is that you are attempting to pass too much data via IPC. The Intent that you supply to setResult() is passed from your app to a core OS process, then back to your app and the caller of startActivityForResult(). At best, you can pass 1MB in an IPC transaction, and depending on what else is going on at the time, the limit may be substantially lower.
Either:
Have one activity, not two, by using fragments for the individual bits of UI and using a shared ViewModel to expose the results of one fragment to another; or
Carefully hold onto these images in a shared cache, so you are passing cache keys as extras, not byte[]; or
Find some other architecture that you like that does not involve putting large byte[] values into an Intent for use with startActivity(), startActivityForResult(), or setResult()
This question already has answers here:
Sending data back to the Main Activity in Android
(13 answers)
Closed 8 years ago.
I have 2 activities: first activity whith Button, which calls The second activity (Open File Dialog) by intent. How to return filename from the second activity to first? By intents ? Which way is worth using ? It`s requiredd to use api <=15.
Refer this answer here:
To start another activity, only to get a response from the activity as in your case, you can use: startActivityForResult(Intent intent, int identifier_value);. In your second activity, you can choose to return a result or cancel the result.
Docs states:
The startActivity(Intent) method is used to start a new activity, which will be placed
at the top of the activity stack. It takes a single argument, an Intent, which describes
the activity to be executed.
Sometimes you want to get a result back from an activity when it ends. For example,
you may start an activity that lets the user pick a person in a list of contacts; when it
ends, it returns the person that was selected. To do this, you call the
startActivityForResult(Intent, int) version with a second integer parameter identifying
the call. The result will come back through your onActivityResult(int, int, Intent) method.
When an activity exits, it can call setResult(int) to return data back to its parent.
It must always supply a result code, which can be the standard results RESULT_CANCELED,
RESULT_OK, or any custom values starting at RESULT_FIRST_USER. In addition, it can
optionally return back an Intent containing any additional data it wants. All of this
information appears back on the parent's Activity.onActivityResult(), along with the
integer identifier it originally supplied.
My app starts up at Activity A which contains a ListView. The ListView can have items added to it if the user hits "Add" button and goes to Activity B.
In Activity B they fill out some forms and hit "OK" button which takes them back to Activity A where the new item is added to the ListView.
I have a finish() method after going from B to A -- but NOT the other way around.
So if you hit back three times after adding three items. It will just repeat the ListView (Activity A) over 3 times -- less one item that was added.
What is the best way in doing this? I can't put a finish method on the "Add" Button (going from A to B) because if you are in Activity B, it will close the app instead of taking you back to A -- which I do not want. That is, if the user changes his mind and doesn't want to "Add new item" by hitting "OK" while in B. Is a manual Back button the only answer?
Start Activity B by using startActivityForResult() and finish activity B after filling the form.
EDIT
When you startActivityForResult(), you pass 2 parameters, namely intent and requestcode. After you are finished with the new activity(in your case Activity B) you use a function setResult(RESULT_OK) to signify that the operation in Activity B was successful and then you call finish(). After the call to finish() the Activity B will return to Activity A and will call onActivityResult(int requestCode, int resultCode, Intent data). The parameter requestcode helps in identifying which particular activity/request has returned.
Hope this explanation helps you.
I have the following scenario (note that activity A has launchMode="singleTop"):
Activity A calls startActivityForResult() on activity B;
Activity B calls startActivity() on C, after which setResult(RESULT_OK) and finish(); at this point, onActivityResult() in A is NOT called.
Activity C calls startActivity() on A using FLAG_ACTIVITY_CLEAR_TOP and finish();
This is where my problem occurs. At this point, A's onActivityResult() is called with the right requestCode, but something else than RESULT_OK as the resultCode. I was expecting it to receive RESULT_OK because I have set it in B (which was started for result).
Why am I getting something other than RESULT_OK?
Read this doc:
http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP
If set, and the activity being launched
is already running in the current
task, then instead of launching a new
instance of that activity, all of the
other activities on top of it will be
closed and this Intent will be
delivered to the (now on top) old
activity as a new Intent.
Passing clear top flag will finish all activities in the stack. So what you'll get in onActivityResult is probably a "notification" that the activity for which you want the result was canceled. (aka RESULT_CANCELED = 0x00)
UPDATE
When I use startActivityForResult, after setting the result, I always finish my activity and let the caller come to action.
You are doing something less common: you set the result, finish your activity but you also start another one. I don't know is the expected behavior in this situation. Can't you change the interaction between the activities?
You could try to call finish() first and then start the new activity (do this in activities B and C). Anyway, I also don't know what should happen when you do this. I recommend you the first approach (changing the interaction so you don't return a result and create a new activity at the same time).
Perhaps you could chain two startActivityForResult or your activity B could send a new intent to A instead of returning its result?
I have a main Activity A that can kick off sub-Activities B, C, or D. I start them using startActivityForResult(newIntent, REQUEST_CODE);
The user can choose to navigate between B, C, and D once one is displayed. Activity A controls that navigation. When they choose swap between screens 'A' first calls finishActivity(REQUEST_CODE); on the one displayed and then call startActivityForResult(newIntent, REQUEST_CODE); for the next one.
In my onActivityResult I've got
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
// these logs are just for my debugging
Log.w(this.toString(), "onActivityResult returned to by " + requestCode);
Log.w(this.toString(), "result code = " + resultCode );
// the Activity set this value if it was returning normally
if (RESULT_OK == resultCode)
{
Log.i(this.toString(), "---- the sub-activity returned OK ----");
// do some stuff
}// end if (RESULT_OK == resultCode)
// Else we got here because the user hit the back button or something went
// went wrong (RESULT_CANCELED). Either way we do not want to display this
// blank screen to the user. Take them back to the device connection screen.
else if (RESULT_CANCELED == resultCode)
{
finish();
}
}
For some reason I'm getting back a zero or RESULT_CANCELED everytime even though in my sub-activities B,C,D I only set that result if the user chooses to hit the back button (I display an "are you sure" dialog and they choose to exit. In B,C,D I have the following in onPause. The userExiting is a flag I set from the Exit dialog.
protected void onPause()
{
Log.i(this.toString(), "onPause");
// hack to try to setResult for an activity whose finishActivty was called
if ( !this.exiting )
{
Log.i(this.toString(), "======== RESULT_OK ========");
Intent returnIntent = new Intent();
setResult(RESULT_OK, returnIntent);
finish();
}
displayed = false;
super.onPause();
}
Any ideas?
Edit - to be clear the issue is that after calling finishActivty on the old Activity, since a result was never set, Activity A always thinks it should exit. One weird side items. This doesn't happen until the user navigates two times. In other words B is displayed and they choose to go to C. Works, but I can see in the log that onActivityResult hasn't been called. User selects to go from C to D, boom, I see two calls to onActivtyResult, 'A; exits and D is left displayed and the user is unable to nav away.
Update: Since this has been getting a lot of views I thought I'd post this update. The project evolved such that sometimes a single Activity is displays and sometimes a group are displayed as tabs. When the user packs out of the tab in some cases data needed to be returned to the Activity that kicked off the tabs. Before I added the code below RESULT_CANCELED would always be returned. Also make sure that setResult is called before finish.
if (getParent() == null)
{
setResult(Activity.RESULT_OK, intent);
}
else
{
getParent().setResult(Activity.RESULT_OK, intent);
}
I'm not quite following everything you are doing here, but it doesn't sound like you are navigating between activities properly. I would highly suggest reading through the notepad example thoroughly so that you understand the android way of moving between Activities.
Here is what I understand regarding what you are trying to accomplish: Activity A can navigate to B, C or D. From B, C or D you can navigate to any of the others.
You are somehow trying to using Activity A as a controller, having it finish other Activities. Don't do this. Each Activity should be responsible for finishing itself at the proper point.
If you want to go from having A -> B on the stack to A -> C, then I would do this:
A starts B for result. When user selects something in B asking to go to C you have a few choices. You could have B start C not for result, then finish. This would have the effect of B being removed from the stack when C exits. Or, you could have B finish itself, passing back a note that C should be loaded in the results intent. A could then load C for result.
I'm really not sure why you want this behavior though. As an Android user, if I go from A -> B -> C, I expect hitting back to take me back to B.
As an aside, your "is exiting" hack doesn't make any sense. By the time you get to onPause, the result has been set. You cannot change it here. You need to properly set the result when you are finishing, before onPause.