第五届世界互联网大会昨日开幕,来自76个国家的1500余位嘉宾出席大会。腾讯公司董事会主席兼首席执行官马化腾在大会开幕式演讲中表示,全球产业都在进行数字化,在此期间机遇挑战并存,产业互联网机会巨大。

作者简介

本篇来自 walker不抽烟的投稿,给大家介绍一下 Android JetPack架构相关知识。一起来看看!希望大家喜欢。

walker不抽烟的博客地址:

https://blog.csdn.net/itismelzp

前言

本文翻译自:Android Room with a View - Java,地址如下:

https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0

架构组件的目的是提供对应用程序体系结构的指导,并为诸如生命周期管理和数据持久化等常见任务提供开发库。

架构组件帮你构造一个鲁棒、易测试、可维护和少模板代码的应用。

架构组件是什么?

为了介绍相关术语,这里有简短的介绍一下各架构组件以及它们之前如何协作。注意这个代码库包含一部分架构组件,它们是:LiveData、ViewModel和Room。每个组件会在使用的时候做解释。下图是基本的架构形式。

  • Entity: 当注解的类,用于描述数据库表。
  • SQLite database: SQLite数据库。
  • DAO: 数据访问对象。SQL查询到方法的映射。
  • Room database: 在SQLite数据库之上的数据库层。
  • Repository: 数据仓库,用于管理数据源。
  • ViewModel: 提供数据给UI。是Repository与UI的连接中心。

你要构建什么

这个Demo用于在Room中存储words(单词)列表,并显示在RecyclerView中。这是个简单的示例,但也足以为它来作为开发应用的模板。

  • Demo的功能:
  • 获取与保存"单词";
  • 单词显示在MainActivity的RecyclerView中;
  • 通过悬浮按钮调起另一个activity,用于输入单词。

RoomWordSample架构预览

下图展示了应用的各个部分。除了SQLite database,其他部分都用在自己创建的类中封装。

你会学到什么

学会如何使用架构组件库和生命周期库设计和构建应用程序。

这里有许多步骤去使用架构组件和推荐的框架。最重要的是学会模型创建的作用、理解各部分组合与数据流向。通过这个Demo,你不单只是简单的复制和粘贴本文的代码,还要理解其内部原理。

你需要掌握什么

  • Android Studio 3.0或更高版本的使用。
  • 一台Android设备或模拟器。

你必须熟悉Java编程,面向对象设计,Android开发基础。尤其:

  • RecyclerView 及其适配器adapters
  • SQLite数据库及SQLite查询语言
  • 线程与AsyncTask
  • 了解一些数据与UI分离的构架概念,如MVP、MVC

本Demo着重于Android架构组件,非主要代码可行自行复制与粘贴。

Android Studio的优势

创建应用

打开Android Studio创建应用:

  1. 新建应用RoomWordSample,目标sdk为26+
  2. 不选include Kotlin support和include C++ support
  3. 下一步,选Phone & Tablet,minimum SDK选API 26
  4. 下一步,选择Basic Activity
  5. 下一步,完成。

更新gradle文件

添加组件库到gradle。在Module:app的build.gradle中dependencies末尾加入:

// Room components

implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion"

annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion"

androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion"

// Lifecycle components

implementation "android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion"

annotationProcessor "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion"

中Project:RoomWordSample的build.gradle中加入版本信息:

ext{

roomVersion= '1.1.1'

archLifecycleVersion = '1.1.1'

}

创建实体

本demo的数据是“单词”,因此首先创建一个Word类,并为其创建构造函数与必要的get方法。这样Room才可以实例化对象。

下面是Word类:

publicclassWord{

privateString mWord;

publicWord( @NonNullString word) { this.mWord = word;}

publicString getWord(){ returnthis.mWord;}

}

为了使Word类对Room库有意义,我们需要为它加注解。注解用了将实体与数据库相关联,Room根据相应的注解信息去生成对应的代码。

  • @Entity(tableName = "word_table") 每一个@Entity类代表数据库中的一张表。tableName为生成表的表名。
  • @PrimaryKey 每个实体需要一个主键。
  • @NonNull 表示参数、字段或返回值不能为null。
  • @ColumnInfo(name = "word") 指定与成员变量对应的列名。
  • 为一个字段需要是public的或提供get方法。

添加注解的Word类:

@Entity(tableName = "word_table")

publicclassWord{

@PrimaryKey

@NonNull

@ColumnInfo(name = "word")

privateString mWord;

publicWord(String word) { this.mWord = word;}

publicString getWord(){ returnthis.mWord;}

}

创建DAO(数据访问对象)

  • 什么是DAO

DAO即数据访问对象,你可以指定SQL查询语句,并将它与方法关联起来。编译器会对常规SQL注解进行编译检查,如@Insert。

DAO对象必须是个接口或抽象类。

默认情况下,所有的查询必须在单独线程中执行。

Room将通过DAO对象去创建相应的接口。

  • DAO的写法

DAO是代码的基础,它用于提供word的增、删、改、查。

  • 创建一个名为WordDao的接口。
  • 为WordDao添加@Dao注解
  • 声明一个插入方法void insert(Word word);
  • 为上述方法添加@Insert注解,并且不需要为其提供SQL语句!(同样的用法还有@Delete and @Update)
  • 声明方法void deleteAll();
  • 这里没有方便的注解可以用于删除多个实体,因此需要用@Query注解
  • 还需要为@Query注解提供SQL语句@Query("DELETE FROM word_table")
  • 创建方法List<Word> getAllWords();
  • 为其添加注解与SQL@Query("SELECT * from word_table ORDER BY word ASC")

下面是其完整的代码:

@Dao

publicinterfaceWordDao{

@Insert

voidinsert(Word word);

@Query( "DELETE FROM word_table")

voiddeleteAll();

@Query( "SELECT * from word_table ORDER BY word ASC")

List<Word> getAllWords();

}

LiveData类

当数据被改变后,通常你需要作一些操作,例如将更新的数据展示在UI上。这就意味着你必须去观察这些数据,以便于当数据改变时你能做出反应。根据数据不同的存储方式,这可能会很棘手。观察贯串多个组件中数据的变化,你必须要编写一个显式、严格依赖的调用链。这使得测试和调试变得非常困难。

LiveData是 lifecycle library 中,用于数据观察的类,可用于解决上述难题。在你的方法中使用LiveData为返回值。这样Room将会为你生成所有必须的代码,当数据库更新时,自动去更新LiveData。

使用LiveData的目的是为了管理数据的更新。但是LiveData类并没有提供公有的更新数据的方法。我们应当使用MutableLiveData,它有两个公有方法(setValue(T)、postValue(T))用于存储数据。通常,MutableLiveData是在ViewModel中使用,然后ViewModel只向观察者暴露不可变的LiveData对象。

在WordDao中,改变getAllWords()方法的返回值:

@Query(" SELECT* fromword_table ORDERBYword ASC")

LiveData<List<Word>> getAllWords();

后面我们会在MainActivity的onCreate()方法中创建一个Observer对象,并覆盖其onChanged()方法。当LiveData改变时,观察者会被通知然后onChanged()会被回调。这时你可以更新适配器中的缓存数据,然后在适配器中更新UI。

添加Room数据库

  • 什么是Room数据库?

Room是在SQLite之上的数据库层。Room用于处理我们曾经用SQLiteOpenHelper来处理任务。

  • Room通过DAO向数据库发送查询
  • 默认情况下,为了避免降低UI线程的性能,Room不允许在主线程中执行数据库操作
  • Room提供了编译时的SQL语句检查
  • 创建的Room类必须是抽象的,并且继承RoomDatabase
  • 通常,在整体应用中只需要一个Room数据库实例,即单例。
  • 实现Room数据库
  • 创建一个public abstract类WordRoomDatabase,并继承RoomDatabase。即public abstract class WordRoomDatabase extends RoomDatabase {}
  • 标注其为一个Room数据库,@Database(entities = {Word.class}, version = 1),声明其在数据库中的实体,并指定版本号。实体可以声明多个,声明的实体将在数据库中创建对应的表。
  • 定义使用数据库的DAO。给每一个@Dao提供get方法。public abstract WordDao wordDao();

完整代码如下:

@Database(entities = {Word.class}, version = 1)

publicabstractclassWordRoomDatabaseextendsRoomDatabase{

publicabstractWordDao wordDao();

}

  • 使WordRoomDatabase作为单例。

privatestaticvolatileWordRoomDatabase INSTANCE;

staticWordRoomDatabase getDatabase(finalContext context){

if(INSTANCE == null) {

synchronized(WordRoomDatabase.class) {

if(INSTANCE == null) {

// Create database here

}

}

}

returnINSTANCE;

}

  • 实例化RoomDatabase对象:使用Room的databaseBuilder,从WordRoomDatabase类的应用上下文context中创建RoomDatabase对象,并将数据库全名为"word_database"。

// Createdatabasehere

INSTANCE= Room.databaseBuilder(context.getApplicationContext(),

WordRoomDatabase.class, "word_database")

.build();

下面是完整代码:

@Database(entities = {Word.class}, version = 1)

publicabstractclassWordRoomDatabaseextendsRoomDatabase{

publicabstractWordDao wordDao();

privatestaticvolatileWordRoomDatabase INSTANCE;

staticWordRoomDatabase getDatabase(finalContext context){

if(INSTANCE == null) {

synchronized(WordRoomDatabase.class) {

if(INSTANCE == null) {

INSTANCE = Room.databaseBuilder(context.getApplicationContext(),

WordRoomDatabase.class, "word_database")

.build();

}

}

}

returnINSTANCE;

}

}

创建Repository(数据仓库)

  • 什么是Repository?

Repository是一个可访问多数据源的类。它并非构架组件库中的一部分,但它是代码分离和体系结构的最佳实践建议。Repository用于处理数据操作,它为应用提供数据访问接口。

  • 为什么要使用Repository?

Repository管理查询线程,并允许您使用多个后端。在最常见的示例中,Repository实现了决定是从网络获取数据还是从本地缓存中获取结果的逻辑。

  • Repository的实现
  • 创建一个公共类WordRepository
  • 添加两个成员变量

privateWordDao mWordDao;

privateLiveData< List<Word>> mAllWords;

  • 添加一个构造函数,该构造函数获取数据库的句柄并初始化成员变量。

WordRepository(Application application) {

WordRoomDatabase db = WordRoomDatabase.getDatabase(application);

mWordDao = db.wordDao();

mAllWords = mWordDao.getAllWords();

}

  • 为getAllWords()添加一个包装器。Room在单独的线程上执行所有查询。观察到LiveData数据更改时,将通知观察者。

LiveData< List<Word>> getAllWords() {

returnmAllWords;

}

  • 为insert()方法添加一个包装器。使用AsyncTask来执行,确保其是在非UI线程中执行。

publicvoidinsert(Word word){

newInsertAsyncTask(mWordDao).execute(word);

}

  • InsertAsyncTask的实现

privatestaticclassinsertAsyncTaskextendsAsyncTask<Word, Void, Void> {

privateWordDao mAsyncTaskDao;

insertAsyncTask(WordDao dao) {

mAsyncTaskDao = dao;

}

@Override

protectedVoid doInBackground(finalWord... params){

mAsyncTaskDao.insert(params[ 0]);

returnnull;

}

}

下面是完整代码:

publicclassWordRepository{

privateWordDao mWordDao;

privateLiveData<List<Word>> mAllWords;

WordRepository(Application application) {

WordRoomDatabase db = WordRoomDatabase.getDatabase(application);

mWordDao = db.wordDao();

mAllWords = mWordDao.getAllWords();

}

LiveData<List<Word>> getAllWords() {

returnmAllWords;

}

publicvoidinsert(Word word){

newinsertAsyncTask(mWordDao).execute(word);

}

privatestaticclassinsertAsyncTaskextendsAsyncTask<Word, Void, Void> {

privateWordDao mAsyncTaskDao;

insertAsyncTask(WordDao dao) {

mAsyncTaskDao = dao;

}

@Override

protectedVoid doInBackground(finalWord... params){

mAsyncTaskDao.insert(params[ 0]);

returnnull;

}

}

}

创建ViewModel

  • 什么是ViewModel?

ViewModel的作用是向UI提供数据,并保存配置更改。ViewModel充当Repository 和UI之间的通信中心。还可以使用ViewModel在fragments之间共享数据。ViewModel是 lifecycle library 库的一部分。

  • 为什么使用ViewModel?

当配置被更改时,ViewModel以一种有生命周期感知的方式保存应用的UI数据。将应用程序的UI数据与Activity和Fragment类分离,可以让你更好地遵循单一责任原则:你的activities和fragments负责将数据绘制到屏幕上,而ViewModel则负责保存和处理UI所需的所有数据。

在ViewModel中,对于UI将使用或显示的可变数据,请使用LiveData。使用LiveData有几个好处:

  • 您可以在数据上放置一个观察者(而不是轮询更改),并且只在数据实际更改时更新UI。
  • Repository和UI由ViewModel完全分离。没有来自ViewModel的数据库调用,使得代码更易于测试。
  • ViewModel的实现
  • 创建WordViewModel类,使其继承AndroidViewModel。

publicclassWordViewModelextendsAndroidViewModel{}

  • 添加一个私有成员变量来保存对存储库的引用。

privateWordRepository mRepository;

  • 添加一个私有LiveData成员变量来缓存单词列表。

privateLiveData< List<Word>> mAllWords;

  • 添加一个构造函数,该构造函数获取对存储库的引用,并从存储库获取单词列表。

publicWordViewModel(Application application){

super(application);

mRepository = newWordRepository(application);

mAllWords = mRepository.getAllWords();

}

  • 为所有单词添加一个get方法。这完全隐藏了对UI的实现。

LiveData< List<Word>> getAllWords() { returnmAllWords; }

  • 创建一个调用Repository的insert()方法的包装器insert()方法。这样,insert()的实现对于UI就完全透明了。

publicvoidinsert(Word word){ mRepository.insert(word); }

下面是WordViewModel的实现:

publicclassWordViewModelextendsAndroidViewModel{

privateWordRepository mRepository;

privateLiveData<List<Word>> mAllWords;

publicWordViewModel(Application application){

super(application);

mRepository = newWordRepository(application);

mAllWords = mRepository.getAllWords();

}

LiveData<List<Word>> getAllWords() { returnmAllWords; }

publicvoidinsert(Word word){ mRepository.insert(word); }

}

警告:不要将context传递到ViewModel实例中。不要在ViewModel中存储活动、片段或视图实例或它们的context。

添加XML布局

在value/styes.xml中添加列表项的样式:

<!-- The default font for RecyclerView items is too small.

The margin is a simple delimiter between the words. -->

<stylename="word_title">

<itemname="android:layout_width">match_parent</item>

<itemname="android:layout_height">26dp</item>

<itemname="android:textSize">24sp</item>

<itemname="android:textStyle">bold</item>

<itemname="android:layout_marginBottom">6dp</item>

<itemname="android:paddingLeft">8dp</item>

</style>

添加一个layout/recyclerview_item.xml布局:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"android:layout_width="match_parent"

android:layout_height="wrap_content">

<TextView

android:id="@+id/textView"

style="@style/word_title"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="@android:color/holo_orange_light"/>

</LinearLayout>

在Layout/Content_main.xml中,将TextView替换为ReccyclerView:

<android.support.v7.widget.RecyclerView

android:id="@+id/recyclerview"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@android:color/darker_gray"

tools:listitem="@layout/recyclerview_item"/>

浮动动作按钮(FAB)应与可用动作相对应。在Layout/Activitymain.xml文件中,给FloatingActionButton一个+符号图标:

  • 在Layout/Activitymain.xml文件中,选择File>New>VectorAsset。
  • 选择Material Icon。
  • 点击Android机器人图标:点field, 然后选 + (“add”) 资源。
  • 按以下方式更改布局文件代码。

android:src="@drawable/ic_add_black_24dp"

添加RecycleView

您将在RecycleView中显示数据,这比将数据抛到TextView中要好一些。注意,适配器中的mWord变量缓存数据。在下一个任务中,添加自动更新数据的代码。还请注意,getItemCount()方法需要优雅地考虑数据尚未准备好且mWord仍然为空的可能性。

添加一个类WordListAdapter,它扩展了ReccyclerView.Adapter。

这是代码:

publicclassWordListAdapterextendsRecyclerView.Adapter<WordListAdapter.WordViewHolder> {

classWordViewHolderextendsRecyclerView.ViewHolder{

privatefinalTextView wordItemView;

privateWordViewHolder(View itemView){

super(itemView);

wordItemView = itemView.findViewById(R.id.textView);

}

}

privatefinalLayoutInflater mInflater;

privateList<Word> mWords; // Cached copy of words

WordListAdapter(Context context) { mInflater = LayoutInflater.from(context); }

@Override

publicWordViewHolder onCreateViewHolder(ViewGroup parent, intviewType){

View itemView = mInflater.inflate(R.layout.recyclerview_item, parent, false);

returnnewWordViewHolder(itemView);

}

@Override

publicvoidonBindViewHolder(WordViewHolder holder, intposition){

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

}

}

voidsetWords(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).

@Override

publicintgetItemCount(){

if(mWords != null)

returnmWords.size();

elsereturn0;

}

}

在MainActivity的onCreate()方法中添加ReccyclerView。在onCreate()方法中:

RecyclerView recyclerView = findViewById(R.id.recyclerview);

finalWordListAdapter adapter = newWordListAdapter( this);

recyclerView.setAdapter(adapter);

recyclerView.setLayoutManager( newLinearLayoutManager( this));

运行你的应用程序,以确保一切正常。没有项目,因为您还没有连接到数据,所以应用程序应该显示灰色背景,没有任何列表项目。

填充数据库

要删除所有内容并在应用程序启动时重新填充数据库,您可以创建一个RoomDatabase.Callback并覆盖onOpen()。由于不能对UI线程执行Room数据库操作,因此onOpen()创建并执行AsyncTask来向数据库添加内容。

下面是在WordRoomDatabase类中创建回调的代码:

privatestaticRoomDatabase.Callback sRoomDatabaseCallback =

newRoomDatabase.Callback(){

@Override

publicvoidonOpen(@NonNull SupportSQLiteDatabase db){

super.onOpen(db);

newPopulateDbAsync(INSTANCE).execute();

}

};

下面是AsyncTask的代码,它删除数据库的内容,然后用两个单词“Hello”和“World”填充数据库。欢迎加入更多的单词!

privatestaticclassPopulateDbAsyncextendsAsyncTask<Void, Void, Void> {

privatefinalWordDao mDao;

PopulateDbAsync(WordRoomDatabase db) {

mDao = db.wordDao();

}

@Override

protectedVoid doInBackground(finalVoid... params){

mDao.deleteAll();

Word word = newWord( "Hello");

mDao.insert(word);

word = newWord( "World");

mDao.insert(word);

returnnull;

}

}

最后,在调用.build()之前,将回调添加到数据库构建序列。

.addCallback(sRoomDatabaseCallback)

添加NewWordActivity

将这些字符串资源添加到values/strings.xml中:

<stringname="hint_word">Word... </string>

<stringname="button_save">Save </string>

<stringname="empty_not_saved">Word not saved because it is empty. </string>

在value/color s.xml中添加此颜色资源:

<colorname="buttonLabel">#d3d3d3 </color>

将这些维度资源添加到values/dimens.xml:

<dimenname="small_padding">6dp </dimen>

<dimenname="big_padding">16dp </dimen>

在布局文件夹中创建Activity_new_word.xml文件:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"android:layout_width="match_parent"

android:layout_height="match_parent">

<EditText

android:id="@+id/edit_word"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:fontFamily="sans-serif-light"

android:hint="@string/hint_word"

android:inputType="textAutoComplete"

android:padding="@dimen/small_padding"

android:layout_marginBottom="@dimen/big_padding"

android:layout_marginTop="@dimen/big_padding"

android:textSize="18sp"/>

<Button

android:id="@+id/button_save"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="@color/colorPrimary"

android:text="@string/button_save"

android:textColor="@color/buttonLabel"/>

</LinearLayout>

使用空活动模板创建一个新活动,NewWordActivity。验证活动是否已添加到AndroidManifest中!

<activityandroid:name=".NewWordActivity"></activity>

下面是该activity的代码:

publicclassNewWordActivityextendsAppCompatActivity{

publicstaticfinalString EXTRA_REPLY = "com.example.android.wordlistsql.REPLY";

privateEditText mEditWordView;

@Override

publicvoidonCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_new_word);

mEditWordView = findViewById(R.id.edit_word);

finalButton button = findViewById(R.id.button_save);

button.setOnClickListener( newView.OnClickListener() {

publicvoidonClick(View view){

Intent replyIntent = newIntent();

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

}

});

}

}

连接数据

最后一步是通过保存用户输入的新单词并在RecyclerView中显示Word数据库的当前内容,将UI连接到数据库。

要显示数据库的当前内容,添加一个观察者来观察ViewModel中的LiveData。每当数据更改时,都会调用onchange()回调,该回调调用适配器的setWord()方法,以更新适配器的缓存数据并刷新显示的列表。

在MainActivity中,为ViewModel创建一个成员变量:

privateWordViewModel mWordViewModel;

使用ViewModelProviders将ViewModel与UI控制器关联起来。当应用程序第一次启动时,ViewModelProviders将创建ViewModel。当activity 被销毁时,例如通过配置更改,ViewModel就会持续存在。重新创建activity 时,ViewModelProviders将返回现有的ViewModel。

在onCreate()中,从ViewModelProvider获取一个ViewModel。

mWordViewModel = ViewModelProviders.of( this). get(WordViewModel. class);

同样在onCreate()中,为getAllWords()返回的LiveData添加一个观察者。当观察到的数据发生变化且activity 位于前台时,onchange()方法就会调用。

mWordViewModel.getAllWords().observe( this, newObserver< List<Word>>() {

@Override

public voidonChanged( @NullablefinalList<Word> words) {

// Update the cached copy of the words in the adapter.

adapter.setWords(words);

}

});

在MainActivity中,为NewWordActivity添加onActivityResult()代码。如果activity返回RESULT_OK,则通过调用WordViewModel的insert()方法将返回的单词插入数据库。

publicvoidonActivityResult(intrequestCode, intresultCode, Intent data){

super.onActivityResult(requestCode, resultCode, data);

if(requestCode == NEW_WORD_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {

Word word = newWord(data.getStringExtra(NewWordActivity.EXTRA_REPLY));

mWordViewModel.insert(word);

} else{

Toast.makeText(

getApplicationContext(),

R. string.empty_not_saved,

Toast.LENGTH_LONG).show();

}

}

定义缺少的请求代码:

publicstaticfinalintNEW_WORD_ACTIVITY_REQUEST_CODE = 1;

在MainActivity中,当用户点击Fab时启动NewWordActivity。用以下代码替换Fab的onclick()单击处理程序中的代码:

Intent intent = newIntent(MainActivity. this, NewWordActivity. class);

startActivityForResult(intent, NEW_WORD_ACTIVITY_REQUEST_CODE);

运行你的APP!!!

当您在NewWordActivity中向数据库添加一个单词时,UI将自动更新。

总结

现在你有了一个实用的应用程序,让我们回顾一下你已经构建了什么。这是最开发的应用程序的结构。您有一个在列表中显示单词的应用程序(MainActivity、ReccyclerView、WordListAdapter)。您可以向列表中添加单词(NewWordActivity)。单词是单词实体类的实例。这些单词作为单词(mWords) List缓存在RecyclerViewAdapter中。当数据库中的单词更改时,此单词列表会自动更新和重新显示。

用于自动UI更新的数据流(反应性UI)

自动更新是可能的,因为我们正在使用LiveData。在MainActivity中,有一个观察者从数据库中观察到LiveData这个词,并在它们更改时得到通知。当发生更改时,将执行观察者的onChange()方法,并更新WordListAdapter中的mWord。可以观察到数据,因为它是LiveData。观察到的是由WordViewModel对象的getAllWords()方法返回的LiveData<list<word>>。WordViewModel从UI层隐藏关于后端的所有内容。它提供访问数据层的方法,并返回LiveData,以便MainActivity可以设置观察者关系。Activities(和Fragments)仅通过ViewModel与数据交互。因此,数据从何而来并不重要。

在这个demo,数据来自一个存储库。ViewModel不需要知道这个仓库与什么交互。它只需要知道如何通过Repository公开的方法与Repository交互。

Repository 管理一个或多个数据源。在WordListSample应用程序中,后端是一个Room数据库。Room是一个包装器,实现了SQLite数据库。房间为你做了很多工作,你以前不得不自己做。例如,Room完成了以前使用SQLiteOpenHelper类所做的一切。

DAO映射方法调用数据库查询,以便当Repository调用getAllWords()等方法时,Room可以通过执行SELECT * from word_table ORDER BY word ASC。

因为从查询返回的结果被观察到LiveData,所以每当Room中的数据发生变化时,会执行观察者接口的onChanged()方法,并更新UI。

单击以下链接下载此codelab的解决方案代码:

https://download.csdn.net/download/itismelzp/10753867

Android JetPack架构篇,一个实战项目带你学懂JetPack相关推荐

  1. Android Jetpack架构组件(一)带你了解Android Jetpack

    本文首发于微信公众号「后厂村码农」 前言 Android已经发展了11年,可以说是比较成熟的技术了,一开始时框架很少,也没有什么规范,所有的代码都是要自己写,比如网络请求,数据库请求,数据解析等等.后 ...

  2. 31个Python实战项目带你学会图像处理

    近期小白学视觉公众号推出了多篇Python+OpenCV实战项目的文章,深受小伙伴们的喜爱.最近有小伙伴推荐,希望可以讲经典的项目整理一下,集成手册,便于小伙伴在日常的学习中使用.于是小白挑选了#Op ...

  3. MTK平台camera bsp学习之android平台架构篇

    android平台架构(简单了解,都是csdn上看其他博客摘抄下来) 从上到下:分为 APP层 framework层 native层 hal层 kernel层 camera bsp侧重点:kernel ...

  4. [Golang梦工厂]一个小项目带你学会GIN框架、JWT鉴权、swagger生成接口文档,看这一篇就够了

    前言 哈喽,大家好,我是asong,这是我的第八篇原创文章.听说你们还不会jwt.swagger,所以我带来一个入门级别的小项目.实现用户登陆.修改密码的操作.使用GIN(后台回复Golang梦工厂: ...

  5. 最新优秀的通用Android应用架构:从建项目开始

    http://www.jianshu.com/p/d9e4ddd1c530 1.项目结构 现在的MVP模式越来越流行.就默认采用了. 如果项目比较小的话: app--Application Activ ...

  6. Android Studio创建第一个Flutter项目

    一.创建Flutter项目 1.1 file->New–>New Flutter Project 1.2 配置Flutter SDK Path 1.3 填写项目相关信息 二.android ...

  7. 架构之路--实战项目记录(二) 忘记数据库 开始抽象

    对我而言,认识BLL层的作用,或者领域模型驱动的含义,最大的干扰来自数据库. 我们很清楚的知道UI层的含义,也知道数据层是做什么的.但对于一个简单的(甚至是相当复杂的)系统而言,实在不知道除了对数据库 ...

  8. mysql主从架构升级_实战项目——mysql主从架构的实现

    一主一从 1.1 环境准备: centos系统服务器2台. 一台用户做Mysql主服务器, 一台用于做Mysql从服务器, 配置好yum源. 防火墙关闭. 各节点时钟服务同步. 各节点之间可以通过主机 ...

  9. android资源编译失败,在android studio中打开一个新项目时,Android资源编译失败

    用我的android studio打开新项目时出现此错误 Android资源编译失败输出:C:\ Projects \ Popular2 \ app \ src \ main \ res \ draw ...

最新文章

  1. 3、编写一个prod()函数,可以接受一个list并利用reduce()求积
  2. Linux下Tomcat与Apache Web服务器的整合
  3. jQuery的Autocomplete插件的远程url取json数据的问题
  4. void什么意思python_python 为什么没有 void 关键字?
  5. mac能远程到wjn的linux,linux远程桌面vnc-server安装配置
  6. Docker入门-构建第一个Java程序
  7. 对象池commons-pool
  8. vue动态生成表单元素
  9. hook什么意思_这是什么骚代码!
  10. 基于递归算法,树形结构数据下业务场景,封装解决方法
  11. [C语言] 插入排序之直接插入排序的特性及实现
  12. Linux文件目录类指令
  13. AVL树---最简单的实现
  14. ps制作html图标素材,PS按钮图标制作
  15. Linux之squirrelmail小松鼠客户端搭建
  16. 关于Unity中DOTween插件的使用(专题一)
  17. python清理浏览器文件_URL可以在浏览器或wget中正常工作,但是从Python或cURL中清空...
  18. 怎么把一个gif表情包分解成多个?
  19. 如何查看windows的CUDA版本
  20. 关于计算机分类 按用途可以分为,计算机的分类一般分为

热门文章

  1. [异能程序员]第四章 偶遇(第四更)
  2. 联想小新V1070-FXSE(FX版)怎么重装系统教程
  3. 晚安前学习——第1天
  4. 运算放大器的差分放大电路
  5. spring boot oauth2 facebook
  6. 浙江宁波天童禅寺重阳节前夕慰问鄞州区东吴福利院
  7. html锚点定位向下偏移,html锚点定位不准确问题
  8. scratch和python怎么读_Python如何像scratch一样朗读文字?|python3教程|python入门|python教程...
  9. NodeMcu arduino ESP8266 使用Ticker库(多任务处理)
  10. XMind 2021 Mac 去水印教程