Room+LiveData+ViewModel+RecyclerView

1 简介

1.1 Room

android 官方推荐的数据库框架,room主要包含三个组件:roomDatabase,entity,Dao. 使用 Room 数据库来获取与该数据库关联的数据访问对象 (DAO)。然后,应用使用每个 DAO 从数据库中获取实体, 然后再将对这些实体的所有更改保存回数据库中。最后,应用使用实体来获取和设置与数据库中的表列相对应的值。

1.2 LiveData

LiveData是可以在给定生命周期内观察到的数据持有者类。 这意味着可以将Observer与LifecycleOwner成对添加,并且只有在配对的LifecycleOwner处于活动状态时,才会向该观察者通知有关包装数据的修改。 如果LifecycleOwner的状态为STARTED或RESUMED,则将其视为活动状态。 通过observeForever添加的观察者被视为始终处于活动状态,因此将始终收到有关修改的通知。 对于这些观察者,您应该手动调用removeObserver。 liveData可以订阅数据库的变化,在子线程中查询数据,并且在主线程中更新UI。

1.3 ViewModel

ViewModel是一个类,负责为Activity或Fragment准备和管理数据。它还处理活动/片段与应用程序其余部分的通信(例如,调用业务逻辑类)。 始终与范围(片段或活动)相关联地创建ViewModel,只要范围是活动的,ViewModel就会保留。例如。如果是活动,则直到完成。换句话说,这意味着如果ViewModel的所有者因配置更改(例如旋转)而被销毁,则不会销毁它。所有者的新实例将重新连接到现有的ViewModel。 ViewModel的目的是获取并保留活动或片段所需的信息。活动或片段应能够观察ViewModel中的更改。 ViewModel通常通过LiveData或Android数据绑定公开此信息。您也可以使用自己喜欢的框架中的任何可观察性构造。

ViewModel的唯一责任是管理UI的数据。它绝不能访问您的视图层次结构或保留对活动或片段的引用。

1.4 RecyclerView

用来显示数据,

2 步骤

第一步:添加依赖

room,recyclerview,lifecycle.
   // Room components
    implementation "androidx.room:room-runtime:$rootProject.roomVersion"
    annotationProcessor "androidx.room:room-compiler:$rootProject.roomVersion"
    androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"

// Lifecycle components
    implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.archLifecycleVersion"
    annotationProcessor "androidx.lifecycle:lifecycle-compiler:$rootProject.archLifecycleVersion"

// UI
    implementation "com.google.android.material:material:$rootProject.materialVersion"

 使用java8,在模块的android下

compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }

在project 的gradle中添加依赖版本号:

ext {
    roomVersion = '2.2.1'
    archLifecycleVersion = '2.2.0-rc02'
    coreTestingVersion = '2.1.0'
    materialVersion = '1.0.0'
}
第二步:创建room数据库

①创建bean类
②创建Dao接口
③创建roomDataBase
④创建Repository

entity:

/**
* 每个@Entity类代表一个SQLite表。
* 注释您的类声明以表明它是一个实体。如果希望表名与类名不同,则可以指定表名。这将表命名为“ word_table”。
* */
@Entity(tableName = "word_table")
public class Word {
    /**
     * 每个实体都需要一个主键。为了简单起见,每个单词都充当其自己的主键。
     * */
    @PrimaryKey
    /**
     * 表示参数,字段或方法的返回值永远不能为null。
     * */
    @NonNull
    /**
     * 如果希望与成员变量的名称不同,请在表中指定列的名称。
     * */
    @ColumnInfo(name = "word")
    private String mWord;
    public Word(@NonNull String word) {this.mWord = word;}

    public String getWord(){return this.mWord;}
}

 Dao接口

/**
* 功能:
* 1.按字母顺序排列所有单词
* 2.插入一个词
* 3.删除所有单词
* */
@Dao
public interface WordDao {
    /**
     * 如果冲突中选择的策略与列表中已有的单词完全相同,则会忽略该单词
     * */
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    void insert(Word word);
    @Query("DELETE FROM word_table ")
    void deleteAll();
    @Query("SELECT * from word_table ORDER BY word ASC")
    LiveData<List> getAlphabetizedWords();
}

创建roomDatabase:


**
* Room是SQLite数据库之上的数据库层。
* Room负责处理您以前使用NET处理的普通任务SQLiteOpenHelper。
* Room使用DAO向其数据库发出查询。
* 默认情况下,为避免UI性能下降,Room不允许您在主线程上发出查询。当Room查询返回时LiveData,查询将自动在后台线程上异步运行。
* Room提供了SQLite语句的编译时检查。
* */
@Database(entities = {Word.class, People.class},version = 2,exportSchema = false)
public abstract class WordRoomDatabase extends RoomDatabase {
/**
* 您使用注释该类为Room数据库,@Database并使用注释参数声明该数据库中的实体并设置版本号。每个实体对应一个将在数据库中创建的表。
* 数据库迁移不在此代码实验室的范围内,因此exportSchema在此处设置为false以避免生成警告。
* 在实际的应用程序中,您应考虑为Room设置目录以用于导出架构,以便可以将当前架构签入版本控制系统
* */
    public abstract WordDao wordDao();
    public abstract PeopleDao peopleDao();

   /**
    * 我们定义了singleton,WordRoomDatabase,以防止同时打开多个数据库实例。
    * */
    private static volatile WordRoomDatabase INSTANCE;
    private static final int NUMBER_OF_THREADS = 4;
    /**
     * 我们创建了一个ExecutorService带有固定线程池的,您将使用该池在后台线程上异步运行数据库操作。
     * */
    public static final ExecutorService databaseWriteExecutor =
            Executors.newFixedThreadPool(NUMBER_OF_THREADS);
/**
* 您通过为每个@Dao创建一个抽象的“ getter”方法来使数据库提供其DAO。
* */

/**
* getDatabase返回单例。它将在首次访问数据库时使用Room的数据库构建器RoomDatabase在类的应用程序上下文
* 中创建一个对象WordRoomDatabase并将其命名,从而创建数据库"word_database"。
* */
    public static WordRoomDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (WordRoomDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            WordRoomDatabase.class, "word_database")
                            .addCallback(sRoomDatabaseCallback)
                            .addMigrations(MIGRATION_1_2)
                            .build();
                }
            }
        }
        return INSTANCE;
    }
    private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback() {
        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            super.onOpen(db);

            // If you want to keep data through app restarts,
            // comment out the following block
            databaseWriteExecutor.execute(() -> {
                // Populate the database in the background.
                // If you want to start with more words, just add them.
                WordDao dao = INSTANCE.wordDao();
                dao.deleteAll();

                Word word = new Word("Hello");
                dao.insert(word);
                word = new Word("World");
                dao.insert(word);
                PeopleDao dao1 = INSTANCE.peopleDao();
                People  people = new People();
                dao1.deleteAll();
                people.setName("张三");
                people.setAge(19);
                people.setSex("男");
                People  people1 = new People();
                people1.setName("李红");
                people1.setAge(20);
                people1.setSex("女");
                dao1.insert(people1);
            });
        }
    };
    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            database.execSQL("CREATE TABLE IF NOT EXISTS `people_table` (`name` TEXT PRIMARY KEY  NOT NULL, `age` INTEGER NOT NULL,'sex' TEXT NOT NULL)");

        }
    };

/*   static final Migration MIGRATION_2_3 = new Migration(2, 3) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE people_table "
                    + " ADD COLUMN pub_year INTEGER");
        }
    };*/


}


创建Repository:

public class WordRepository {

    private WordDao mWordDao;
    private LiveData<List> mAllWords;

    private PeopleDao mPeopleDao;



    private LiveData<List> mPeoples;

    // 请注意,为了对WordRepository进行单元测试,必须删除Application依赖项。
    // 这增加了复杂性和更多的代码,并且该示例与测试无关。
    // See the BasicSample in the android-architecture-components repository at
    // https://github.com/googlesamples
    public WordRepository(Application application) {
        WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
        mWordDao = db.wordDao();
        mAllWords = mWordDao.getAlphabetizedWords();
        mPeopleDao =  db.peopleDao();
        mPeoples = mPeopleDao.getAllPeople();

    }
    /**
     * words的方法
     * */
    // Room在单独的线程上执行所有查询。
    // 观察到的LiveData将在数据更改时通知观察者。
    public LiveData<List> getAllWords() {
        return mAllWords;
    }

    // 您必须在非UI线程上调用此函数,否则您的应用程序将引发异常。
    // Room确保您不会在主线程上执行任何长时间运行的操作,从而阻止了UI。
    public void insert(Word word) {
        WordRoomDatabase.databaseWriteExecutor.execute(() -> {
            mWordDao.insert(word);
        });
    }
    /**
     * people的方法
     * */
    public LiveData<List> getmPeoples() {
        return mPeoples;
    }
    public void insert(People people){
        WordRoomDatabase.databaseWriteExecutor.execute(()->{mPeopleDao.insert(people);});
    }

}

第三步:创建ViewModel

**
* A ViewModel以生命周期感知的方式保存应用程序的UI数据,以在配置更改后生存下来。
* 将应用程序的UI数据与Activity和Fragment类分开,可以更好地遵循单一职责原则:
* 您的活动和片段负责将数据绘制到屏幕上,而您ViewModel可以负责保存和处理UI所需的所有数据。
* */
public class WordViewModel extends AndroidViewModel {

    /**
     * 1.创建了一个名为的类WordViewModel,该类获取Application作为参数并扩展AndroidViewModel。
     * 2.添加了一个私有成员变量来保存对存储库的引用。
     * 3.添加了一种getAllWords()方法来返回缓存的单词列表。
     * 4.实现了创建的构造函数WordRepository。
     * 5.在构造函数中,allWords使用存储库初始化LiveData。
     * 6.创建了一个包装insert()方法,该方法调用存储库的insert()方法。这样,insert()UI 的实现就被封装了。
     * */
    private WordRepository mRepository;

    private LiveData<List> mAllWords;
    private LiveData<List> mPeoples;
    public WordViewModel(@NonNull Application application) {
        super(application);
        mRepository = new WordRepository(application);
        mAllWords = mRepository.getAllWords();
        mPeoples = mRepository.getmPeoples();

    }
    public LiveData<List> getAllWords() { return mAllWords; }

    public void insert(Word word) { mRepository.insert(word); }
    public LiveData<List> getmPeoples() { return mPeoples; }

    public void insert(People people) { mRepository.insert(people); }


}



第四步:创建RecyclerView布局


public class WordListAdapter extends RecyclerView.Adapter {
    class WordViewHolder extends RecyclerView.ViewHolder {
        private final TextView wordItemView;

        private WordViewHolder(View itemView) {
            super(itemView);
            wordItemView = itemView.findViewById(R.id.textView);
        }
    }

    private final LayoutInflater mInflater;
    private List mWords; // Cached copy of words

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

    @NonNull
    @Override
    public WordViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.recyclerview_item,parent,false);
        return new WordViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull 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");
        }
    }
    public void setWords(List 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
    public int getItemCount() {
        if (mWords != null)
            return mWords.size();
        else return 0;
    }


}



第五步:在MainActivity获取ViewModel的实例,并通过ViewModel对数据库进行增删改查


  private void init(){
        recyclerview = findViewById(R.id.recyclerview);
        adapter = new WordListAdapter(this);
        recyclerview.setLayoutManager(new LinearLayoutManager(this));
        recyclerview.setAdapter(adapter);
        mWordViewModel = new ViewModelProvider(this).get(WordViewModel.class);
        mWordViewModel.getAllWords().observe(this, new Observer<List>() {
            @Override
            public void onChanged(@Nullable final List words) {
                // Update the cached copy of the words in the adapter.
                adapter.setWords(words);
            }
        });
        mWordViewModel.getmPeoples().observe(this, new Observer<List>() {
            @Override
            public void onChanged(List people) {
                for(People people1 :people) {
                    Log.d("people Data", "name:" + people1.getName()+"age:"+people1.getAge()+"sex:"+people1.getSex());
                }
            }
        });

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, NewWordActivity.class);
                startActivityForResult(intent, NEW_WORD_ACTIVITY_REQUEST_CODE);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable 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();
        }
    }
Room+LiveData+ViewModel+RecyclerView
滚动到顶部