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
相关产品推荐
相关产品推荐

