Android学习笔记
Android 开发者基础知识 (Java) —— Google Developers 培训团队
文章目录
Android学习笔记Android 开发者基础知识 (Java) —— Google Developers 培训团队第3单元 在后台运行第10课 使用Room存储数据93. Room、LiveData 和 ViewModel93.1 创建项目93.2 创建Word实体93.3 创建DAO93.4 使用LiveData93.5 添加Room数据库93.6 创建存储库93.7 创建ViewModel93.8 为UI添加XML布局93.9 创建一个Adapter并添加RecylerView93.10 填充数据库93.11 将UI与数据连接起来93.12 创建一个用于添加单词的Activity93.13 小结第3单元 在后台运行
第10课 使用Room存储数据
93. Room、LiveData 和 ViewModel
项目地址:/google-developer-training/android-fundamentals-apps-v2/tree/master/RoomWordsSample
93.1 创建项目
创建项目更新Gradle配置文件93.2 创建Word实体
表结构
创建Word类
public class Word {private String mWord;public Word(@NonNull String word) {this.mWord = word;}public String getWord(){return this.mWord;}}
注解Word类
@Entity(tableName = "word_table")public class Word {@PrimaryKey@NonNull@ColumnInfo(name = "word")private String mWord;public Word(@NonNull String word) {this.mWord = word;}public String getWord(){return this.mWord;}}
93.3 创建DAO
数据访问对象或Dao
是一个带注释的类,您可以在其中指定 SQL 查询并将它们与方法调用相关联。编译器检查 SQL 是否有错误,然后从注释中生成查询。对于常见查询,库提供了方便的注释,例如@Insert
.
注意:
DAO 必须是一个interface
或abstract
类。Room 使用 DAO 为您的代码创建干净的 API。默认情况下,所有查询 (@Query
) 必须在主线程以外的线程上执行。(您稍后再处理。)对于插入或删除等操作,如果您使用提供的便利注释,Room 会为您处理线程管理。
实现DAO类
@Daopublic interface WordDao {@Insertvoid insert(Word word);@Query("DELETE FROM word_table")void deleteAll();@Query("SELECT * from word_table ORDER BY word ASC")List<Word> getAllWords();}
93.4 使用LiveData
在WordDao中返回LiveData
@Query("SELECT * from word_table ORDER BY word ASC")LiveData<List<Word>> getAllWords();
93.5 添加Room数据库
Room 是 SQLite 数据库之上的数据库层。Room 负责处理您过去使用数据库助手类(例如SQLiteOpenHelper
.
Room 使用 DAO 向其数据库发出查询。默认情况下,为了避免糟糕的 UI 性能,Room 不允许您在主线程上发出数据库查询。LiveData
需要时,通过在后台线程上自动异步运行查询来应用此规则。Room 提供 SQLite 语句的编译时检查。您的Room
类必须是 abstract 和 extendRoomDatabase
。通常,整个应用程序只需要一个 Room 数据库实例。
创建一个Room数据库
@Database(entities = {Word.class}, version = 1, exportSchema = false)public abstract class WordRoomDatabase extends RoomDatabase {public abstract WordDao wordDao();private static WordRoomDatabase INSTANCE;static WordRoomDatabase getDatabase(final Context context) {if (INSTANCE == null) {synchronized (WordRoomDatabase.class) {if (INSTANCE == null) {INSTANCE = Room.databaseBuilder(context.getApplicationContext(),WordRoomDatabase.class, "word_database")// Wipes and rebuilds instead of migrating // if no Migration object.// Migration is not part of this practical..fallbackToDestructiveMigration().build();}}}return INSTANCE;}}
93.6 创建存储库
实施存储库
public class WordRepository {private WordDao mWordDao;private LiveData<List<Word>> mAllWords;WordRepository(Application application) {WordRoomDatabase db = WordRoomDatabase.getDatabase(application);mWordDao = db.wordDao();mAllWords = mWordDao.getAllWords();}LiveData<List<Word>> getAllWords() {return mAllWords;}public void insert (Word word) {new insertAsyncTask(mWordDao).execute(word);}private static class insertAsyncTask extends AsyncTask<Word, Void, Void> {private WordDao mAsyncTaskDao;insertAsyncTask(WordDao dao) {mAsyncTaskDao = dao;}@Overrideprotected Void doInBackground(final Word... params) {mAsyncTaskDao.insert(params[0]);return null;}}}
93.7 创建ViewModel
实现WordViewModel
public class WordViewModel extends AndroidViewModel {private WordRepository mRepository;private LiveData<List<Word>> mAllWords;public WordViewModel (Application application) {super(application);mRepository = new WordRepository(application);mAllWords = mRepository.getAllWords();}LiveData<List<Word>> getAllWords() {return mAllWords; }public void insert(Word word) {mRepository.insert(word); }}
93.8 为UI添加XML布局
添加样式添加布局添加回收站视图修改FAB图标93.9 创建一个Adapter并添加RecylerView
创建 WordListAdapter 类
public class WordListAdapter extends RecyclerView.Adapter<WordListAdapter.WordViewHolder> {private final LayoutInflater mInflater;private List<Word> mWords; // Cached copy of wordsWordListAdapter(Context context) {mInflater = LayoutInflater.from(context); }@Overridepublic WordViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View itemView = mInflater.inflate(R.layout.recyclerview_item, parent, false);return new WordViewHolder(itemView);}@Overridepublic void onBindViewHolder(WordViewHolder holder, int position) {if (mWords != null) {Word current = mWords.get(position);holder.wordItemView.setText(current.getWord());} else {// Covers the case of data not being ready yet.holder.wordItemView.setText("No Word");}}void setWords(List<Word> words){mWords = words;notifyDataSetChanged();}// getItemCount() is called many times, and when it is first called,// mWords has not been updated (means initially, it's null, and we can't return null).@Overridepublic int getItemCount() {if (mWords != null)return mWords.size();else return 0;}class WordViewHolder extends RecyclerView.ViewHolder {private final TextView wordItemView;private WordViewHolder(View itemView) {super(itemView);wordItemView = itemView.findViewById(R.id.textView);}}}
将 RecyclerView 添加到 MainActivity
RecyclerView recyclerView = findViewById(R.id.recyclerview);final WordListAdapter adapter = new WordListAdapter(this);recyclerView.setAdapter(adapter);recyclerView.setLayoutManager(new LinearLayoutManager(this));
93.10 填充数据库
创建用于填充数据库的回调
private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {private final WordDao mDao;String[] words = {"dolphin", "crocodile", "cobra"};PopulateDbAsync(WordRoomDatabase db) {mDao = db.wordDao();}@Overrideprotected Void doInBackground(final Void... params) {// Start the app with a clean database every time.// Not needed if you only populate the database// when it is first createdmDao.deleteAll();for (int i = 0; i <= words.length - 1; i++) {Word word = new Word(words[i]);mDao.insert(word);}return null;}}
93.11 将UI与数据连接起来
显示单词
mWordViewModel.getAllWords().observe(this, new Observer<List<Word>>() {@Overridepublic void onChanged(@Nullable final List<Word> words) {// Update the cached copy of the words in the adapter.adapter.setWords(words);}});
93.12 创建一个用于添加单词的Activity
public class NewWordActivity extends AppCompatActivity {public static final String EXTRA_REPLY = "com.example.android.roomwordssample.REPLY";private EditText mEditWordView;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_new_word);mEditWordView = findViewById(R.id.edit_word);final Button button = findViewById(R.id.button_save);button.setOnClickListener(new View.OnClickListener() {public void onClick(View view) {Intent replyIntent = new Intent();if (TextUtils.isEmpty(mEditWordView.getText())) {setResult(RESULT_CANCELED, replyIntent);} else {String word = mEditWordView.getText().toString();replyIntent.putExtra(EXTRA_REPLY, word);setResult(RESULT_OK, replyIntent);}finish();}});}}
将单词插入数据库的代码
public void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == NEW_WORD_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {Word word = new Word(data.getStringExtra(NewWordActivity.EXTRA_REPLY));mWordViewModel.insert(word);} else {Toast.makeText(getApplicationContext(),R.string.empty_not_saved,Toast.LENGTH_LONG).show();}}
93.13 小结
MainActivity
您有一个应用程序可以在列表 ( ,RecyclerView
,WordListAdapter
)中显示单词。您可以将单词添加到列表 (NewWordActivity
)。一个词是Word
实体类的一个实例。单词被缓存在RecyclerViewAdapter
单词List
(mWords
) 中。当数据发生变化时,列表会自动更新并重新显示。发生自动更新是因为在 中MainActivity
,有一个Observer
观察单词并在单词更改时收到通知。当有变化时,观察者的onChange()
方法被执行并mWords
在WordListAdapter
.可以观察到数据,因为它是LiveData
。观察到的是对象LiveData<List<Word>>
返回的WordViewModel
。WordViewModel
从用户界面隐藏有关后端的所有内容。它提供了访问 UI 数据的方法,并返回LiveData
以便MainActivity
可以设置观察者关系。视图、活动和片段仅通过ViewModel
. 因此,数据来自哪里并不重要。在这种情况下,数据来自存储库。ViewModel
不需要知道存储库与什么交互。它只需要知道如何与Repository 交互,就是通过Repository 暴露的方法。存储库管理一个或多个数据源。在 RoomWordsSample 应用程序中,该后端是 Room 数据库。Room 是一个包装器并实现了一个 SQLite 数据库。Room 为您做了很多以前必须自己做的工作。例如,Room 可以完成您过去使用SQLiteOpenHelper
类所做的所有事情。DAO 将方法调用映射到数据库查询,因此当 Repository 调用诸如 之类的方法时getAllWords()
,Room 可以执行**。**SELECT * from word_table ORDER BY word ASC观察查询返回的结果LiveData
。因此,每次 Room 中的数据发生变化时,都会执行Observer
接口的onChanged()
方法并更新 UI。