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

Android RxJava竞态条件问题排查及解决方案咨询

问题判断与解决方案

你的判断完全正确:isWordRedundant方法始终返回false的核心原因,就是异步操作的执行时序问题。mWordDao.wordExists()通过subscribeOn(Schedulers.computation())在后台线程执行查询,但主线程不会等待异步操作完成,会直接返回mWordRedundant的初始值false——等异步查询完成并给mWordRedundant赋值时,方法早就执行完毕了。

下面是具体的修复方案:


1. 重构Repository层:暴露异步流而非同步返回

把isWordRedundant改为返回Single<Boolean>,直接将RxJava的异步流暴露给上层,同时调整线程池(数据库操作属于IO密集型,用Schedulers.io()更合理):

class WordRepository {

    private WordDao mWordDao;
    private final CompositeDisposable mCompositeDisposable = new CompositeDisposable();

    WordRepository(Application application) {
        WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
        mWordDao = db.wordDao();
    }

    void insert(Word word) {
        WordRoomDatabase.databaseWriteExecutor.execute(() -> {
            mWordDao.insert(word);
        });
    }

    // 修改为返回Single<Boolean>,直接传递异步结果
    public Single<Boolean> isWordRedundant(String word) {
        return mWordDao.wordExists(word)
                .subscribeOn(Schedulers.io())
                .map(integer -> integer == 1); // 将查询结果转换为布尔值
    }
}

2. 调整ViewModel层:透传异步流

ViewModel不需要做额外逻辑,直接把Repository的异步流透传给View层:

public class WordViewModel extends AndroidViewModel {

    private WordRepository mRepository;

    public WordViewModel(Application application) {
        super(application);
        mRepository = new WordRepository(application);
    }

    public Single<Boolean> isWordRedundant(String word) {
        return mRepository.isWordRedundant(word);
    }

    public void insert(Word word) {
        mRepository.insert(word);
    }
}

3. 修改Activity层:异步处理查询结果

在按钮点击事件中,订阅异步流,在回调中处理UI逻辑(注意切换回主线程,同时管理订阅生命周期避免内存泄漏):

public class NewWordActivity extends AppCompatActivity {

    public static final String EXTRA_REPLY = "com.example.android.wordlistsql.REPLY";

    private EditText mEditWordView;
    private WordViewModel mWordViewModel;
    private final CompositeDisposable mDisposable = new CompositeDisposable(); // 管理订阅生命周期

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new_word);
        mWordViewModel = new ViewModelProvider(this).get(WordViewModel.class);
        mEditWordView = findViewById(R.id.edit_word);

        final Button button = findViewById(R.id.button_save);
        button.setOnClickListener(view -> {
            Intent replyIntent = new Intent();
            String inputWord = mEditWordView.getText().toString().trim();

            if (TextUtils.isEmpty(inputWord)) {
                System.out.println("Word empty");
                setResult(RESULT_CANCELED, replyIntent);
                finish();
                return;
            }

            // 订阅异步流,在回调中处理结果
            mDisposable.add(
                mWordViewModel.isWordRedundant(inputWord)
                    .observeOn(AndroidSchedulers.mainThread()) // 切换回主线程操作UI
                    .subscribe(
                        isRedundant -> {
                            if (isRedundant) {
                                System.out.println("Word redundant");
                                setResult(RESULT_CANCELED, replyIntent);
                            } else {
                                System.out.println("Word acceptable");
                                replyIntent.putExtra(EXTRA_REPLY, inputWord);
                                setResult(RESULT_OK, replyIntent);
                            }
                            finish();
                        },
                        throwable -> {
                            // 处理查询异常
                            System.out.println("Check word failed: " + throwable.getMessage());
                            setResult(RESULT_CANCELED, replyIntent);
                            finish();
                        }
                    )
            );
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mDisposable.clear(); // 清空订阅,避免内存泄漏
    }
}

额外优化建议

如果你的需求只是避免重复插入数据,可以直接给Room的@Insert注解添加冲突策略,无需手动查询:

@Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(Word word);

这样当插入重复数据时,Room会自动忽略操作,适合不需要给用户提示重复的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 07:50:25