티스토리 뷰
이건 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에 변경사항이 반영되어서 해결되었다고 판단되었다.
결과물
결론 및 이후 활동
- Room 라이브러리 활용을 위해 Room 라이브러리를 좀더 공부해볼 필요가 있을 것 같음
- 데이터베이스 저장과 불러오는 과정이 백그라운드 스레드에서 이루어지다보니 스레드 관련 메서드 동작 방식을 정확히 이해할 필요가 있음 => 추후에 자바 스레드에 관해 제대로 공부해서 블로그 포스팅 할 것!!!
- RecyclerView에 대한 이해가 충분히 되지 않아서 적지 않아도 될 코드를 적은 것 같다. 우선 RecyclerView에 대해서 다시 책을 읽고 공식 문서를 읽어서 코드를 제대로 수정하려고 한다.
'개발 일기 > To-do 리스트' 카테고리의 다른 글
[개발 일기] ActionBar 숨기기 (0) | 2020.12.14 |
---|