天天看點

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

    ④建立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();

        }

    }