Android Java - OnCreate 中的竞争条件与两个观察者并制作列表
Android Java - Race Condition in OnCreate with two Observers and making lists
抱歉,如果这是一个令人费解的问题。正在为大学课程创建应用程序,我 运行 在我的 OnCreate 方法中陷入(似乎是)竞争条件。
TL;DR - 有时我的微调器会填充,我可以从中获取索引。有时在尝试获取特定索引时尚未填充。详细信息和代码如下。
该应用程序是 "course scheduler" 面向大学生的应用程序。
我正在创建一个 Activity 来显示现有课程信息并允许您对其进行编辑。在此 Activity 的 OnCreate 方法中,我正在为课程填充 "Mentors" 的微调器和课程所属的 "Term" 微调器。此信息是从 Room DB 中提取的.
我有一个单独的 activity 用于新课程和编辑课程。对于 "new course" activity,一切正常。我 getAllMentors() 或 getAllTerms() 成功并填充微调器列表。
对于 "Edit Course" Activity,有一个额外的步骤,它似乎给我带来了一些问题。
在编辑课程时,我会传递原始 Activity 的意图以及所有必要的 EXTRAS。这是成功的。
在 EditCourseActivity 的 OnCreate 中,我执行以下操作:
- I get the mentorID from the EXTRA that's passed in from the originating Activity.
- I access my MentorViewModel and call my getAllMentors() method which returns LiveData> of all mentors in the db.
- because it returns LiveData, I use an observer and loop through the LiveData adding the Name of each mentor to a List and the
entire mentor to a List.
- I populate my spinner with the information in List full of mentor names.
- then I do a for loop, looping through List looking for one that has the same id as what I grabbed form the EXTRA in step 1.
- If I find a match in that list, I call a getMentorName() method to snag their name as a string.
- I have a methond getIndex(spinner, string) that will loop through the provided spinner, trying to find a match for the string that's
passed in (mentors name) that I grabbed that should match the ID of
the mentor assigned to the course. This method returns index location
of the matched string in the spinner.
- I set the spinner selection to the index found.
我对学期进行基本相同的处理。
我是一名新开发人员,我不习惯 OnCreate 运行同步代码。
因此,我似乎在填充微调器的导师姓名列表和调用我的 getIndex() 方法之间存在竞争条件。
有时微调器被填充并且 getIndex 正常工作并设置正确的指导者。有时微调器是空的,而我的 getIndex() returns -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 循环 运行?
或者更好的处理方式?
提前致谢。
Room 总是 运行 将代码放在单独的线程上,而不是 Main/UI 线程上。您可以使用
更改该行为
allowMainThreadQueries()
初始化数据库后。这将首先使查询 运行,填充您的列表,然后 运行 您的 for 循环代码。我不推荐这种方法,因为在 UI 线程上进行查询是一种不好的做法。
您有两个选择:
将您的 foor 循环更改为一个函数,并在添加来自观察者的值后调用它:
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 更改为 return 列表并在您自己的视图模型上使用 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);
}
}
}
}
});
抱歉,如果这是一个令人费解的问题。正在为大学课程创建应用程序,我 运行 在我的 OnCreate 方法中陷入(似乎是)竞争条件。
TL;DR - 有时我的微调器会填充,我可以从中获取索引。有时在尝试获取特定索引时尚未填充。详细信息和代码如下。
该应用程序是 "course scheduler" 面向大学生的应用程序。
我正在创建一个 Activity 来显示现有课程信息并允许您对其进行编辑。在此 Activity 的 OnCreate 方法中,我正在为课程填充 "Mentors" 的微调器和课程所属的 "Term" 微调器。此信息是从 Room DB 中提取的.
我有一个单独的 activity 用于新课程和编辑课程。对于 "new course" activity,一切正常。我 getAllMentors() 或 getAllTerms() 成功并填充微调器列表。
对于 "Edit Course" Activity,有一个额外的步骤,它似乎给我带来了一些问题。
在编辑课程时,我会传递原始 Activity 的意图以及所有必要的 EXTRAS。这是成功的。 在 EditCourseActivity 的 OnCreate 中,我执行以下操作:
- I get the mentorID from the EXTRA that's passed in from the originating Activity.
- I access my MentorViewModel and call my getAllMentors() method which returns LiveData> of all mentors in the db.
- because it returns LiveData, I use an observer and loop through the LiveData adding the Name of each mentor to a List and the entire mentor to a List.
- I populate my spinner with the information in List full of mentor names.
- then I do a for loop, looping through List looking for one that has the same id as what I grabbed form the EXTRA in step 1.
- If I find a match in that list, I call a getMentorName() method to snag their name as a string.
- I have a methond getIndex(spinner, string) that will loop through the provided spinner, trying to find a match for the string that's passed in (mentors name) that I grabbed that should match the ID of the mentor assigned to the course. This method returns index location of the matched string in the spinner.
- I set the spinner selection to the index found.
我对学期进行基本相同的处理。
我是一名新开发人员,我不习惯 OnCreate 运行同步代码。
因此,我似乎在填充微调器的导师姓名列表和调用我的 getIndex() 方法之间存在竞争条件。
有时微调器被填充并且 getIndex 正常工作并设置正确的指导者。有时微调器是空的,而我的 getIndex() returns -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 循环 运行?
或者更好的处理方式?
提前致谢。
Room 总是 运行 将代码放在单独的线程上,而不是 Main/UI 线程上。您可以使用
更改该行为allowMainThreadQueries()
初始化数据库后。这将首先使查询 运行,填充您的列表,然后 运行 您的 for 循环代码。我不推荐这种方法,因为在 UI 线程上进行查询是一种不好的做法。
您有两个选择:
将您的 foor 循环更改为一个函数,并在添加来自观察者的值后调用它:
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 更改为 return 列表并在您自己的视图模型上使用 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);
}
}
}
}
});