ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Android 上管理 startActivityForResult

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

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

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

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


P
Peter Mortensen

在您的 FirstActivity 中,使用 startActivityForResult() 方法调用 SecondActivity

例如:

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

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

例如:在 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();

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

@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 中以更好的方式在两个 Activity 之间传递数据,请通过 'A better way to pass data between Activities'


在 setResult(RESULT_CANCELLED, returnIntent); 中放置一个意图的目的是什么?
@ismail 假设在 SecondActivity 中发生了一些异常,在这种情况下,您还需要将结果返回到 FirstActivity,因此您可以在 catch 块中将结果设置为 "RESULT_CANCELLED" 并返回到 FirstActivty 并在FirstActivity's' 'onActivityResult()您可以检查您是否获得了成功或失败的结果。
所以这取决于你,如果你不需要知道取消的原因,你可以使用 setResult(RESULT_CANCELED);没有任何意图
@Lei Leyba No finish() 在调用 startActivityForResult() 后不会被调用。First Activity 将进入暂停状态。
对我来说它不起作用-.-这是我非常讨厌Android的地方-这个系统太不可靠了:-/
P
Peter Mortensen

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

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

requestCode 标识哪个应用程序返回了这些结果。这是您在调用 startActivityForResult() 时定义的。

resultCode 通知您此应用程序是成功、失败还是其他情况

data 包含此应用程序返回的任何信息。这可能为空。


意思是 requestCode 只用在第一个活动中,从不用于第二个活动?如果第二个活动有不同的方法,它会改变,但基于意图附加而不是 requestCode,对吗? 编辑: 是的,stackoverflow.com/questions/5104269/…
V
Van

例子

要在上下文中查看整个过程,这是一个补充答案。有关详细说明,请参阅 my fuller answer

https://i.stack.imgur.com/Tyhf8.png

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

这可以由两个不同的应用 A 和应用 b 完成吗?stackoverflow.com/questions/52975645/…
P
Peter Mortensen

作为 the answer from Nishant 的补充,返回活动结果的最佳方式是:

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

我遇到了问题

new Intent();

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

getIntent();

获取当前意图。


创建一个新的 Intent 感觉有点奇怪,它的存在只是为了保存一个 Bundle 并且没有像操作或组件这样的正常值。但是修改用于启动当前活动的 Intent 也感觉有点奇怪(并且有潜在危险?)。所以我搜索了 Android 本身的源代码,发现他们总是创建一个新的 Intent 来用作结果。例如,github.com/aosp-mirror/platform_frameworks_base/blob/…
您好 spaaaky21,谢谢您的评论。很抱歉,我不太清楚地解释我是如何最终得到这个解决方案的。那是三年前,我只记得我的应用程序因为“新意图”而崩溃,这就是我说“我遇到问题”时的意思。实际上,我只是尝试使用“getIntent”,因为它在当时是有意义的,并且有效!因此,我决定分享我的解决方案。也许不是说“最佳方式”或“正确方式”的最佳选择,但我坚持我的解决方案。它解决了我的问题,显然也解决了其他人的问题。谢谢
哇!效果很好。 getIntent() 似乎是将数据返回到未知活动的完美方式,从那里调用活动。谢谢!
S
Sanjayrajsinh

startActivityForResult:在 Android X 中已弃用

对于 new 方式,我们有 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);
      }

在科特林:

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

优势:

新方法是降低我们从片段或另一个活动调用活动时面临的复杂性轻松请求任何许可并获得回调


在 Android API 29 上等待 -1 (Activity.RESULT_OK) 时,result.resultCode 仍然为 0。
在 Kotlin 中全局初始化它,这样写: private lateinit var startActivityForResult:ActivityResultLauncher
C
Community

对于那些对wrong requestCode in onActivityResult有问题的人

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

如果您想在活动中获得正确的 resultCode,请尝试以下操作:

改变:

startActivityForResult(intent, 1); 致:

getActivity().startActivityForResult(intent, 1);


P
Peter Mortensen

ActivityResultRegistry 是推荐的方法

ComponentActivity 现在提供了一个 ActivityResultRegistry,可让您处理 startActivityForResult()+onActivityResult()requestPermissions()+onRequestPermissionsResult() 流,而无需覆盖 ActivityFragment 中的方法,通过以下方式提高类型安全性ActivityResultContract,并提供用于测试这些流的挂钩。

强烈建议使用 Android 10 Activity 1.2.0-alpha02 和 Fragment 1.3.0-alpha02 中引入的 Activity Result 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 具有以下预建功能

TakeVideo PickContact GetContent GetContents OpenDocument OpenDocuments OpenDocumentTree CreateDocument Dial TakePicture RequestPermission RequestPermissions

一个使用 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() }
}

那么这里发生了什么?让我们稍微分解一下。 takePicture 只是一个返回可空位图的回调 - 它是否为空取决于 onActivityResult 进程是否成功。 prepareCall 然后将此调用注册到 ComponentActivity 上称为 ActivityResultRegistry 的新功能中 - 我们稍后再讨论。 ActivityResultContracts.TakePicture() 是 Google 为我们创建的内置助手之一,最终调用 takePicture 实际上会触发 Intent,其方式与您之前使用 Activity.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)
        }
    }
}

查看 this official documentation 了解更多信息。


抱歉,我没有收到 prepareCall
P
Peter Mortensen

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

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

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


P
Peter Mortensen

我将在简短的回答中发布带有 Android X 的新“方式”(因为在某些情况下您不需要自定义注册表或合同)。如果您想了解更多信息,请参阅:Getting a result from an activity

重要提示:Android X 的向后兼容性实际上存在一个错误,因此您必须在 Gradle 文件中添加 fragment_version否则会出现异常“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"
}

现在你只需要添加你的活动的这个成员变量。这使用预定义的注册表和通用合同。

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

P
Peter Mortensen

首先,您将 startActivityForResult() 与第一个 Activity 中的参数一起使用,如果您想将数据从第二个 Activity 发送到第一个 Activity,则使用 IntentsetResult() 方法传递值并获取该数据在第一个 ActivityonActivityResult() 方法内。


P
Peter Mortensen

这是Android上非常常见的问题

它可以分为三部分

启动活动 B(在活动 A 中发生) 设置请求的数据(在活动 B 中发生) 接收请求的数据(在活动 A 中发生)

开始活动 B

Intent i = new Intent(A.this, B.class);
startActivity(i);

设置请求的数据

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

例如:在活动 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 响应代码设置结果

检索结果

对于那个覆盖 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
    }
}

E
Eldhopj

在科特林

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

在一个

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

在乙

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

S
Sagar

在您的主要活动中

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

            }
    }
}

P
Peter Mortensen

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

    }
}

感谢您的回答,但是您的回答和批准的回答有什么区别?