Android Java:EditCourseActivity的OnCreate中Observer与Spinner竞态问题
解决EditCourseActivity中Spinner填充与索引设置的竞态条件问题
这个问题是典型的LiveData异步回调和同步代码执行顺序冲突导致的——你的for循环在Observer收到数据库数据之前就已经执行了,此时mentorListMentor还是空集合,自然找不到对应的索引,最终Spinner只能显示默认的第一项。下面给你几个可行的解决思路,按推荐程度排序:
方案1:将索引设置逻辑移入Observer的onChanged回调
既然LiveData的onChanged只有在数据库数据加载完成后才会触发,那把查找索引、设置选中项的逻辑放到这里面,就能确保Spinner数据已经填充完成。同时要注意清空旧数据,避免配置变更(比如旋转屏幕)导致重复添加:
protected void onCreate(Bundle savedInstanceState) { //////////////////////////Handling Mentor spinner menu///////////////////////////////////////////////// int mentorId = courseData.getIntExtra(EXTRA_COURSE_MENTOR_ID, -1); final ArrayAdapter<String> sp_CourseMentorAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, mentorNameList); sp_CourseMentorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); sp_CourseMentor.setAdapter(sp_CourseMentorAdapter); final MentorViewModel mentorViewModel = ViewModelProviders.of(this).get(MentorViewModel.class); mentorViewModel.getAllMentors().observe(this, new Observer<List<Mentor>>() { @Override public void onChanged(@Nullable List<Mentor> mentorList) { if (mentorList != null) { // 先清空旧数据,防止重复添加 mentorNameList.clear(); mentorListMentor.clear(); for (Mentor m : mentorList) { mentorNameList.add(m.getMentor_name()); mentorListMentor.add(m); } sp_CourseMentorAdapter.notifyDataSetChanged(); // 数据已加载完成,执行索引匹配与选中逻辑 if (mentorId != -1) { for(Mentor m: mentorListMentor){ if (m.getMentor_id() == mentorId){ int spinnerSelectionM2 = getIndexM(sp_CourseMentor, m.getMentor_name()); if (spinnerSelectionM2 != -1) { // 避免无效索引 sp_CourseMentor.setSelection(spinnerSelectionM2); } break; // 找到匹配项后直接退出循环,提升效率 } } } } } }); }
方案2:优化索引查找逻辑,一次遍历完成数据填充+位置记录
这个方案可以避免二次遍历集合,直接在填充Spinner数据的时候记录目标Mentor的位置,更高效:
mentorViewModel.getAllMentors().observe(this, new Observer<List<Mentor>>() { @Override public void onChanged(@Nullable List<Mentor> mentorList) { if (mentorList != null) { mentorNameList.clear(); mentorListMentor.clear(); int targetPosition = -1; int mentorId = courseData.getIntExtra(EXTRA_COURSE_MENTOR_ID, -1); // 一次遍历同时完成数据填充和目标位置查找 for (int i = 0; i < mentorList.size(); i++) { Mentor m = mentorList.get(i); mentorNameList.add(m.getMentor_name()); mentorListMentor.add(m); if (m.getMentor_id() == mentorId) { targetPosition = i; // 直接记录当前索引 } } sp_CourseMentorAdapter.notifyDataSetChanged(); if (targetPosition != -1) { sp_CourseMentor.setSelection(targetPosition); } } } });
方案3:直接观察单个Mentor的LiveData(如果支持)
如果你已经在Room Dao中实现了根据ID查询单个Mentor的方法(比如你注释掉的getMentorById),可以直接观察这个单条数据的LiveData,减少不必要的全量数据加载:
int mentorId = courseData.getIntExtra(EXTRA_COURSE_MENTOR_ID, -1); if (mentorId != -1) { // 先保留原有的全量Mentor加载逻辑,确保Spinner有所有选项 mentorViewModel.getAllMentors().observe(this, new Observer<List<Mentor>>() { @Override public void onChanged(@Nullable List<Mentor> mentorList) { if (mentorList != null) { mentorNameList.clear(); mentorListMentor.clear(); for (Mentor m : mentorList) { mentorNameList.add(m.getMentor_name()); mentorListMentor.add(m); } sp_CourseMentorAdapter.notifyDataSetChanged(); } } }); // 单独观察目标Mentor,找到后设置选中项 mentorViewModel.getMentorById(mentorId).observe(this, new Observer<Mentor>() { @Override public void onChanged(@Nullable Mentor targetMentor) { if (targetMentor != null) { int position = getIndexM(sp_CourseMentor, targetMentor.getMentor_name()); if (position != -1) { sp_CourseMentor.setSelection(position); } } } }); }
关键注意点
- 永远不要假设LiveData的回调会在同步代码之前执行,数据库查询是异步操作,LiveData的
observe只是注册回调,不会阻塞主线程。 - 处理配置变更时,Observer会重新触发,所以必须清空旧数据,避免重复添加。
- 要处理
mentorId = -1的情况(比如新建课程场景),避免无效的索引匹配操作。
内容的提问来源于stack exchange,提问作者TylerTwoForks
相关产品推荐
相关产品推荐

