You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Activity来回切换时Intent传值为空触发NPE问题求解

故障根因
  • 核心逻辑错误:底部导航页面切换实现有误。从ProfileActivity点击Home按钮时,代码通过startActivity()新建了全新的ProjectActivity实例,而非回到登录成功后创建的原有ProjectActivity实例。新实例的Intent中没有携带authenticatedUser参数,调用getIntent().getStringExtra()取到的值必然为null,后续依赖该参数的数据库查询全部失效,再次跳转ProfileActivity时传递的参数也是null,直接触发空指针异常。
  • 架构设计缺陷:登录态完全依赖Intent逐层传递,没有全局兜底存储机制,只要出现跳转漏传参数、Activity被系统回收重建的场景,就会丢失用户信息。
  • 代码健壮性不足:数据库操作方法未做入参空值校验,Cursor查询完成后未及时关闭释放资源,查库返回null对象后直接调用其方法,没有做空判断兜底。
  • 语法错误:DaoHelper中getProjectCount方法的rawQuery语句行尾缺少分号,会直接导致编译失败。

可行修复方案

1. 修正导航跳转逻辑,避免重复创建Activity实例

给跨页面跳转的Intent添加启动Flag,复用任务栈中已存在的Activity实例,不要反复创建新实例导致参数丢失。

修正ProjectActivity跳转ProfileActivity的逻辑

case R.id.nav_profile:
    Intent goToProfileActivityIntent = new Intent(ProjectActivity.this, ProfileActivity.class);
    // 添加Flag:栈中存在ProfileActivity实例时直接复用,清空其上的其他页面
    goToProfileActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    goToProfileActivityIntent.putExtra("authenticatedUser", authenticatedUser);
    startActivity(goToProfileActivityIntent);
    overridePendingTransition(0,0);
    return true;

修正ProfileActivity返回ProjectActivity的逻辑

case R.id.nav_home:
    Intent goHomeIntent = new Intent(ProfileActivity.this, ProjectActivity.class);
    // 添加Flag:直接回到栈中已存在的ProjectActivity实例,不会重新走onCreate创建新页面
    goHomeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    startActivity(goHomeIntent);
    overridePendingTransition(0,0);
    finish(); // 销毁当前ProfileActivity,避免返回栈堆积
    return true;

2. 增加全局登录态兜底存储,不单独依赖Intent传参

登录成功后将用户邮箱存入SharedPreferences,所有需要获取登录用户信息的页面优先读Intent参数,取不到时从SharedPreferences读兜底,从根源避免参数丢失。

登录成功后(LoginActivity中校验账号通过后执行)存储登录态

SharedPreferences sp = getSharedPreferences("app_config", MODE_PRIVATE);
sp.edit().putString("authenticatedUser", 登录用户邮箱字符串).apply();

所有需要获取当前用户的页面,统一做参数兜底读取

以ProjectActivity和ProfileActivity中获取用户信息的逻辑为例:

SharedPreferences sp = getSharedPreferences("app_config", MODE_PRIVATE);
// 优先取Intent传递的参数,取不到读sp的兜底值
if (getIntent().getStringExtra("authenticatedUser") != null) {
    authenticatedUser = getIntent().getStringExtra("authenticatedUser");
    // 同步更新sp中存储的值,保证数据一致
    sp.edit().putString("authenticatedUser", authenticatedUser).apply();
} else {
    authenticatedUser = sp.getString("authenticatedUser", null);
}
// 极端场景下还是没拿到用户信息,直接跳回登录页,不执行后续逻辑
if (authenticatedUser == null) {
    startActivity(new Intent(this, LoginActivity.class));
    finish();
    return;
}

3. 补全数据库操作的健壮性逻辑

修正DaoHelper的查询方法,增加入参判空、Cursor资源释放

getUserDetails方法修正后代码:

public User getUserDetails(String theEmailAddress) {
    // 入参为空直接返回,不执行查询
    if (TextUtils.isEmpty(theEmailAddress)) {
        return null;
    }
    SQLiteDatabase db = this.getReadableDatabase();
    User user = null;
    Cursor cursor = null;
    try {
        cursor = db.query(USER_TABLE,
                new String[]{COLUMN_USER_ID, COLUMN_USER_FIRST_NAME, COLUMN_USER_LAST_NAME, COLUMN_USER_EMAIL_ADDRESS, COLUMN_USER_PASSWORD},
                COLUMN_USER_EMAIL_ADDRESS + " = ?",
                new String[]{theEmailAddress},
                null, null, null);
        // 用moveToFirst取第一条匹配数据,比moveToNext逻辑更准确
        if(cursor.moveToFirst()) {
            long userId = cursor.getLong(0);
            String firstName = cursor.getString(1);
            String lastName = cursor.getString(2);
            String emailAddress = cursor.getString(3);
            String password = cursor.getString(4);
            user = new User(userId, firstName, lastName, emailAddress, password);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 用完关闭Cursor,避免内存泄漏
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }
    return user;
}

getProjectCount方法修正语法错误+补全判空逻辑:

public int getProjectCount(long userId) {
    if (userId <= 0) {
        return 0;
    }
    SQLiteDatabase db = this.getReadableDatabase();
    Cursor cursor = null;
    int count = 0;
    try {
        cursor = db.rawQuery("SELECT * FROM " + PROJECT_TABLE + " WHERE " + COLUMN_USER_PROJECT_FK + " = ?", new String[]{String.valueOf(userId)});
        count = cursor.getCount();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }
    return count;
}

查库返回对象使用前做空判断

以ProfileActivity的loadUserDetails方法为例:

private void loadUserDetails() {
    User user = daoHelper.getUserDetails(authenticatedUser);
    if (user == null) {
        Toast.makeText(this, "用户信息获取失败,请重新登录", Toast.LENGTH_SHORT).show();
        startActivity(new Intent(this, LoginActivity.class));
        finish();
        return;
    }
    profileFirstNameEditText.setText(user.getFirstName());
    profileLastNameEditText.setText(user.getLastName());
    profileEmailAddressEditText.setText(user.getEmailAddress());
}

4. 可选优化:配置Activity启动模式

在AndroidManifest.xml中给ProjectActivity、ProfileActivity添加配置android:launchMode="singleTask",保证整个应用任务栈中两个页面各只有一个实例,从系统层面避免反复创建实例导致的参数丢失问题。


内容的提问来源于stack exchange,提问作者ServletException

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 01:45:43