如何在 Android 上管理 startActivityForResult

How to manage startActivityForResult on Android

提问人:Hesam 提问时间:5/2/2012 最后编辑:Peter MortensenHesam 更新时间:3/2/2022 访问量:907624

问:

在我的活动中,我通过 调用主活动的第二个活动。在我的第二个活动中,有一些方法可以完成此活动(可能没有结果),但是,其中只有一个返回结果。startActivityForResult

例如,从主活动,我调用第二个活动。在这个活动中,我正在检查手机的一些功能,例如它是否有摄像头。如果没有,那么我将关闭此活动。此外,在准备期间或如果出现问题,我将关闭此活动。MediaRecorderMediaPlayer

如果它的设备有摄像头并且录制完成,那么在录制视频后,如果用户单击完成按钮,那么我会将结果(录制视频的地址)发送回主要活动。

如何查看主活动的结果?

android 意图 android-activity startactivityforresult

评论

2赞 Code-Apprentice 8/2/2013
如何从 TabHost 活动返回结果 (startActivityForResult) 的可能重复项?

答:

56赞 Sam 5/2/2012 #1

如何查看主要活动的结果?

您需要覆盖 Activity.onActivityResult(),然后检查其参数:

  • requestCode标识哪个应用返回了这些结果。这是您在调用 时定义的。startActivityForResult()
  • resultCode通知你此应用是成功、失败还是其他
  • data保存此应用程序返回的任何信息。这可能是 .null

评论

0赞 JCarlosR 8/12/2017
这意味着requestCode只在第一个活动中使用,而在第二个活动中从未使用过?如果第二个活动有不同的方法,它会改变,但基于意图附加而不是 requestCode,对吧?编辑:是的,stackoverflow.com/questions/5104269/......
2643赞 Nishant 5/2/2012 #2

从 your 中调用 using 方法。FirstActivitySecondActivitystartActivityForResult()

例如:

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

在 中,设置要返回的数据。如果您不想返回,请不要设置任何内容。SecondActivityFirstActivity

例如:如果要发回数据:SecondActivity

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

如果不想返回数据:

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

现在,在您的类中,为该方法编写以下代码。FirstActivityonActivityResult()

@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

要在 Kotlin 中以更好的方式在两个活动之间传递数据,请参阅在活动之间传递数据的更好方法”。

评论

2赞 Ismail Sahin 10/31/2013
在setResult(RESULT_CANCELED, returnIntent)中RESUT_CANCELLED时放置意图的目的是什么?
4赞 Nishant 10/31/2013
@ismail 假设在某个异常中发生了,在这种情况下,您还需要将结果返回给 ,因此您可以将结果设置为在 catch 块中并返回并返回,您可以检查是否得到了成功或失败的结果。SecondActivityFirstActivity"RESULT_CANCELLED"FirstActivtyFirstActivity's' 'onActivityResult()
13赞 Ismail Sahin 10/31/2013
所以这取决于你,如果你不需要知道取消的原因,你可以只使用 setResult(RESULT_CANCELED);没有任何意图
2赞 Nishant 10/1/2014
@Lei Leyba 调用 startActivityForResult() 后未调用 finish()。First Actvity 将进入暂停状态。
9赞 Martin Pfeffer 3/10/2015
对我来说,它不起作用-.-这就是我非常讨厌 Android 的原因 - 这个系统太不可靠了:-/
10赞 DaviF 3/8/2014 #3

如果要使用活动结果更新用户界面,则不能使用 .执行此操作时,UI 不会使用新值刷新。相反,您可以这样做:this.runOnUiThread(new Runnable() {}

@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;
    }
}

这看起来很傻,但它效果很好。

2赞 Dharmendra Pratap 9/30/2014 #4

首先,您在第一个中使用 with 参数,如果要从第二个发送到第一个,则将值与方法一起使用,并在第一个方法中获取该数据。startActivityForResult()ActivityActivityActivityIntentsetResult()onActivityResult()Activity

49赞 Julian Alberto 5/27/2015 #5

补充 Nishant 的答案,返回活动结果的最佳方式是:

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

我遇到了一个问题

new Intent();

然后我发现正确的方法是使用

getIntent();

以获取当前意图。

评论

1赞 spaaarky21 6/30/2018
创建一个只存在用于保存 a 并且没有像动作或组件那样的正常值的 new 感觉有点奇怪。但是,修改用于启动当前活动的内容也感觉有点奇怪(并且具有潜在的危险性?所以我搜索了 Android 本身的源代码,发现他们总是创建一个新的作为结果使用。例如,github.com/aosp-mirror/platform_frameworks_base/blob/...IntentBundleIntentIntent
0赞 Julian Alberto 6/30/2018
您好spaaarky21,感谢您的评论。很抱歉,我没有那么清楚地解释我是如何最终得到这个解决方案的。那是三年前,我只记得我的应用程序因为“新意图”而崩溃,这就是我说“我有问题”的意思。实际上,我只是尝试了“getIntent”,因为它在当时是有道理的,而且它奏效了!正因为如此,我决定分享我的解决方案。也许不是说“最佳方式”或“正确方式”的最佳选择,但我坚持我的解决方案。它解决了我的问题,也解决了其他人的问题。谢谢
1赞 vss 2/25/2019
哇!效果很好。 似乎是将数据返回到未知活动的完美方式,从调用活动的位置。谢谢!getIntent()
14赞 Tomasz Mularczyk 10/23/2015 #6

对于那些在onActivityResult中遇到错误requestCode的人

如果从 调用 ,则 requestCode 由拥有 Fragment 的 Activity 更改。startActivityForResult()Fragment

如果要在 Activity 中获取正确的 resultCode,请尝试以下操作:

改变:

startActivityForResult(intent, 1);沃克斯

getActivity().startActivityForResult(intent, 1);

55赞 Suragch 12/5/2016 #7

要在上下文中查看整个过程,这里有一个补充答案。有关更多解释,请参阅我更完整的答案

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();
    }
}

评论

0赞 Jerry Abraham 10/25/2018
这可以通过两个不同的应用程序 A 和应用程序 b 来完成吗?stackoverflow.com/questions/52975645/......
3赞 Rohit Singh 12/22/2017 #8

这是Android上非常常见的问题

它可以分解为三部分

  1. 启动活动 B(在活动 A 中发生)
  2. 设置请求的数据(在活动 B 中发生)
  3. 接收请求的数据(在活动 A 中发生)
  1. 开始活动 B
Intent i = new Intent(A.this, B.class);
startActivity(i);
  1. 设置请求的数据

在这一部分中,您将决定是否要在特定事件发生时发回数据。

例如:在活动 B 中,有一个 EditText 和两个按钮 b1、b2。 单击按钮 b1 将数据发送回活动 A。 单击按钮 b2 不会发送任何数据。

发送数据

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

不发送数据

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

用户单击“后退”按钮

默认情况下,结果设置Activity.RESULT_CANCEL响应代码

  1. 检索结果

对于该重写 onActivityResult 方法

@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
    }
}
0赞 Nitesh Dev Kunwar 4/24/2019 #9

您需要覆盖 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{

    }
}

评论

0赞 Hesam 8/18/2020
感谢您的回答,但您的答案和批准的答案有什么区别?
14赞 Darish 4/3/2020 #10

建议使用 ActivityResultRegistry 方法

ComponentActivity现在提供了一个,它允许你处理 + 和 + 流,而无需覆盖 or 中的方法,通过 带来更高的类型安全性,并提供用于测试这些流的钩子。ActivityResultRegistrystartActivityForResult()onActivityResult()requestPermissions()onRequestPermissionsResult()ActivityFragmentActivityResultContract

强烈建议使用 Android 10 Activity 1.2.0-alpha02 和 Fragment 1.3.0-alpha02 中引入的 Activity 结果 API。

将此添加到您的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"

如何使用预构建的合约

这个新的 API 具有以下预构建的功能

  1. 拍摄视频
  2. Pick联系方式
  3. 获取内容
  4. 获取内容
  5. 打开文档
  6. 打开文档
  7. OpenDocumentTree
  8. 创建文档
  9. 拨号
  10. 拍摄照片
  11. 请求权限
  12. 请求权限

使用 takePicture 合约的示例:

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() }
}

那么这是怎么回事呢?让我们稍微分解一下。 只是一个返回可为 null 的 Bitmap 的回调 - 它是否为 null 取决于该过程是否成功。 然后将此调用注册到名为 的新功能中 - 我们稍后会回到这个功能。 是 Google 为我们创建的内置帮助程序之一,最后调用实际上会以与之前相同的方式触发 Intent。takePictureonActivityResultprepareCallComponentActivityActivityResultRegistryActivityResultContracts.TakePicture()takePictureActivity.startActivityForResult(intent, REQUEST_CODE)

如何编写自定义合约

一个简单的协定,它接受一个 Int 作为输入,并返回一个字符串,请求的 Activity 在结果 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)
        }
    }
}

有关更多信息,请查看此官方文档

评论

1赞 Upendra Shah 10/20/2021
对不起,我没有得到prepareCall
0赞 Sagar 8/15/2020 #11

在您的主要活动中

@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);

}

在要显示的第二个活动中

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){

            }
    }
}
4赞 Zhar 12/6/2020 #12

我将在简短的回答中发布Android X的新“方式”(因为在某些情况下,您不需要自定义注册表或合同)。如果需要更多信息,请参阅: 从活动中获取结果

重要提示:Android X的向后兼容性实际上存在一个错误,因此您必须添加Gradle文件。否则,您将收到异常“新结果 API 错误:只能对 requestCode 使用较低的 16 位”。fragment_version

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"
}

现在,您只需为活动添加此成员变量即可。这使用预定义的注册表和通用协定。

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;
                    }
                }
            });

在新的 API 之前,您有:

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);
            }
        });

您可能会注意到,请求代码现在由 Google 框架生成(并保留)。您的代码变为:

 btn.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(MyActivity .this, EditActivity.class);
                    mStartForResult.launch(intent);
                }
            });
44赞 Sanjayrajsinh 2/24/2021 #13

startActivityForResult:在 Android X 中已弃用

对于我们有的新方式.registerForActivityResult

在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);
      }

在 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)
}

优势:

  1. 新的方法是降低复杂性,当我们从片段或另一个活动调用活动时,我们面临着这种复杂性
  2. 轻松请求任何权限并获得回调

评论

0赞 Dr.jacky 4/23/2021
在 Android API 29 上等待 -1 () 时仍为 0。result.resultCodeActivity.RESULT_OK
0赞 asadullah 9/11/2021
要在 Kotlin 中全局初始化它,请这样写:private lateinit var startActivityForResult:ActivityResultLauncher<Intent>
6赞 Eldhopj 3/2/2022 #14

Kotlin

假设 A 和 B 是活动,导航是从 A -> B 我们需要 A < B 的结果

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
            }
        }
    }

B

    // Sending result value back to A
    if (success) {
       setResult(RESULT_OK, Intent().putExtra("VALUE", value))
    } else {
       setResult(RESULT_CANCELED)
    }

评论

0赞 Kuvonchbek Yakubov 9/10/2022
A&B&C活动呢?