Android Activity数据回调:从startActivityForResult到Activity Result API 1. 项目概述与核心价值在Android应用开发中Activity之间的跳转和数据传递是基础中的基础。我们最常用的工具就是Intent。但很多开发者尤其是刚入行的朋友往往只停留在使用startActivity(Intent)进行简单的页面跳转一旦涉及到需要从目标Activity“带回”一些数据比如用户的选择结果、表单的填写内容就有点手足无措了。这时候带回调方法的Intent也就是通过startActivityForResult()启动Activity并在onActivityResult()中接收返回数据就成了必须掌握的技能。这个模式的核心价值在于它建立了一个清晰、可控的“请求-响应”流程。想象一下你开发一个电商App主页面是商品列表点击某个商品进入详情页用户在详情页可以选择加入购物车或者立即购买。无论用户做了哪个操作详情页关闭后主页面都需要知道这个结果以便更新购物车角标或者直接跳转到订单页。startActivityForResult和onActivityResult这套组合拳就是为了优雅地解决这类场景而生的。今天我就以一个包含两个Activity的完整工程为例手把手带你走一遍从零搭建到数据双向传递的全过程。我们不仅会实现基础的回调传值还会进阶到如何在Activity间传递自定义对象。过程中我会穿插一些我在实际开发中踩过的坑和总结的实用技巧希望能帮你把这块知识吃得透透的。2. 工程搭建与环境准备2.1 创建新项目与Activity首先打开Android Studio选择创建一个新的“Empty Activity”项目。这里有个小建议项目名称和包名尽量取得有意义一些比如我们这个演示项目可以叫ActivityResultDemo包名就用com.example.activityresultdemo。这虽然是个演示但养成好习惯对维护真实项目大有裨益。系统会自动为我们生成一个MainActivity和一个对应的activity_main.xml布局文件。这就是我们的起点也就是“请求方”。接下来我们需要创建作为“响应方”的第二个Activity。正确的方法是在Android Studio左侧的“Project”视图中找到你的应用包名例如com.example.activityresultdemo右键点击它选择New - Activity - Empty Activity。注意千万不要在文件系统中手动新建Java文件和XML文件然后自己拼凑。通过IDE的菜单创建Android Studio会自动完成三件事1. 生成SecondaryActivity.java类文件2. 生成对应的activity_secondary.xml布局文件3.最关键的一步在AndroidManifest.xml中注册这个新的Activity。很多新手会忘记第三步导致应用崩溃错误信息通常是“Unable to find explicit activity class”。在弹出的配置对话框中将Activity命名为SecondaryActivity布局文件名会自动同步为activity_secondary保持默认即可然后点击“Finish”。2.2 清单文件AndroidManifest.xml确认创建完成后我们必须养成一个条件反射检查AndroidManifest.xml。双击打开该文件你应该能看到类似下面的代码?xml version1.0 encodingutf-8? manifest xmlns:androidhttp://schemas.android.com/apk/res/android packagecom.example.activityresultdemo application android:allowBackuptrue android:iconmipmap/ic_launcher android:labelstring/app_name android:roundIconmipmap/ic_launcher_round android:supportsRtltrue android:themestyle/Theme.ActivityResultDemo activity android:name.MainActivity android:exportedtrue intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity activity android:name.SecondaryActivity / /application /manifest请重点确认application标签内是否存在activity android:name.SecondaryActivity /这一行。只要你是通过Android Studio的向导创建的这一行就会自动添加。如果没有请手动补上。这里的.SecondaryActivity是缩写代表com.example.activityresultdemo.SecondaryActivity。3. 核心原理请求码、结果码与Intent在深入代码之前我们必须搞清楚三个核心概念请求码Request Code、结果码Result Code和承载数据的Intent。这是理解整个回调流程的钥匙。请求码Request Code这是一个由“请求方”定义的整型数字。它的核心作用是标识请求的来源。想象一下你的MainActivity可能会跳转到SecondaryActivity去选择一张图片也可能会跳转到ThirdActivity去编辑一段文本。当这两个目标Activity都关闭并回调到MainActivity的onActivityResult方法时你如何区分返回的数据是图片还是文本就是靠这个请求码。它就像你寄出一封信时在信封上写的编号当回信到来时你可以通过这个编号知道这封回信对应的是哪一次寄出。结果码Result Code这是一个由“响应方”设置的整型数字用于表明操作的结果状态。系统预定义了RESULT_OK和RESULT_CANCELED两个常用值。通常用户正常完成操作并返回比如选择了某项内容时我们设置setResult(RESULT_OK, intent)如果用户取消了操作比如按了返回键则设置setResult(RESULT_CANCELED)。你也可以根据业务需要定义自己的结果码例如RESULT_DELETED、RESULT_EDIT等。Intent这是数据传输的载体。启动时请求方通过Intent向响应方传递数据使用putExtra方法。返回时响应方通过一个新的Intent将数据回传给请求方同样使用putExtra这个Intent作为setResult方法的参数。整个流程可以类比为一次函数调用MainActivity调用startActivityForResult(intent, REQUEST_CODE)相当于发起一次“远程函数调用”。SecondaryActivity执行任务。SecondaryActivity调用setResult(RESULT_CODE, resultIntent)并finish()相当于返回一个结果。系统回调MainActivity的onActivityResult(requestCode, resultCode, data)MainActivity在这里处理返回的结果。4. 基础实现启动Activity与接收返回数据4.1 请求方MainActivity的实现首先我们来布置主界面。打开activity_main.xml删除默认的TextView添加一个按钮。?xml version1.0 encodingutf-8? androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto xmlns:toolshttp://schemas.android.com/tools android:layout_widthmatch_parent android:layout_heightmatch_parent tools:context.MainActivity Button android:idid/btn_launch android:layout_widthwrap_content android:layout_heightwrap_content android:text启动SecondaryActivity app:layout_constraintBottom_toBottomOfparent app:layout_constraintEnd_toEndOfparent app:layout_constraintStart_toStartOfparent app:layout_constraintTop_toTopOfparent / /androidx.constraintlayout.widget.ConstraintLayout接下来在MainActivity.java中实现逻辑。这里有几个关键点需要注意。package com.example.activityresultdemo; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { // 1. 定义请求码用于标识这次启动请求 private static final int REQUEST_CODE_SECONDARY 1001; private static final String TAG MainActivity; private Button btnLaunch; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnLaunch findViewById(R.id.btn_launch); btnLaunch.setOnClickListener(new View.OnClickListener() { Override public void onClick(View v) { launchSecondaryActivity(); } }); } private void launchSecondaryActivity() { // 2. 创建显式Intent明确指定要启动的目标Activity Intent intent new Intent(MainActivity.this, SecondaryActivity.class); // 3. 可选向目标Activity传递数据 intent.putExtra(KEY_MESSAGE_FROM_MAIN, 你好这是来自MainActivity的消息); intent.putExtra(KEY_NUMBER_FROM_MAIN, 2024); // 4. 使用startActivityForResult启动并传入请求码 startActivityForResult(intent, REQUEST_CODE_SECONDARY); Log.d(TAG, 已启动SecondaryActivity请求码: REQUEST_CODE_SECONDARY); } // 5. 重写onActivityResult方法接收返回的数据 Override protected void onActivityResult(int requestCode, int resultCode, Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); // 务必先调用父类方法 // 6. 首先检查请求码确认是来自我们关心的那次启动 if (requestCode REQUEST_CODE_SECONDARY) { // 7. 然后根据结果码判断操作是否成功 if (resultCode RESULT_OK) { // 8. 从返回的Intent中提取数据 if (data ! null) { String returnedMessage data.getStringExtra(KEY_RETURN_MESSAGE); int returnedNumber data.getIntExtra(KEY_RETURN_NUMBER, -1); // -1是默认值 Log.d(TAG, 从SecondaryActivity返回成功); Log.d(TAG, 返回的消息: returnedMessage); Log.d(TAG, 返回的数字: returnedNumber); // 这里可以更新UI例如将返回的数据显示在TextView上 } } else if (resultCode RESULT_CANCELED) { Log.d(TAG, 用户在SecondaryActivity中取消了操作。); } else { Log.d(TAG, 收到未知的结果码: resultCode); } } else { // 如果不是我们关心的请求码可以忽略或者交由其他逻辑处理 Log.d(TAG, 收到其他请求码的回调: requestCode); } } }实操心得与避坑指南请求码的选择请求码可以是任意整数但强烈建议使用静态常量static final来定义并赋予一个有明确意义的名字。不要直接使用魔法数字如1或0。通常我们会为每个需要启动的Activity定义一个独立的请求码。如果请求码范围有冲突可能会导致回调处理错乱。务必调用super.onActivityResult在重写onActivityResult时第一行代码必须是super.onActivityResult(requestCode, resultCode, data);。这是因为父类AppCompatActivity或Activity中可能有一些内部逻辑需要处理跳过它可能引发难以察觉的问题。数据判空从data即返回的Intent中获取数据前一定要先判断data ! null。因为setResult方法可以只传结果码不传IntentsetResult(RESULT_CANCELED)此时data为null。获取数据时的默认值使用getIntExtra、getBooleanExtra等方法时第二个参数是默认值。当Intent中不存在该键Key时会返回这个默认值。选择一个合理的默认值如-1对于IDfalse对于布尔值有助于调试。4.2 响应方SecondaryActivity的实现现在我们来构建响应方。打开activity_secondary.xml同样添加一个按钮用于返回。?xml version1.0 encodingutf-8? androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto xmlns:toolshttp://schemas.android.com/tools android:layout_widthmatch_parent android:layout_heightmatch_parent tools:context.SecondaryActivity TextView android:idid/tv_message android:layout_widthwrap_content android:layout_heightwrap_content android:text这是SecondaryActivity android:textSize24sp app:layout_constraintBottom_toBottomOfparent app:layout_constraintEnd_toEndOfparent app:layout_constraintStart_toStartOfparent app:layout_constraintTop_toTopOfparent / Button android:idid/btn_finish_with_result android:layout_widthwrap_content android:layout_heightwrap_content android:text返回数据并结束 app:layout_constraintEnd_toEndOfparent app:layout_constraintStart_toStartOfparent app:layout_constraintTop_toBottomOfid/tv_message / /androidx.constraintlayout.widget.ConstraintLayout在SecondaryActivity.java中我们需要做三件事接收启动时传来的数据、准备返回数据、设置结果并结束自身。package com.example.activityresultdemo; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; public class SecondaryActivity extends AppCompatActivity { private static final String TAG SecondaryActivity; private TextView tvMessage; private Button btnFinish; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_secondary); tvMessage findViewById(R.id.tv_message); btnFinish findViewById(R.id.btn_finish_with_result); // 1. 接收从MainActivity传递过来的数据 Intent receivedIntent getIntent(); if (receivedIntent ! null) { String message receivedIntent.getStringExtra(KEY_MESSAGE_FROM_MAIN); int number receivedIntent.getIntExtra(KEY_NUMBER_FROM_MAIN, 0); String displayText 收到消息: message \n收到数字: number; tvMessage.setText(displayText); Log.d(TAG, 接收到数据 - 消息: message , 数字: number); } // 2. 设置按钮点击事件准备返回数据并结束Activity btnFinish.setOnClickListener(new View.OnClickListener() { Override public void onClick(View v) { returnDataToMain(); } }); } private void returnDataToMain() { // 3. 创建一个新的Intent用于存放返回数据 Intent resultIntent new Intent(); resultIntent.putExtra(KEY_RETURN_MESSAGE, 操作已完成这是来自SecondaryActivity的返回数据); resultIntent.putExtra(KEY_RETURN_NUMBER, 888); // 4. 设置结果码和返回的Intent setResult(RESULT_OK, resultIntent); Log.d(TAG, 已设置返回结果结果码: RESULT_OK); // 5. 结束当前Activity此时系统会将结果回调给MainActivity的onActivityResult finish(); } // 6. 处理返回键物理返回键或ActionBar的向上按钮 Override public void onBackPressed() { // 可以在这里设置用户取消操作的结果 // setResult(RESULT_CANCELED); // 如果希望区分返回键和取消按钮可以设置 // 也可以不设置直接finishMainActivity中会收到RESULT_CANCELED Log.d(TAG, 用户按下了返回键); super.onBackPressed(); // 默认会调用finish() } }关键点解析与注意事项setResult的调用时机setResult()方法可以在任何地方调用但必须在当前Activity调用finish()之前。通常我们在用户执行了某个明确操作如点击“确定”、“提交”按钮时调用。一旦调用了finish()结果就会被固定并发送出去。返回键的处理用户除了点击我们提供的按钮还可能直接按设备的返回键。默认情况下按返回键会调用finish()但不会自动调用setResult()。此时系统会向请求方发送一个结果为RESULT_CANCELED且data为null的回调。如果你希望按返回键也返回一些数据就需要重写onBackPressed()方法在里面先调用setResult再调用super.onBackPressed()或finish()。结果码的灵活性RESULT_OK和RESULT_CANCELED是常用选项。但你完全可以定义自己的结果码比如public static final int RESULT_DELETED 2;。在onActivityResult中根据这个自定义结果码来做不同的处理。Intent复用返回的IntentresultIntent是一个全新的Intent对象与启动时传来的Intent无关。不要尝试去修改getIntent()返回的那个Intent。4.3 运行与调试现在运行你的应用。点击主界面的按钮跳转到第二个界面。在第二个界面点击“返回数据并结束”按钮。观察Android Studio的Logcat窗口你应该能看到类似以下的日志输出D/MainActivity: 已启动SecondaryActivity请求码: 1001 D/SecondaryActivity: 接收到数据 - 消息: 你好这是来自MainActivity的消息, 数字: 2024 D/SecondaryActivity: 已设置返回结果结果码: RESULT_OK D/MainActivity: 从SecondaryActivity返回成功 D/MainActivity: 返回的消息: 操作已完成这是来自SecondaryActivity的返回数据 D/MainActivity: 返回的数字: 888这证明整个回调流程已经成功跑通。你可以尝试在SecondaryActivity按返回键看看MainActivity的日志是否会输出“用户在SecondaryActivity中取消了操作。”5. 进阶应用在Activity间传递自定义对象在实际项目中我们经常需要传递比基本类型String, int更复杂的数据比如一个用户对象、一个订单对象。Android的Intent通过putExtra方法直接支持传递实现了Serializable或Parcelable接口的对象。SerializablevsParcelable如何选SerializableJava标准接口使用简单只需让类实现Serializable接口即可。但它是通过反射来序列化的在Android上性能较差会产生大量临时对象引发频繁GC。ParcelableAndroid专用接口。实现起来稍显繁琐需要手动编写writeToParcel、describeContents方法和CREATOR静态字段。但其性能极高是Android推荐的进程间对象传递方式。对于简单的、不频繁传递的数据用Serializable图个方便没问题。但对于性能要求高的场景或者对象较大、传递频繁时必须使用Parcelable。下面我们以Parcelable为例演示如何传递一个自定义的User对象。5.1 创建Parcelable对象首先创建一个User类。Android Studio可以帮我们快速生成Parcelable的模板代码。package com.example.activityresultdemo; import android.os.Parcel; import android.os.Parcelable; public class User implements Parcelable { private String name; private int age; private String email; // 构造函数 public User(String name, int age, String email) { this.name name; this.age age; this.email email; } // 从Parcel中读取数据的构造函数由CREATOR调用 protected User(Parcel in) { name in.readString(); age in.readInt(); email in.readString(); } // 实现Creator public static final CreatorUser CREATOR new CreatorUser() { Override public User createFromParcel(Parcel in) { return new User(in); } Override public User[] newArray(int size) { return new User[size]; } }; Override public int describeContents() { return 0; // 大多数情况下返回0即可除非含有文件描述符 } // 将对象数据写入Parcel Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); dest.writeString(email); } // Getter和Setter (略) public String getName() { return name; } public void setName(String name) { this.name name; } public int getAge() { return age; } public void setAge(int age) { this.age age; } public String getEmail() { return email; } public void setEmail(String email) { this.email email; } Override public String toString() { return User{name name , age age , email email }; } }生成技巧在Android Studio中你可以先写好类的字段然后使用快捷键Alt InsertWindows/Linux或Cmd NMac选择“Parcelable”IDE会自动为你生成writeToParcel、describeContents和CREATOR的代码非常方便。5.2 改造MainActivity发送对象修改MainActivity中的launchSecondaryActivity方法将User对象放入Intent。private void launchSecondaryActivity() { Intent intent new Intent(MainActivity.this, SecondaryActivity.class); // 创建并传递User对象 User userToSend new User(张三, 25, zhangsanexample.com); // 关键使用 putExtra 传递 Parcelable 对象 intent.putExtra(KEY_USER_OBJECT, userToSend); startActivityForResult(intent, REQUEST_CODE_SECONDARY); Log.d(TAG, 已启动SecondaryActivity并传递User对象: userToSend); }5.3 改造SecondaryActivity接收并修改对象在SecondaryActivity的onCreate中接收这个User对象并允许修改它。// 在onCreate中接收 User receivedUser receivedIntent.getParcelableExtra(KEY_USER_OBJECT); if (receivedUser ! null) { Log.d(TAG, 接收到User对象: receivedUser.toString()); // 可以显示或编辑这个对象... // 例如修改年龄 receivedUser.setAge(receivedUser.getAge() 1); }在returnDataToMain方法中将这个可能修改过的对象传回去。private void returnDataToMain() { Intent resultIntent new Intent(); // 假设我们修改了接收到的User对象或者创建了一个新的 User userToReturn new User(李四已修改, 30, lisi_modifiedexample.com); resultIntent.putExtra(KEY_RETURN_USER, userToReturn); setResult(RESULT_OK, resultIntent); finish(); }5.4 改造MainActivity接收返回的对象最后在MainActivity的onActivityResult中取出返回的User对象。Override protected void onActivityResult(int requestCode, int resultCode, Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode REQUEST_CODE_SECONDARY resultCode RESULT_OK) { if (data ! null) { // 关键使用 getParcelableExtra 取出对象 User returnedUser data.getParcelableExtra(KEY_RETURN_USER); if (returnedUser ! null) { Log.d(TAG, 收到返回的User对象: returnedUser.toString()); // 使用返回的对象更新UI或进行其他业务逻辑 } } } }运行程序你将看到User对象被成功传递和返回并在日志中打印出对象的内容。重要提示传递Parcelable对象时请确保对象的类定义在发送方和接收方的类加载路径中是完全一致的即同一个类。否则在反序列化时会抛出ClassNotFoundException。在模块化开发或跨进程通信时这一点需要特别注意。6. 新APIActivity Result API你可能已经注意到传统的startActivityForResult和onActivityResult方法有一些缺点代码分散启动请求和结果处理逻辑被分割在两个不同的地方一个在点击事件里一个在重写的方法里。请求码管理繁琐需要定义和维护一堆静态常量。生命周期问题如果startActivityForResult之后Activity因为配置变更如屏幕旋转而重建处理起来会比较麻烦。因此Android Jetpack引入了Activity Result API在androidx.activity:activity-ktx和androidx.fragment:fragment-ktx库中。它通过**契约Contract和启动器Launcher**的概念让代码更清晰、更安全。6.1 使用Activity Result API重构首先确保你的app/build.gradle依赖中包含了相关库新项目通常已包含dependencies { def activity_version 1.8.0 // 使用最新稳定版 implementation androidx.activity:activity:$activity_version // 如果使用Kotlin推荐使用 activity-ktx // implementation androidx.activity:activity-ktx:$activity_version }然后我们重构MainActivitypackage com.example.activityresultdemo; import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { private static final String TAG MainActivity_NewAPI; private Button btnLaunch; // 1. 声明一个ActivityResultLauncher private ActivityResultLauncherIntent secondaryActivityLauncher; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnLaunch findViewById(R.id.btn_launch); // 2. 注册一个启动器并定义结果回调 secondaryActivityLauncher registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), // 使用启动Activity的契约 new ActivityResultCallbackActivityResult() { Override public void onActivityResult(ActivityResult result) { // 3. 在这里集中处理所有从SecondaryActivity返回的结果 int resultCode result.getResultCode(); Intent data result.getData(); if (resultCode RESULT_OK data ! null) { String returnedMessage data.getStringExtra(KEY_RETURN_MESSAGE); int returnedNumber data.getIntExtra(KEY_RETURN_NUMBER, -1); Log.d(TAG, [新API]返回成功消息: returnedMessage , 数字: returnedNumber); } else if (resultCode RESULT_CANCELED) { Log.d(TAG, [新API]用户取消了操作。); } // 处理Parcelable对象的方式完全相同 // User user data.getParcelableExtra(KEY_RETURN_USER); } }); btnLaunch.setOnClickListener(new View.OnClickListener() { Override public void onClick(View v) { launchSecondaryActivityNew(); } }); } private void launchSecondaryActivityNew() { Intent intent new Intent(MainActivity.this, SecondaryActivity.class); intent.putExtra(KEY_MESSAGE_FROM_MAIN, 使用新API启动); // 4. 使用launcher启动Activity不再需要请求码 secondaryActivityLauncher.launch(intent); Log.d(TAG, [新API]已启动SecondaryActivity); } }SecondaryActivity的代码完全不需要修改它仍然使用setResult和finish。6.2 新API的优势与选择优势类型安全可以为不同的启动契约定义不同的ActivityResultLauncher类型减少错误。代码聚合启动请求和结果处理逻辑可以写在相邻的代码块中更易读。生命周期感知ActivityResultLauncher自动与生命周期组件绑定避免了因Activity重建导致回调丢失的问题。无需请求码每个Launcher实例独立管理自己的回调彻底告别了请求码的管理。如何选择新项目强烈推荐直接使用Activity Result API。老项目维护如果已有大量基于旧API的代码可以逐步迁移或在新增功能时使用新API。简单场景如果只是一个非常简单的跳转旧API写起来更短。但考虑到可维护性和现代性还是建议用新API。7. 常见问题排查与实战技巧在实际开发中你可能会遇到一些“坑”。下面我总结了一些最常见的问题和解决方法。7.1 问题排查清单问题现象可能原因解决方案onActivityResult不执行或立即执行1. 启动Activity时用了startActivity()而不是startActivityForResult()或新API的launcher.launch()。2. 在SecondaryActivity中没调用setResult()就直接finish()了。3. 请求的Activity启动模式Launch Mode为singleTask或singleInstance。1. 检查启动代码。2. 确保在返回前调用了setResult。3. 避免对需要返回结果的Activity使用singleTask或singleInstance改用standard或singleTop。onActivityResult中resultCode总是0在SecondaryActivity中setResult只调用了setResult(RESULT_OK)或setResult(RESULT_CANCELED)但没传Intent。RESULT_OK-1RESULT_CANCELED0。如果没调用setResult默认就是0。确认在SecondaryActivity中正确调用了setResult(int resultCode, Intent data)。接收到的data(Intent) 为null1.SecondaryActivity调用setResult(RESULT_OK)时没传Intent参数。2. 用户按返回键退出此时结果码为RESULT_CANCELED且data为null。1. 检查setResult调用。2. 在onActivityResult中先判断resultCode再处理data。传递自定义对象时报错ClassNotFoundException或BadParcelableException1. 传递的对象没有实现Serializable或Parcelable。2. 实现了Parcelable但CREATOR字段写错了不是静态的或名字不对。3.跨模块/进程时类的全路径名不一致。1. 确保类正确实现接口。2. 仔细核对Parcelable实现尤其是CREATOR。3. 确保发送和接收端是完全相同的类。屏幕旋转后onActivityResult在错误的地方触发旧API中请求码是存储在Activity实例中的。旋转导致Activity重建新的实例可能没有正确的请求码映射。使用新的Activity Result API它能很好地处理生命周期问题。如果必须用旧API需要覆写onSaveInstanceState保存请求码并在onCreate中恢复。7.2 实战技巧与心得请求码管理策略即使使用新API在大型项目中为不同的启动意图起一个清晰的名字仍然很重要。你可以创建一个常量类来集中管理这些“契约名称”。public class ActivityContracts { public static final String KEY_PICK_IMAGE pick_image; public static final String KEY_EDIT_TEXT edit_text; // ... 为每个Launcher定义一个唯一的key虽然不是请求码但有助于理解 }数据键名管理和请求码一样用于putExtra和getExtra的键名也容易写错。建议也使用常量来定义。public class IntentKeys { public static final String EXTRA_USER extra_user; public static final String EXTRA_MESSAGE extra_message; } // 使用时 intent.putExtra(IntentKeys.EXTRA_USER, user);处理多个返回源如果一个Activity可能被多个不同的地方启动并且需要返回不同的数据可以在启动的Intent中放入一个“来源标识”。在SecondaryActivity中根据这个标识决定如何组织返回的数据。onActivityResult的早期调用在极少数情况下onActivityResult可能会在onCreate或onStart之前被调用。如果你的结果处理逻辑依赖于某些在onCreate中初始化的组件就需要将结果先暂存起来等组件初始化完成后再处理。新API的Launcher回调发生在onCreate之后通常没有这个问题。对于Fragment在Fragment中也可以使用startActivityForResult但它的生命周期更复杂。最佳实践是始终使用Activity Result API并通过registerForActivityResult在Fragment中注册启动器这样能获得最稳定可靠的行为。掌握了带回调的Intent使用Activity间的数据通信对你来说就不再是障碍。从基础的回调传值到复杂的对象传递再到现代化的Activity Result API这条学习路径清晰地展示了Android开发中一个核心模式的演进。我个人的体会是在新项目中毫不犹豫地拥抱Activity Result API它带来的代码清晰度和可维护性提升是巨大的。而对于那些传递复杂数据的场景多花点时间把Parcelable写对绝对是值得的这能从根本上避免许多诡异的运行时错误。最后记住那个检查清单当回调不起作用时按照顺序排查问题总能迎刃而解。