我有一个字典应用程序,我想将现有的同义词分配给字典中的一个词。为此,我使用的是在单词表和同义词表之间使用 M:N 关系。
实体:
@Entity(tableName = "word_table",
indices = @Index(value = "word", unique = true))
public class Word {
@PrimaryKey(autoGenerate = true)
private long id;
private String word;
@Ignore
public Word(String word) {
this.word = word;
}
public Word(long id, String word) {
this.id = id;
this.word = word;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
}
@Entity(tableName = "synonym_table")
public class Synonym {
@PrimaryKey(autoGenerate = true)
private long sid;
private String synonym;
@Ignore
public Synonym(String synonym) {
this.synonym = synonym;
}
public Synonym(long sid, String synonym) {
this.sid = sid;
this.synonym = synonym;
}
public long getSid() {
return sid;
}
public void setSid(long id) {
this.sid = sid;
}
public String getSynonym() {
return synonym;
}
public void setSynonym(String synonym) {
this.synonym = synonym;
}
}
@Entity(tableName = "word_synonym_join_table",
primaryKeys= {"word_id" , "synonym_id"},
foreignKeys = {@ForeignKey(entity = Word.class, parentColumns = "id", childColumns = "word_id"),
@ForeignKey(entity = Synonym.class, parentColumns = "sid", childColumns = "synonym_id")})
public class WordSynonymJoin {
@ColumnInfo(name = "word_id")
private long wordId;
@ColumnInfo(name = "synonym_id")
private long synonymId;
public WordSynonymJoin(long wordId, long synonymId) {
this.wordId = wordId;
this.synonymId = synonymId;
}
public long getWordId() {
return wordId;
}
public void setWordId(long wordId) {
this.wordId = wordId;
}
public long getSynonymId() {
return synonymId;
}
public void setSynonymId(long synonymId) {
this.synonymId = synonymId;
}
}
为了检索 Word 和相关同义词的数据,我创建了一个名为 WordWithSynonyms 的 POJO。
public class WordWithSynonyms {
@Embedded
public Word word;
@Embedded
public WordSynonymJoin wordSynonymJoin;
}
道如下:
@Dao
public interface WordDao {
@Query("SELECT * FROM word_table")
public LiveData<List<Word>> getAllWords();
@Query("SELECT * FROM word_table WHERE id =:wordId")
public LiveData<List<Word>> getWordById(long wordId);
@Query("SELECT * from word_table WHERE word =:value")
public LiveData<List<Word>> getWordByValue(String value);
@Insert
public long insert(Word word);
@Delete
public void delete(Word word);
@Update
public void update(Word word);
@Query("DELETE FROM word_table")
public void deleteAll();
}
@Dao
public interface SynonymDao {
@Query("SELECT * FROM synonym_table")
public LiveData<List<Synonym>> getAllSynonyms();
@Query("SELECT * FROM synonym_table WHERE synonym =:value")
public LiveData<List<Synonym>> getSynonymByValue(String value);
@Insert
public void insert(Synonym synonym);
@Delete
public void delete(Synonym synonym);
@Query("DELETE FROM synonym_table")
public void deleteAll();
}
@Dao
public interface WordSynonymJoinDao {
@Query("SELECT * FROM word_table INNER JOIN word_synonym_join_table " +
"ON word_table.id = word_synonym_join_table.word_id " +
"WHERE word_synonym_join_table.synonym_id =:synonymId")
public LiveData<List<WordWithSynonyms>> getWordsBySynonym(long synonymId);
@Query("SELECT * FROM synonym_table INNER JOIN word_synonym_join_table " +
"ON synonym_table.sid = word_synonym_join_table.synonym_id " +
"WHERE word_synonym_join_table.word_id =:wordId")
public LiveData<List<SynonymWithWords>> getSynonymsByWord(long wordId);
@Query("SELECT * FROM synonym_table INNER JOIN word_synonym_join_table " +
"ON synonym_table.sid = word_synonym_join_table.synonym_id " +
"WHERE word_synonym_join_table.word_id !=:wordId")
public LiveData<List<SynonymWithWords>> getSynonymsByNotWord(long wordId);
@Insert
public void insert(WordSynonymJoin wordSynonymJoin);
@Delete
public void delete(WordSynonymJoin wordSynonymJoin);
@Query("DELETE FROM word_synonym_join_table")
public void deleteAll();
}
当我到达同义词 Activity 时,我传递 wordId 以通过 ViewModel 观察器检索该词的当前同义词。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_synonym);
Intent intent = getIntent();
wordId = Long.parseLong(intent.getStringExtra(EXTRA_WORD_ID));
//SynonymViewModel synonymViewModel = ViewModelProviders.of(this).get(SynonymViewModel.class);
WordSynonymJoinViewModel wordSynonymJoinViewModel = ViewModelProviders.of(this).get(WordSynonymJoinViewModel.class);
//synonymAdapter = new SynonymListAdapter(this);
synonymAdapter = new SynonymWithWordListAdapter(this);
synonynRecyclerView = findViewById(R.id.recycler_view_syonym);
if (wordId != 0) {
wordSynonymJoinViewModel.getSynonymsByWord(wordId).observe(SynonymActivity.this, new Observer<List<SynonymWithWords>>() {
@Override
public void onChanged(@Nullable List<SynonymWithWords> synonymWithWords) {
synonymAdapter.setSynonyms(synonymWithWords);
synonymAdapter.notifyDataSetChanged();
}
});
}
synonynRecyclerView.setAdapter(synonymAdapter);
synonynRecyclerView.setLayoutManager(new LinearLayoutManager(SynonymActivity.this));
}
然后我让用户有机会将同义词表中现有的、未分配的同义词关联到 Word 表。
我通过 AlertDialog 内的单独 ViewModel 观察器检索未使用和可用的同义词,AlertDialog 使用微调器使用另一个 ViewModel 观察器通过 WordSynonymJoin 表显示它们。 最后,当用户单击 AlertDialog 上的“确定”按钮时,在该 ViewModel 观察器内部,运行第三个 VieModel 观察器以实际插入到 WordSynonymJoin 表中。
case R.id.synonym_assign_synonym:
final WordSynonymJoinViewModel wordSynonymJoinViewModel = ViewModelProviders.of(SynonymActivity.this).get(WordSynonymJoinViewModel.class);
wordSynonymJoinViewModel.getSynonymsByNotWord(wordId).observe(SynonymActivity.this, new Observer<List<SynonymWithWords>>() {
@Override
public void onChanged(@Nullable List<SynonymWithWords> synonymWithWords) {
List<String> synonymsNotAssignList = new ArrayList<>();
for (SynonymWithWords sww : synonymWithWords)
synonymsNotAssignList.add(sww.synonym.getSynonym());
AlertDialog.Builder assignSynonymDialog = new AlertDialog.Builder(SynonymActivity.this);
assignSynonymDialog.setTitle("Select New Category:");
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.alert_dialog_spinner_view, null);
final Spinner synonymSpinner = (Spinner) view.findViewById(R.id.alert_dialog_spinner);
final SynonymViewModel synonymViewModel = ViewModelProviders.of(SynonymActivity.this).get(SynonymViewModel.class);
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter(SynonymActivity.this, android.R.layout.simple_spinner_dropdown_item, synonymsNotAssignList);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
synonymSpinner.setAdapter(spinnerAdapter);
synonymSpinner.setSelection(synonymId);
assignSynonymDialog.setView(view);
assignSynonymDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final String synonymValue = synonymSpinner.getSelectedItem().toString();
// get new synonym id
synonymViewModel.getSynonymByValue(synonymValue).observe(SynonymActivity.this, new Observer<List<Synonym>>() {
@Override
public void onChanged(@Nullable List<Synonym> synonyms) {
long id = 0;
if (!synonyms.get(0).getSynonym().equals(synonymValue)) {
if (synonyms.size() > 1)
Toast.makeText(SynonymActivity.this, "Query found " + synonyms.size() + " which is more than the one expected.", Toast.LENGTH_SHORT).show();
} else {
id = synonyms.get(0).getSid();
}
WordSynonymJoinViewModel wordSynonymJoinViewModel = ViewModelProviders.of(SynonymActivity.this).get(WordSynonymJoinViewModel.class);
wordSynonymJoinViewModel.insert(new WordSynonymJoin(wordId, id));
}
});
}
});
assignSynonymDialog.setNegativeButton("Cancel", null);
assignSynonymDialog.create();
assignSynonymDialog.show();
}
});
return true;
在第一遍中,一切似乎都运行良好。但是,在用户继续向单词添加新同义词的连续传递中,在添加每个同义词后需要多次单击 AlertDialog 的取消按钮才能退出。添加了 2 个同义词, 2 单击取消返回主 Activity 。添加了 3 个同义词,单击 3 次取消以删除 AlertDialog。
我对 MVVM 和 Room 持久性的整个概念非常陌生,所以我知道会有问题。下面是 AlertDialog 的代码,用于将现有的、未分配的同义词添加到当前单词。
我不喜欢为此使用了多少代码,但我无法用词来表达我的搜索,因此我无法找到绕过它的方法。
我的问题是:
为什么每次我输入 associate new synonym to the word 时,代码都会循环 +1?我是不是应该清理一些东西。
这个编码是否正确?
要完成看似微不足道的事情,这似乎是一项艰巨的工作。我想我错过了什么。我把它弄得异常复杂了吗?
这段代码看起来如此繁琐笨拙,我错过了什么?
这似乎是一种检索值的非常麻烦的方法,而且我真的认为我不需要观察上面运行的每个查询。也许我错了。
是否有研究方向可以帮助我更好地理解这一点?
这会是 Rx Java 的用武之地吗?
我当然可以根据需要提供更多代码。
如有任何帮助,我们将不胜感激。
最佳答案
TRDL:不要在 ON_CREATE 之外调用 .observe
状态。
您犯了一个 LiveData
错误...但您并不孤单!这个错误是 StackOverflow 上最常见的 LiveData
错误:在 Activity#onCreate()
之外调用 .observe
。这包括在点击监听器、onResume
、广播接收器等中调用 .observe
我在大多数第一次使用 LiveData
的人身上看到的问题是,他们将 LiveData
视为回调,而实际上并非如此。 LiveData
是一个流。 LiveData
不会只通知一次。附加到 LiveData
的 Observers
将继续收到通知,直到他们取消订阅。此外,它意味着在生命周期开始时订阅(例如 Activity#onCreate 或 Fragment#onViewCreated)并在生命周期结束时取消订阅。 LiveData
自动处理取消订阅部分,因此您只需确保在 onCreate
中订阅即可。
你不断获得 +1 Dialog 的根本原因是之前的观察者没有死,每次重复同样的事情时你都会不断地向数据库添加新的订阅。尝试旋转手机,看看对话框的数量是否重置回 1。这是因为当您旋转屏幕并重新创建 Activity 时,所有之前的观察者都已取消订阅。
也许您可以调用 isShowing()
并查看是否有任何对话框打开,如另一个答案中所建议的那样。但是,这样做只是一种解决方法。如果它是 Toast
或您无法检查的其他内容怎么办?此外,您很幸运能够轻松发现此错误。您可能在其他地方有这个重复的观察者错误,而这些错误在视觉上并不明显。
所以我认为你已经知道如何使用LiveData
,但你只需要知道如何正确地实现 react 模式。用一篇文章来解释太多了,但让我举一个简单的例子:
假设您有一个按钮,当您按下该按钮时,您会从数据库中获取一些数据。在类似回调的设计中,您经常调用 ViewModel 中的某些函数并传递回调实例。例如你有这个:
//ViewModel
void getSynonymsByNotWord(WordSynonymJoin word, Callback callback) { ... }
//Activity
void onClick(View v) {
wordSynonymJoinViewModel.changeCurrentSysnonymsByNotWord(wordId, callback);
}
您对 ViewModel 执行操作并通过回调接收响应。这非常适合回调。但是,您不能对 LiveData
执行相同的操作。使用 LiveData
时,View 层不期望每个操作都会有响应。相反,View 层应该始终盲目地听取响应,甚至在单击按钮之前也是如此。
//ViewModel
private MutableLiveData wordQuery;
private Livedata synonymsByNotWord = Transformations.switchMap(wordQuery, word -> {
return repository.getSynonymsByWord(word);
});
LiveData getCurrentSynonymsByNotWord() {
return synonymsByNotWord;
}
void changeCurrentSynonymsByNotWord(WordSynonymJoin word) {
wordQuery.postValue(word);
}
//Activity
void onCreate() {
wordSynonymJoinViewModel.getCurrentSynonymsByNotWord().observe(...);
}
void onClick(View v) {
wordSynonymJoinViewModel.changeCurrentSynonymsByNotWord(wordId);
}
也可以,但通常不会在每次需要 View 模型时从 ViewModelProviders
获取 ViewModel。您应该只在 onCreate
获取一个 View 模型,将其保存为 Activity 实例变量,并在 Activity 的其余部分使用相同的实例。
关于android - 随着数据库更改,我不断增加对我的可观察对象的重复 onChange 传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57366800/