티스토리 뷰

이건 Room 라이브러리의 공부 정리가 아니고 더더욱 활용법이나 어떤 해답을 줄 수 있는 글이 아니다. 단지 안드로이드 뉴비 개발자의 처절한 발버둥을 기록해놓은 글에 불과하다.

 

며칠 전 <Room 라이브러리 적용하기>라고 호기롭게 적었지만 RecyclerView를 넣고 데이터를 불러오려고 하니 전혀 불러와지지 않았다.

 

이 글은 그 처절한 발버둥을 기록해놓은 기록에 불과하고 실제로 원리가 어떤 식으로 이루어지는지 알지 못 한다. 나름대로 알아보려고 했으니 어림짐작만 있을 뿐 정확히 무엇이 문제였는지 알아내지 못 했다. (훗날 다시 이 글을 보았을 때 그 원인을 정확히 알아냈길 바라며..)

 

오류는 큰 오류하나 작은 오류 하나가 있다.

 

큰 오류 : 데이터베이스에서 데이터를 가져왔지만 RecyclerAdapter에서 todos를 불러오는 시점에서 null이어서 문제가 생김

작은 오류 : Date 타입의 데이터를 Room을 통해서 넣는 중에 타입 에러가 생김.

 

이전 게시글에서 변화된 부분만 코드로 따로 뽑아내었다. 어떤 부분을 바꿔서 해결되었는지 모르겠다. 그래서 이 부분은 오늘 내일 중에 하나하나 뜯어보면서 알아낼 것이다.

 

우선 작은 오류를 해결한 방법 부터 설명해야겠다.

 

<이슈 1> Date 타입의 데이터를 Room을 통해서 넣는 중에 타입 에러가 생김.

1. Todo

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.Date;
 
@Entity(tableName = "todos")
public class Todo {
    @PrimaryKey(autoGenerate = true)
    public long id;
    public String title;
    public String content;
    public Date date;
    public Boolean isCompleted;
    
    ...
}
cs

Todo의 데이터 중에서 long, String, Boolean과 같은 타입의 데이터는 별 문제가 없는데 Date와 같은 타입의 경우에는 별도의 Conveter가 필요하다고 한다.

2. Converters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import androidx.room.TypeConverter;
import java.util.Date;
 
public class Converters {
    @TypeConverter
    public static Date fromTimestamp(Long value) {
        return value == null ? null : new Date(value);
    }
 
    @TypeConverter
    public static Long dateToTimestamp(Date date) {
        return date == null ? null : date.getTime();
    }
}
cs

단순하게 Date를 Long으로 Long을 Date 타입으로 바꾸는 Converter를 작성한다.

 

3. AppDatabase

1
2
3
4
5
@Database(entities = {Todo.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
    ...
}
cs

작성하는데서 그치면 안 되고 AppDatabase에다가 앞으로 Date타입이 들어올 경우 이 Converter를 사용해서 변환하겠다는 약속을 어노테이션으로 작성해줘야 한다.

 

<이슈2> 데이터베이스에서 데이터를 가져왔지만 RecyclerAdapter에서 todos를 불러오는 시점에서 null이어서 문제가 생김

1. TodoAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
 
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
 
import com.weekyear.todolist.models.Todo;
 
public class TodoAdapter extends ListAdapter<Todo, TodoAdapter.ItemViewHolder> {
 
    public TodoAdapter() {
        super(Todo.DIFF_CALLBACK);
    }
 
    @NonNull
    @Override
    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(android.R.layout.simple_list_item_checked, parent, false);
        return new TodoAdapter.ItemViewHolder(view);
    }
 
    @Override
    public void onBindViewHolder(@NonNull TodoAdapter.ItemViewHolder holder, int position) {
        Todo todo = getItem(position);
        holder.title.setText(todo.title);
    }
 
    class ItemViewHolder  extends RecyclerView.ViewHolder {
        public TextView title;
 
        public ItemViewHolder(@NonNull View itemView) {
            super(itemView);
            title = itemView.findViewById(android.R.id.text1);
        }
    }
}
cs

이런 저런 방법을 찾다가 RecyclerAdapter 대신에 ListAdapter를 활용한 방법이 있어서 써봤는데 이 방법이 오류를 해결한 것 같지 않다..그래서 다시 RecyclerAdapter로 바꿔보는 작업을 해보려고 한다.

2. Todo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Entity(tableName = "todos")
public class Todo {
    ...
 
    public static DiffUtil.ItemCallback<Todo> DIFF_CALLBACK = new  DiffUtil.ItemCallback<Todo>() {
        @Override
        public boolean areItemsTheSame(@NonNull Todo oldItem, @NonNull Todo newItem) {
            return oldItem.id == newItem.id;
        }
 
        @Override
        public boolean areContentsTheSame(@NonNull Todo oldItem, @NonNull Todo newItem) {
            return oldItem.equals(newItem);
        }
 
    };
 
    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        Todo myEntity = (Todo) obj;
        return myEntity.id == this.id && myEntity.title == this.title;
    }
}
cs

TodoAdapter에서 생성자에서 DIFF_CALLBACK을 사용해주기 위해서 여기서 넣어줬지만 정확히 이 코드가 무엇을 수행하는지 모르겠다.

3. MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    viewModel.delete();
    Todo[] initTodos = {
            new Todo("1번 제목""1번 내용"),
            new Todo("2번 제목""2번 내용"),
            new Todo("3번 제목""3번 내용"),
            new Todo("4번 제목""4번 내용"),
            new Todo("5번 제목""5번 내용"),
            new Todo("6번 제목""6번 내용"),
    };
    for (int i = 0; i < initTodos.length; i++) {
        viewModel.save(initTodos[i]);
    }
    viewModel.getAll().observe(this, todos -> adapter.submitList(todos));
    RecyclerView recyclerView=findViewById(R.id.todoRecyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(adapter);
}
cs

이 코드를 통해 해결한 것 같다고 생각한다.

데이터베이스에서 todo 타입의 데이터를 불러오는 과정이 백그라운드로 이루어지니 제때 todos를 불러오지 않은 채(null 상태)로 Adapter에 반영되었던게 문제였는데

데이터 바인딩의 observe를 활용하면 이후에 todos 변경에 따라서 adapter에 변경사항이 반영되어서 해결되었다고 판단되었다.

 

결과물

 

결론 및 이후 활동

  1. Room 라이브러리 활용을 위해 Room 라이브러리를 좀더 공부해볼 필요가 있을 것 같음
  2. 데이터베이스 저장과 불러오는 과정이 백그라운드 스레드에서 이루어지다보니 스레드 관련 메서드 동작 방식을 정확히 이해할 필요가 있음 => 추후에 자바 스레드에 관해 제대로 공부해서 블로그 포스팅 할 것!!!
  3. RecyclerView에 대한 이해가 충분히 되지 않아서 적지 않아도 될 코드를 적은 것 같다. 우선 RecyclerView에 대해서 다시 책을 읽고 공식 문서를 읽어서 코드를 제대로 수정하려고 한다.

 

반응형

'개발 일기 > To-do 리스트' 카테고리의 다른 글

[개발 일기] ActionBar 숨기기  (0) 2020.12.14
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함