Hi everyone, As the title of the tags I posted, today I would like to share my understanding of Android Architecture MVVM (Model – View – ViewModel) with you. I would like to briefly introduce about MVVM for You haven’t defined what it is
As its name implies, MVVM (Model – view – ViewModel) consists of 3 main components
Map of MVVM
first . Model Including classes working with data (Local query (SQLite)) This is very good Google support in the Room Database section, Implementing request Data from API (Https, …) must be outstanding. is the Retrofit, is it right? Next, the model receives requests to retrieve and change data from the View model. And the final note is that it does not directly communicate with the View 2 class. ViewModel. That’s strange
. View model Will receive events from View, From events (Events like click, Touch, …), then send read and update requests to the Model class. Contains LiveData variables that store temporary
” data and View classes will listen (Observable) Changes of Livedata to update the UI. The good thing is that the Viewmodel will not interfere or be affected by the UI so when we have Rotate, it will not be affected. In addition, the ViewModel will listen for events when data from the Model class changes and changes the LiveData variables. 3 View: Includes UI display classes. Does not contain processing code inside. Listening and observing Live Data data changes from Viewmodel Classes and updating corresponding UIs
The theory is too long. But I have tried, the package calls back to my understanding in the most accurate and concise way. Now comes the example directly and, I will just demo the code and will explain each component in it.
B1: The first error part when you follow the example is wrong, the main reason Wrong implementation with the sample code .
Build Gradle (App ):
// 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”
** Build Gradle Project **
123456 ext {roomVersion = '2.2.0'archLifecycleVersion = '2.2.0-beta01'coreTestingVersion = '2.1.0'materialVersion = '1.0.0'}
** B2: Initialize Word class with 1 attribute as mStringword **
12 @Entity(tableName = "word_table")public class Word {
1234567891011121314 @PrimaryKey@NonNull@ColumnInfo(name = "word")private String mWord;public Word(@NonNull String word) {this.mWord = word;}@NonNullpublic String getWord() {return this.mWord;}}
@Entity (tableName = “word_table”). @PrimaryKey @NonNull @ColumnInfo (name = “word”) In this class we have used the Room Database, using the corresponding Anotation.
B3: Initialize Interface WordDao (For each Entity you create a DAO class because this is the class that performs queries to SQLite and obviously different tables, the handling must be separate.) And forget to forget @Dao On Interface
@Dao public interface WordDao {
@Query (“SELECT * from word_table ORDER BY word ASC”) LiveData <List <Word>> getAlphabetizedWords ();
1234567 @Insert(onConflict = OnConflictStrategy.IGNORE)void insert(Word word);@Query("DELETE FROM word_table")void deleteAll();}
** B4: Create a class WordRoomDatabase **
@Database (entities = {Word.class}, version = 1, exportSchema = false) abstract class WordRoomDatabase extends RoomDatabase {
123456789101112131415161718192021222324252627282930313233 abstract WordDao wordDao();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).build();}}}return INSTANCE;}private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback() {@Overridepublic void onOpen(@NonNull SupportSQLiteDatabase db) {super.onOpen(db);databaseWriteExecutor.execute(() -> {WordDao dao = INSTANCE.wordDao();dao.deleteAll();Word word = new Word("Hello");dao.insert(word);word = new Word("World");dao.insert(word);});}};}
** B5: Create Repository **
1234567891011121314151617181920 class WordRepository {private WordDao mWordDao;private LiveData<List<Word>> mAllWords;WordRepository(Application application) {WordRoomDatabase db = WordRoomDatabase.getDatabase(application);mWordDao = db.wordDao();mAllWords = mWordDao.getAlphabetizedWords();}LiveData<List<Word>> getAllWords() {return mAllWords;}void insert(Word word) {WordRoomDatabase.databaseWriteExecutor.execute(() -> {mWordDao.insert(word);});}}
** B6: Create a ViewModel (The good part is paying attention to it here )) **
1234567891011121314151617181920 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;}void insert(Word word) {mRepository.insert(word);}}
1 2 3 4 5 6 | Ở Phần tạo viewmodel này . Các bạn luôn nhớ Việc sử dụng Live data để cập nhật data cho UI Là 1 điều vô cùng quan trọng bởi vì : Bạn có thể đặt observer vào dữ liệuvà chỉ cập nhật UI khi dữ liệu thực sự thay đổi. Repository và giao diện người dùng được phân tách hoàn toàn bởi ViewModel. Không call Database từ ViewModel (tất cả điều này được xử lý trong Repository), làm cho code dễ kiểm tra hơn. |
B7: Add XML
In the res / style tag Please add yourself:
12 <style name="word_title"><item name = “android: layout_width”> match_parent </item> <item name = “android: layout_marginBottom”> 8dp </item> <item name = “android: paddingLeft”> 8dp </item> <item name = ” android: background “> @android : color / holo_orange_light </item> <item name =” android: textAppearance “> @android : style / TextAppearance.Large </item> </style>
1 2 | Tiếp theo tạo 1 file Xml : có tên Là Recycleview_item : |
12 <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns: android = “http://schemas.android.com/apk/res/android” android: orientation = “vertical” android: layout_width = “match_parent” android: layout_height = “wrap_content”>
1234567 <TextViewandroid: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>
In Main_activity:
12 <?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns: android = ” http://schemas.android.com/apk/res/android ” xmlns: app = ” http://schemas.android.com/apk/res-auto ” xmlns: tools = ” http://schemas.android.com/tools ” android: layout_width = “match_parent” android: layout_height = “match_parent”>
12345678910111213141516171819 <androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerview"android:layout_width="0dp"android:layout_height="0dp"android:background="@android:color/darker_gray"tools:listitem="@layout/recyclerview_item"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:id="@+id/fab"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="16dp" /></androidx.constraintlayout.widget.ConstraintLayout>
** B8: Add RecycleViewAdapter **
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 public class WordListAdapter extends RecyclerView.Adapter<WordListAdapter.WordViewHolder> {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<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();}@Overridepublic int getItemCount() {if (mWords != null)return mWords.size();else return 0;}}
1 2 3 4 5 6 7 | Việc tạo Adapter thì cũng không có gì là khó lắm nên mình không đề cập sâu đến. Trong MainActivity thêm đoạn code RecyclerView recyclerView = findViewById(R.id.recyclerview); final WordListAdapter adapter = new WordListAdapter(this); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(new LinearLayoutManager(this)); |
** B9: Create a new activity: named NewwordActivity ** Code Xml:
12 <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns: 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” />
<Android Button: 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>
1 2 | Code Java: |
12 public class NewWordActivity extends AppCompatActivity {public static final String EXTRA_REPLY = “com.example.android.wordlistsql.REPLY”;
private EditText mEditWordView;
@Override public void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState); setContentView (R.layout.activity_new_word); mEditWordView = findViewById (R.id.edit_word);
123456789101112131415 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();}});}}
** B10: Data connection with UI **
1234567 Trong Main Activity :private WordViewModel mWordViewModel;mWordViewModel = new ViewModelProvider(this).get(WordViewModel.class);mWordViewModel.getAllWords().observe(this, new Observer<List<Word>>() {@Override public void onChanged ( @Nullable final List <Word> words) {adapter.setWords (words); }});
123456789101112131415161718192021222324 FloatingActionButton fab = findViewById(R.id.fab);fab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Intent intent = new Intent(MainActivity.this, NewWordActivity.class);startActivityForResult(intent, NEW_WORD_ACTIVITY_REQUEST_CODE);}});}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();}}
Through the 10 Steps above, you have succeeded in a small program using MVVM Model. RoomDatabase, and LiveData Android JetPack components. Do not know how to arrange layout or expression problem for the first time writing, I hope you can give me suggestions to improve, through this article is my little share about MVVM. Thank you for reading. Thank all
Link Docs reference: https://developer.android.com/jetpack/docs/guide Link Code Lab: https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0