【问题标题】:Android Java - Race Condition in OnCreate with two Observers and making listsAndroid Java - OnCreate 中的竞争条件与两个观察者并制作列表
【发布时间】:2020-02-09 15:44:11
【问题描述】:

抱歉,这是一个令人费解的问题。正在为大学课程创建应用程序,我在 OnCreate 方法中遇到(似乎是)竞争条件。

TL;DR - 有时我的微调器会填充,我可以从中获取索引。有时在尝试获取特定索引时它尚未填充。详细信息和代码如下。

该应用程序是大学生的“课程安排程序”。

我正在创建一个显示现有课程信息并允许您对其进行编辑的活动。在此活动的 OnCreate 方法中,我正在为课程的“导师”填充一个微调器,以及该课程所属的“学期”的微调器。此信息是从 Room DB 中提取的。

我有一个针对新课程和编辑课程的单独活动。对于“新课程”活动,一切正常。我成功地 getAllMentors() 或 getAllTerms() 并填写了微调器列表。

对于“编辑课程”活动,涉及一个额外的步骤,这似乎给我带来了一些问题。

在编辑课程时,我将来自原始 Activity 的意图与所有必要的 EXTRAS 一起传递。这是成功的。 在 EditCourseActivity 的 OnCreate 中,我执行以下操作:

  1. 我从原始活动传入的 EXTRA 中获得了导师 ID。
  2. 我访问我的 MentorViewModel 并调用我的 getAllMentors() 方法,该方法返回数据库中所有导师的 LiveData>。
  3. 因为它返回 LiveData,我使用观察者并循环遍历 LiveData,将每个导师的姓名添加到列表中,然后 整个导师到一个列表。
  4. 我用列表中的信息填充我的微调器,其中包含导师姓名。
  5. 然后我执行一个 for 循环,在 List 中循环查找与我在步骤 1 中从 EXTRA 中抓取的 ID 相同的 ID。
  6. 如果我在该列表中找到匹配项,我将调用 getMentorName() 方法将其名称作为字符串获取。
  7. 我有一个方法 getIndex(spinner, string) 将循环通过提供的微调器,试图找到匹配的字符串 我抓取的传入的(导师姓名)应该与 分配给课程的导师。此方法返回索引位置 微调器中匹配的字符串。
  8. 我将微调器选择设置为找到的索引。

我对学期的处理基本相同。

我是一名新开发人员,我不习惯 OnCreate 同步运行代码。 因此,在填充微调器的导师姓名列表和调用我的 getIndex() 方法之间,我似乎有一个竞争条件。
有时微调器已填充并且 getIndex 正常工作并设置正确的指导者。有时微调器是空的,而我的 getIndex() 返回 -1(它应该在找不到的情况下执行),它用列表中的第一项填充微调器(一旦它被填充)。

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);

        //Mentor test = mentorViewModel.getMentorById(mentorId);

        mentorViewModel.getAllMentors().observe(this, new Observer<List<Mentor>>() {
            @Override
            public void onChanged(@Nullable List<Mentor> mentorList) {
                if (mentorList != null) {
                    for (Mentor m : mentorList) {
                        mentorNameList.add(m.getMentor_name());
                        mentorListMentor.add(m);
                    }
                }
                sp_CourseMentorAdapter.notifyDataSetChanged();
            }
        });

        for(Mentor m: mentorListMentor){
            if (m.getMentor_id()==mentorId){
                String test = m.getMentor_name();
                int spinnerSelectionM2 = getIndexM(sp_CourseMentor, test);
                sp_CourseMentor.setSelection(spinnerSelectionM2);
            }
        }

有没有办法让它们异步运行?以某种方式让观察者首先完成我的 getAllMentors() 并填充微调器,然后运行 ​​for 循环?

或者更好的方法来处理这个问题?

提前致谢。

【问题讨论】:

  • 尝试在 sp_CourseMentorAdapter.notifyDataSetChanged() 之后移动mentorListMentor 上的 for 循环
  • @SuyashChavan - 成功了!谢谢!

标签: java android spinner race-condition oncreate


【解决方案1】:

Room 始终在单独的线程上运行代码,而不是在 Main/UI 线程上。您可以使用

更改该行为

allowMainThreadQueries()

在初始化您的数据库之后。这将使查询首先运行,填充您的列表,然后运行您的 for 循环代码。我不推荐这种方法,因为在 UI 线程上进行查询是一种不好的做法。

你有两个选择:

  • 将你的 foo 循环更改为一个函数,并在添加来自观察者的值后调用它:

    mentorViewModel.getAllMentors().observe(this, new Observer<List<Mentor>>() {
        @Override
        public void onChanged(@Nullable List<Mentor> mentorList) {
            if (mentorList != null) {
                for (Mentor m : mentorList) {
                    mentorNameList.add(m.getMentor_name());
                    mentorListMentor.add(m);
                }
                lookForMentor();
            }
        }
    });
    
    private void lookForMentor() {
       for(Mentor m: mentorListMentor){
        if (m.getMentor_id()==mentorId){
            String test = m.getMentor_name();
            int spinnerSelectionM2 = getIndexM(sp_CourseMentor, test);
            sp_CourseMentor.setSelection(spinnerSelectionM2);
        }
      }
    }
    
  • 将 for 放在观察者内部,更改 Room DAO 以返回 List 并在您自己的视图模型上使用 LiveData:

MentorViewModel.java:

MentorViewModel extends ViewModel {
   private MutableLiveData<List<Mentor>> _mentorsLiveData = new MutableLiveData<List<Mentor>>();
   public LiveData<List<Mentor>> mentorsLiveData = (LiveData) _mentorsLiveData;

 void getAllMentors(){
    //room db query
    _mentorsLiveData.postValue(mentorsList);
 }
}

EditActivity.java:

mentorsViewModel.getAllMentors();
mentorViewModel.mentorsLiveData.observe(this, new Observer<List<Mentor>>() {
        @Override
        public void onChanged(@Nullable List<Mentor> mentorList) {
              mentorsListMentor.addAll(mentorList);
              sp_CourseMentorAdapter.notifyDataSetChanged();
              for(Mentor m: mentorListMentor){
                 if (m.getMentor_id()==mentorId){
                   String test = m.getMentor_name();
                   int spinnerSelectionM2 = getIndexM(sp_CourseMentor, test);
                   sp_CourseMentor.setSelection(spinnerSelectionM2);
                }
             }
            }
        }
    });

【讨论】:

  • 谢谢。我绝对不想做 allowMainThreadQueries() 因为这是你提到的不好的做法。我做了上面提到的 Suyash 并在更新列表后将 for 循环移到了观察者内。你提到的同样的事情,只是没有首先使它成为一个功能。感谢您的详细回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-17
  • 1970-01-01
  • 2020-05-29
  • 1970-01-01
  • 2019-03-20
  • 1970-01-01
相关资源
最近更新 更多