How to manage startActivityForResult on Android
Asked Answered
Z

14

1106

In my activity, I'm calling a second activity from the main activity by startActivityForResult. In my second activity, there are some methods that finish this activity (maybe without a result), however, just one of them returns a result.

For example, from the main activity, I call a second one. In this activity, I'm checking some features of a handset, such as does it have a camera. If it doesn't have then I'll close this activity. Also, during the preparation of MediaRecorder or MediaPlayer if a problem happens then I'll close this activity.

If its device has a camera and recording is done completely, then after recording a video if a user clicks on the done button then I'll send the result (address of the recorded video) back to the main activity.

How do I check the result from the main activity?

Zoomorphism answered 2/5, 2012 at 3:3 Comment(1)
possible duplicate of How to return a result (startActivityForResult) from a TabHost Activity?Hangeron
L
2645

From your FirstActivity, call the SecondActivity using the startActivityForResult() method.

For example:

int LAUNCH_SECOND_ACTIVITY = 1
Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, LAUNCH_SECOND_ACTIVITY);

In your SecondActivity, set the data which you want to return back to FirstActivity. If you don't want to return back, don't set any.

For example: In SecondActivity if you want to send back data:

Intent returnIntent = new Intent();
returnIntent.putExtra("result",result);
setResult(Activity.RESULT_OK,returnIntent);
finish();

If you don't want to return data:

Intent returnIntent = new Intent();
setResult(Activity.RESULT_CANCELED, returnIntent);
finish();

Now in your FirstActivity class, write the following code for the onActivityResult() method.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == LAUNCH_SECOND_ACTIVITY) {
        if(resultCode == Activity.RESULT_OK){
            String result=data.getStringExtra("result");
        }
        if (resultCode == Activity.RESULT_CANCELED) {
            // Write your code if there's no result
        }
    }
} //onActivityResult

To implement passing data between two activities in a much better way in Kotlin, please go through 'A better way to pass data between Activities'.

Lalalalage answered 2/5, 2012 at 3:36 Comment(17)
What is the purpose of putting an intent when RESUT_CANCELLED in setResult(RESULT_CANCELED, returnIntent);Boice
@ismail Suppose in the SecondActivity some exception occurred, in that case also you need to return the result to the FirstActivity,so you can set the result as "RESULT_CANCELLED" in the catch block and return to FirstActivty and in FirstActivity's' 'onActivityResult() you can check whether you got the success or failure result.Lalalalage
So it's up to you, if you don't need to know the reason for cancelling, you can use just setResult(RESULT_CANCELED); without any intentBoice
can I do this in an AsynkTask as the SecondActivity?,Hazing
@FranciscoCorrales If you have to start SecondActivity from AsyncTask, Yes you can do this.Lalalalage
@Lalalalage How to implement the same thing in fragments instead of activity?Galcha
@Galcha there is no concept of onActivityResult() call back for fragments as fragments basically are the part of a single Activity.If you want to communicate between two fragments you can see this link developer.android.com/training/basics/fragments/…Lalalalage
finish isn't called after startActivityForResult?Outfall
@Lei Leyba No finish() is not called after calling startActivityForResult().First Actvity will move to pause state.Lalalalage
For me it is not working -.- this is what I hate soooo much about Android - this system is so unreliable :-/Caelum
What happens if we have three or more actvities? I have a problem with it now. I need to return to first activity from third through second, but I loss data in onActivityResult(...) in first activity after this and resultCode = CANCELED...Holtorf
You can use setResult(RESULT_OK); for example directly before calling finish(); without creating a new intent in newer versions of android.Aliquot
Works perfectly for some simple use cases.Fleshings
How come setResult method is not mentioned in the guide developer.android.com/training/basics/intents/result? I'm curious to know how you've learned it.Revolute
can a third activity return intent?Outfall
What is the second parameter of startActivityForResult() that you specified as 1?Tong
Details about RequestCode #12246780Lalalalage
B
56

How to check the result from the main activity?

You need to override Activity.onActivityResult() and then check its parameters:

  • requestCode identifies which app returned these results. This is defined by you when you call startActivityForResult().
  • resultCode informs you whether this app succeeded, failed, or something different
  • data holds any information returned by this app. This may be null.
Benioff answered 2/5, 2012 at 3:7 Comment(1)
It means that the requestCode is only used in the first activity, and it is never used for the 2nd activity? If the 2nd activity has different approaches, it would change, but based in the intent extras and not by the requestCode, right? Edit: Yes, #5104769Volt
T
55

Example

To see the entire process in context, here is a supplemental answer. See my fuller answer for more explanation.

enter image description here

MainActivity.java

public class MainActivity extends AppCompatActivity {

    // Add a different request code for every activity you are starting from here
    private static final int SECOND_ACTIVITY_REQUEST_CODE = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // "Go to Second Activity" button click
    public void onButtonClick(View view) {

        // Start the SecondActivity
        Intent intent = new Intent(this, SecondActivity.class);
        startActivityForResult(intent, SECOND_ACTIVITY_REQUEST_CODE);
    }

    // This method is called when the second activity finishes
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // check that it is the SecondActivity with an OK result
        if (requestCode == SECOND_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) { // Activity.RESULT_OK

                // get String data from Intent
                String returnString = data.getStringExtra("keyName");

                // set text view with string
                TextView textView = (TextView) findViewById(R.id.textView);
                textView.setText(returnString);
            }
        }
    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }

    // "Send text back" button click
    public void onButtonClick(View view) {

        // get the text from the EditText
        EditText editText = (EditText) findViewById(R.id.editText);
        String stringToPassBack = editText.getText().toString();

        // put the String to pass back into an Intent and close this activity
        Intent intent = new Intent();
        intent.putExtra("keyName", stringToPassBack);
        setResult(RESULT_OK, intent);
        finish();
    }
}
Thrombokinase answered 5/12, 2016 at 8:36 Comment(1)
Can this done by two different apps A and app b?#52976145Amphitheater
H
50

Complementing the answer from Nishant, the best way to return the activity result is:

Intent returnIntent = getIntent();
returnIntent.putExtra("result",result);
setResult(RESULT_OK,returnIntent);
finish();

I was having a problem with

new Intent();

Then I found out that the correct way is using

getIntent();

to get the current intent.

Herra answered 26/5, 2015 at 20:40 Comment(3)
It feels a bit odd to create a new Intent that only exists to hold a Bundle and doesn't have the normal values like an action or component. But it also feels a bit odd (and potentially dangerous?) to modify the Intent that was used to launch the current activity. So I searched the source for Android itself and found that they always create a new Intent to use as the result. For example, github.com/aosp-mirror/platform_frameworks_base/blob/…Primus
Hello spaaarky21, thank you for your comment. I'm sorry I wasn't so clear in explaining exactly how I ended up with that solution. It was three years ago, and I can only remember that my app was crashing because of "new Intent", that's what I meant when I said "I was having problem with". Actually I just tryed with "getIntent", because it made sense at the time, and it worked! Because of it I decided to share my solution. Maybe not the best choise of words to say "best way" or "correct way", but I stand by my solution. It's what solved my problem and aparently of other people too. ThanksHerra
Wow! works great. getIntent() seem to be a perfect way to return data to unkown activity, from where the activity has been called. Thanks!Staurolite
F
44

startActivityForResult: Deprecated in Android X

For the new way we have registerForActivityResult.

In Java :

 // You need to create a launcher variable inside onAttach or onCreate or global, i.e, before the activity is displayed
 ActivityResultLauncher<Intent> launchSomeActivity = registerForActivityResult(
     new ActivityResultContracts.StartActivityForResult(),
     new ActivityResultCallback<ActivityResult>() {
              @Override
              public void onActivityResult(ActivityResult result) {
                   if (result.getResultCode() == Activity.RESULT_OK) {
                         Intent data = result.getData();
                         // your operation....
                    }
               }
      });

      public void openYourActivity() {
            Intent intent = new Intent(this, SomeActivity.class);
            launchSomeActivity.launch(intent);
      }

In Kotlin :

var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data: Intent? = result.data
        // your operation...
    }
}

fun openYourActivity() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}

Advantage:

  1. The new way is reduce complexity which we faced when we call the activity from a fragment or from another activity
  2. Easily ask for any permission and get callback
Frisette answered 24/2, 2021 at 6:28 Comment(4)
Still result.resultCode is 0 while waiting for -1 (Activity.RESULT_OK), on Android API 29.Borszcz
to initialize it globally in Kotlin write this way : private lateinit var startActivityForResult:ActivityResultLauncher<Intent>Organic
how do I return the result in SomeActivity class? This is only half of the answer.Desperate
@GoranHoriaMihail we can get result setResult(RESULT_OK, Intent().putExtra("DATA", value))Frisette
C
15

The ActivityResultRegistry is the recommended approach

ComponentActivity now provides an ActivityResultRegistry that lets you handle the startActivityForResult()+onActivityResult() as well as requestPermissions()+onRequestPermissionsResult() flows without overriding methods in your Activity or Fragment, brings increased type safety via ActivityResultContract, and provides hooks for testing these flows.

It is strongly recommended to use the Activity Result APIs introduced in Android 10 Activity 1.2.0-alpha02 and Fragment 1.3.0-alpha02.

Add this to your build.gradle

def activity_version = "1.2.0-beta01"

// Java language implementation
implementation "androidx.activity:activity:$activity_version"
// Kotlin
implementation "androidx.activity:activity-ktx:$activity_version"

How to use the pre-built contract

This new API has the following pre-built functionalities

  1. TakeVideo
  2. PickContact
  3. GetContent
  4. GetContents
  5. OpenDocument
  6. OpenDocuments
  7. OpenDocumentTree
  8. CreateDocument
  9. Dial
  10. TakePicture
  11. RequestPermission
  12. RequestPermissions

An example that uses the takePicture contract:

private val takePicture = prepareCall(ActivityResultContracts.TakePicture()) { bitmap: Bitmap? ->
    // Do something with the Bitmap, if present
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    button.setOnClickListener { takePicture() }
}

So what’s going on here? Let’s break it down slightly. takePicture is just a callback which returns a nullable Bitmap - whether or not it’s null depends on whether or not the onActivityResult process was successful. prepareCall then registers this call into a new feature on ComponentActivity called the ActivityResultRegistry - we’ll come back to this later. ActivityResultContracts.TakePicture() is one of the built-in helpers which Google have created for us, and finally invoking takePicture actually triggers the Intent in the same way that you would previously with Activity.startActivityForResult(intent, REQUEST_CODE).

How to write a custom contract

A simple contract that takes an Int as an input and returns a string that the requested Activity returns in the result Intent.

class MyContract : ActivityResultContract<Int, String>() {

    companion object {
        const val ACTION = "com.myapp.action.MY_ACTION"
        const val INPUT_INT = "input_int"
        const val OUTPUT_STRING = "output_string"
    }

    override fun createIntent(input: Int): Intent {
        return Intent(ACTION)
            .apply { putExtra(INPUT_INT, input) }
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        return when (resultCode) {
            Activity.RESULT_OK -> intent?.getStringExtra(OUTPUT_STRING)
            else -> null
        }
    }
}

class MyActivity : AppCompatActivity() {

    private val myActionCall = prepareCall(MyContract()) { result ->
        Log.i("MyActivity", "Obtained result: $result")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        button.setOnClickListener {
            myActionCall(500)
        }
    }
}

Check this official documentation for more information.

Carsick answered 3/4, 2020 at 10:6 Comment(1)
Sorry but I didn't get prepareCallNicaragua
A
14

For those who have problem with wrong requestCode in onActivityResult

If you are calling startActivityForResult() from your Fragment, the requestCode is changed by the Activity that owns the Fragment.

If you want to get the correct resultCode in your activity try this:

Change:

startActivityForResult(intent, 1); To:

getActivity().startActivityForResult(intent, 1);

Annia answered 23/10, 2015 at 12:58 Comment(0)
R
10

If you want to update the user interface with the activity result, you can't to use this.runOnUiThread(new Runnable() {}. Doing this, the UI won't refresh with the new value. Instead, you can do this:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_CANCELED) {
        return;
    }

    global_lat = data.getDoubleExtra("LATITUDE", 0);
    global_lng = data.getDoubleExtra("LONGITUDE", 0);
    new_latlng = true;
}

@Override
protected void onResume() {
    super.onResume();

    if(new_latlng)
    {
        PhysicalTagProperties.this.setLocation(global_lat, global_lng);
        new_latlng=false;
    }
}

This seems silly, but it works pretty well.

Reachmedown answered 8/3, 2014 at 13:54 Comment(0)
N
8

In Kotlin

Suppose A & B are activities the navigation is from A -> B We need the result back from A <- B

in A

    // calling the Activity B
    resultLauncher.launch(Intent(requireContext(), B::class.java))

    // we get data in here from B
    private var resultLauncher =
    registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        when (result.resultCode) {
            Activity.RESULT_OK -> {
                result.data?.getStringExtra("VALUE")?.let {
                    // data received here
                }
            }
            Activity.RESULT_CANCELED -> {
                // cancel or failure
            }
        }
    }

In B

    // Sending result value back to A
    if (success) {
       setResult(RESULT_OK, Intent().putExtra("VALUE", value))
    } else {
       setResult(RESULT_CANCELED)
    }
Neighbor answered 2/3, 2022 at 12:59 Comment(1)
What about A & B & C activities?Odine
M
4

It is a very common problem on Android

It can be broken down into three pieces

  1. Start Activity B (happens in Activity A)
  2. Set requested data (happens in activity B)
  3. Receive requested data (happens in activity A)
  1. startActivity B
Intent i = new Intent(A.this, B.class);
startActivity(i);
  1. Set requested data

In this part, you decide whether you want to send data back or not when a particular event occurs.

E.g.: In activity B there is an EditText and two buttons b1, b2. Clicking on Button b1 sends data back to activity A. Clicking on Button b2 does not send any data.

Sending data

b1......clickListener
{
    Intent resultIntent = new Intent();
    resultIntent.putExtra("Your_key", "Your_value");
    setResult(RES_CODE_A, resultIntent);
    finish();
}

Not sending data

b2......clickListener
{
   setResult(RES_CODE_B, new Intent());
   finish();
}

The user clicks the back button

By default, the result is set with Activity.RESULT_CANCEL response code

  1. Retrieve result

For that override onActivityResult method

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RES_CODE_A) {

       // b1 was clicked
       String x = data.getStringExtra("RES_CODE_A");

    }
    else if(resultCode == RES_CODE_B){

       // b2 was clicked
    }
    else{
       // The back button was clicked
    }
}
Metaphysic answered 22/12, 2017 at 9:54 Comment(0)
C
4

I will post the new "way" with Android X in a short answer (because in some case you does not need custom registry or contract). If you want more information, see: Getting a result from an activity

Important: there is actually a bug with the backward compatibility of Android X so you have to add fragment_version in your Gradle file. Otherwise you will get an exception "New result API error : Can only use lower 16 bits for requestCode".

dependencies {

    def activity_version = "1.2.0-beta01"
    // Java language implementation
    implementation "androidx.activity:activity:$activity_version"
    // Kotlin
    implementation "androidx.activity:activity-ktx:$activity_version"

    def fragment_version = "1.3.0-beta02"
    // Java language implementation
    implementation "androidx.fragment:fragment:$fragment_version"
    // Kotlin
    implementation "androidx.fragment:fragment-ktx:$fragment_version"
    // Testing Fragments in Isolation
    debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
}

Now you just have to add this member variable of your activity. This use a predefined registry and generic contract.

public class MyActivity extends AppCompatActivity{

   ...

    /**
     * Activity callback API.
     */
    // https://developer.android.com/training/basics/intents/result
    private ActivityResultLauncher<Intent> mStartForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),

            new ActivityResultCallback<ActivityResult>() {

                @Override
                public void onActivityResult(ActivityResult result) {
                    switch (result.getResultCode()) {
                        case Activity.RESULT_OK:
                            Intent intent = result.getData();
                            // Handle the Intent
                            Toast.makeText(MyActivity.this, "Activity returned ok", Toast.LENGTH_SHORT).show();
                            break;
                        case Activity.RESULT_CANCELED:
                            Toast.makeText(MyActivity.this, "Activity canceled", Toast.LENGTH_SHORT).show();
                            break;
                    }
                }
            });

Before new API you had :

btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MyActivity .this, EditActivity.class);
                startActivityForResult(intent, Constants.INTENT_EDIT_REQUEST_CODE);
            }
        });

You may notice that the request code is now generated (and hold) by the Google framework. Your code becomes:

 btn.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(MyActivity .this, EditActivity.class);
                    mStartForResult.launch(intent);
                }
            });
Claussen answered 6/12, 2020 at 11:0 Comment(0)
O
2

First you use startActivityForResult() with parameters in the first Activity and if you want to send data from the second Activity to first Activity then pass the value using Intent with the setResult() method and get that data inside the onActivityResult() method in the first Activity.

Oncoming answered 30/9, 2014 at 12:30 Comment(0)
K
0

You need to override Activity.onActivityResult():

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_CODE_ONE) {

       String a = data.getStringExtra("RESULT_CODE_ONE");

    }
    else if(resultCode == RESULT_CODE_TWO){

       // b was clicked
    }
    else{

    }
}
Kinross answered 24/4, 2019 at 13:38 Comment(1)
Thanks for your answer but what is the difference between your answer and the approved one?Zoomorphism
U
0

In your Main Activity

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViewById(R.id.takeCam).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent=new Intent(getApplicationContext(),TakePhotoActivity.class);
            intent.putExtra("Mode","Take");
            startActivity(intent);
        }
    });
    findViewById(R.id.selectGal).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent=new Intent(getApplicationContext(),TakePhotoActivity.class);
            intent.putExtra("Mode","Gallery");
            startActivity(intent);
        }
    });
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

}

In Second Activity to Display

private static final int CAMERA_REQUEST = 1888;
private ImageView imageView;
private static final int MY_CAMERA_PERMISSION_CODE = 100;
private static final int PICK_PHOTO_FOR_AVATAR = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_take_photo);

    imageView=findViewById(R.id.imageView);

    if(getIntent().getStringExtra("Mode").equals("Gallery"))
    {
        pickImage();
    }
    else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.CAMERA}, MY_CAMERA_PERMISSION_CODE);
            } else {
                Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(cameraIntent, CAMERA_REQUEST);
            }
        }
    }
}
public void pickImage() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType("image/*");
    startActivityForResult(intent, PICK_PHOTO_FOR_AVATAR);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == MY_CAMERA_PERMISSION_CODE)
    {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
        {
            Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(cameraIntent, CAMERA_REQUEST);
        }
        else
        {
            Toast.makeText(this, "Camera Permission Denied..", Toast.LENGTH_LONG).show();
        }
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK) {
        Bitmap photo = (Bitmap) data.getExtras().get("data");
        imageView.setImageBitmap(photo);
    }
        if (requestCode == PICK_PHOTO_FOR_AVATAR && resultCode == Activity.RESULT_OK) {
            if (data == null) {
                Log.d("ABC","No Such Image Selected");
                return;
            }
            try {
                Uri selectedData=data.getData();
                Log.d("ABC","Image Pick-Up");
                imageView.setImageURI(selectedData);
                InputStream inputStream = getApplicationContext().getContentResolver().openInputStream(selectedData);
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                Bitmap bmp=MediaStore.Images.Media.getBitmap(getContentResolver(),selectedData);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch(IOException e){

            }
    }
}
Underdrawers answered 15/8, 2020 at 3:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.