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
④建立Reposite
entity:
@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;}
}
Dao接口
@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 {
public abstract WordDao wordDao();
public abstract PeopleDao peopleDao();
private static volatile WordRoomDatabase INSTANCE;
private static final int NUMBER_OF_THREADS = 4;
public static final ExecutorService databaseWriteExecutor =
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
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)");
}
};
}
建立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();
}
// Room在單獨的線程上執行所有查詢。
// 觀察到的LiveData将在資料更改時通知觀察者。
public LiveData<List> getAllWords() {
return mAllWords;
}
// 您必須在非UI線程上調用此函數,否則您的應用程式将引發異常。
// Room確定您不會在主線程上執行任何長時間運作的操作,進而阻止了UI。
public void insert(Word word) {
WordRoomDatabase.databaseWriteExecutor.execute(() -> {
mWordDao.insert(word);
});
}
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 {
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();
}
}