Android

[Android] MVVM 패턴 적용 - ACC 예제

팽팽 2022. 10. 11. 23:01

- DAO ? DB의 data에 접근하기 위한 객체 

 

 

< viewmodel 적용 이전>

Todo.class (Room)

: id, title이 정의된 데이터

package org.techtown.myapplication;

import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity //룸에서 사용할수 있는 entity
public class Todo {
    @PrimaryKey(autoGenerate = true)
    private int id;
    private String title;

    public Todo(String title) {
        this.title = title;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Todo{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

TodoDao.interface

: Todo에 접근하기 위한 데이터 접근 객체

: 조회, 삽입 , 삭제등의 메서드를 정의함 getAll() -> Todo의 모든 값 조회

: LiveData로 지정함

package org.techtown.myapplication;

import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import java.util.List;

//Todo에 접근하기 위한 Data access object
@Dao
public interface TodoDao {
    //Todo에 어떤 동작 제공할지 정의
    @Query("SELECT * FROM Todo")
    LiveData<List<Todo>> getAll();

    @Insert
    void insert(Todo todo);

    @Update
    void update(Todo todo);

    @Delete
    void delete(Todo todo);

}

AppDatabase.class

: RoomDB 상속후 tododao 구체화

package org.techtown.myapplication;

import androidx.room.Database;
import androidx.room.RoomDatabase;
@Database(entities = {Todo.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract TodoDao todoDao(); // todo조작
}

 

MainActivity.class

: observe를 통해 값이 바뀌면 갱신함

package org.techtown.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.room.Room;

import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    private EditText mTodoEditText;
    private TextView mResultTextview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTodoEditText = findViewById(R.id.todoEdit);
        mResultTextview = findViewById(R.id.result_text);


        final AppDatabase db = Room.databaseBuilder(this,AppDatabase.class,"todo-db")
                .build();

        //UI 갱신
        db.todoDao().getAll().observe(this,todos -> {
            mResultTextview.setText(todos.toString());
        });


        // 버튼 클릭시 db에 insert
        findViewById(R.id.addBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new InsertAsyncTask(db.todoDao())
                        .execute(new Todo(mTodoEditText.getText().toString()));
            }
        });
    }

    private static class InsertAsyncTask extends AsyncTask<Todo, Void, Void> {
        private TodoDao mTodoDao;

        public InsertAsyncTask(TodoDao todoDao){
            this.mTodoDao = todoDao;
        }
        //Room을 비동기로 사용
        @Override
        protected Void doInBackground(Todo... todos) {
            mTodoDao.insert(todos[0]);
            return null;
        }
    }


}

 

< ViewModel 적용 후 >

MainActivity.class

: 원래는 db 선언, 버튼 클릭시 insert가 MainActivity에 선언되어 있었지만,

UI와 기능을 분리하기 위해

MainActivity에서 viewprovider를 선언하고 따로 선언된 MainViewModel의 메서드를 불러옴

observe()로 변하는 ui를 관찰함

package org.techtown.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.room.Room;

import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    private EditText mTodoEditText;
    private TextView mResultTextview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTodoEditText = findViewById(R.id.todoEdit);
        mResultTextview = findViewById(R.id.result_text);
		
        //ViewModelPrivider로 ViewModel 사용 가능
        MainViewModel viewModel = new ViewModelProvider(this)
                .get(MainViewModel.class);

        // getall시 결과가 변경될때마다 todo로 들어옴
        // 관찰하다가 ui 갱신
        viewModel.getAll().observe(this, new Observer<List<Todo>>() {
            @Override
            public void onChanged(List<Todo> todos) {
                mResultTextview.setText(todos.toString());
            }
        });

        // 버튼 클릭시 db에 insert
        // 값이 안 바뀔수도 있는 경우엔 ui갱신을 하지 않아도 됨
        findViewById(R.id.addBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                viewModel.insert(new Todo(mTodoEditText.getText().toString()));



            }
        });
    }


//                원래 아래 코드를 onClick()에 썼다면 aac패턴에서는 onChanged안에 넣기
//                mResultTextview.setText(db.todoDao().getAll().toString());

}

 

MainViewModel.class

: 여기서 db선언, LiveData 선언, insert() 메서드 선언을 따로 해줌

getAll() 을 호출하면 tododao의 getall을 호출해줌

package org.techtown.myapplication;

import android.app.Application;
import android.os.AsyncTask;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import androidx.room.Room;

import java.util.List;

public class MainViewModel extends AndroidViewModel {
    private AppDatabase db;
    public MainViewModel(@NonNull Application application) {
        super(application);

               db = Room.databaseBuilder(application, AppDatabase.class,"todo-db")
               .build();
    }

        public LiveData<List<Todo>> getAll() {
            return db.todoDao().getAll();
        }

        public void insert(Todo todo) {
            new InsertAsyncTask(db.todoDao())
                    .execute(todo);
        }

    private static class InsertAsyncTask extends AsyncTask<Todo, Void, Void> {
        private TodoDao mTodoDao;

        public InsertAsyncTask(TodoDao todoDao){
            this.mTodoDao = todoDao;
        }
        @Override
        protected Void doInBackground(Todo... todos) {
            mTodoDao.insert(todos[0]);
            return null;
        }
    }
//
//    private AppDatabase db;
//
//    public MainViewModel(@NonNull Application application) {
//        super(application);
//        // db 객체 생성
//       db = Room.databaseBuilder(application, AppDatabase.class,"todo-db")
//               .build();
//
//        //.allowMainThreadQueries() //백그라운드 말고 메인 스레드에서 db사용
//
//    public LiveData<List<Todo>> getAll() {
//            return db.todoDao().getAll();
//        }
//    public void insert(Todo todo) {
//
//        }
//    }


}

 -> MainActivity에는 ui관련 코드만 남게 됨

 

 

<DataBinding 적용후>