이번 포스팅에서는 SharedPreferences를 사용하는 법에 대해 알아보도록 하겠습니다.
데이터를 앱에 저장하는 방법
데이터를 앱에 저장하는 방법에는 크게 세가지가 있습니다.
파일 I/O (내부 또는 외부 저장소)
접근 권한을 획득하고 파일을 열었다 닫았다 하는 수고가 필요함
관계형 데이터베이스
SQLite 등을 이용해 복잡한 관계형 데이터를 저장할 수 있음
간단한 데이터를 저장할거라면 구축과 관리에 많은 시간과 노려이 요구됨
SharedPreference
Key/Value 형태로 이용함
내부적으로는 XML 파일로 저장됨
파일을 열고 닫을 필요 없이 핸들러를 만들어서 간편하게 사용가능함
SharedPreferences 는 보통 복잡한 데이터를 기록하기보다는, 게임의 환경설정이라든지 그런 단순한 내용을 저장하는데 적절한 저장공간이라고 생각하면 됩니다. 그래서 이번에는 SharedPreferences
를 사용해서 게임설정값을 저장하고 복원하는 앱을 만들어보도록 하겠습니다.
SharedPreferences 사용법
화면 구성
위에서부터 순서대로 화면의 요소를 구성하는 여러가지 종류의 버튼을 배치하여 값을 변화시킬수 있도록 구성하였습니다. 앱이 처음 실행되었을 때 표시되는 기본값도 여기서 설정해줍니다. 그리고 마지막으로 SAVE버튼과 LOAD 동작을 하는 버튼 두개도 배치합니다.
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<?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"
tools:context= ".MainActivity" >
<TextView
android:id= "@+id/textView"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "50dp"
android:layout_marginEnd= "16dp"
android:text= "Game Settings"
android:textSize= "30sp"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toTopOf= "parent" />
<TextView
android:id= "@+id/textView2"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "40dp"
android:layout_marginEnd= "16dp"
android:text= "Graphic Quality"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/textView" />
<RadioGroup
android:id= "@+id/radio_graphics"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "20dp"
android:layout_marginEnd= "16dp"
android:orientation= "horizontal"
android:checkedButton= "@id/radioButton_medium"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/textView2" >
<RadioButton
android:id= "@+id/radioButton_low"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:text= "Low" />
<RadioButton
android:id= "@+id/radioButton_medium"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:text= "Medium" />
<RadioButton
android:id= "@+id/radioButton_high"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:text= "High" />
</RadioGroup>
<TextView
android:id= "@+id/textView3"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "40dp"
android:layout_marginEnd= "16dp"
android:text= "Music Volume"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/radio_graphics" />
<SeekBar
android:id= "@+id/seekBar_music"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "20dp"
android:layout_marginEnd= "16dp"
android:max= "100"
android:progress= "50"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/textView3" />
<TextView
android:id= "@+id/textView4"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "40dp"
android:layout_marginEnd= "16dp"
android:text= "SFX Volume"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/seekBar_music" />
<SeekBar
android:id= "@+id/seekBar_sfx"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "20dp"
android:layout_marginEnd= "16dp"
android:max= "100"
android:progress= "50"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/textView4" />
<TextView
android:id= "@+id/textView5"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "40dp"
android:layout_marginEnd= "16dp"
android:text= "Enable Vertical Sync"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/seekBar_sfx" />
<androidx.appcompat.widget.SwitchCompat
android:id= "@+id/switch_vsync"
android:layout_width= "match_parent"
android:layout_height= "wrap_content"
android:layout_marginStart= "16dp"
android:layout_marginTop= "30dp"
android:layout_marginEnd= "16dp"
android:checked= "true"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/seekBar_sfx" />
<Button
android:id= "@+id/button_save"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:layout_marginTop= "70dp"
android:text= "SAVE"
app:layout_constraintEnd_toStartOf= "@+id/button_load"
app:layout_constraintHorizontal_bias= "0.5"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/textView5" />
<Button
android:id= "@+id/button_load"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:layout_marginTop= "70dp"
android:text= "LOAD"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintHorizontal_bias= "0.5"
app:layout_constraintStart_toEndOf= "@+id/button_save"
app:layout_constraintTop_toBottomOf= "@+id/textView5" />
</androidx.constraintlayout.widget.ConstraintLayout>
버튼에 클릭리스너 설정
View Binding을 적용하고 SAVE 버튼을 눌렀을 때는 savePref
, LOAD 버튼을 눌렀을때는 loadPref
를 실행하도록 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MainActivity : AppCompatActivity () {
private lateinit var binding : ActivityMainBinding
override fun onCreate ( savedInstanceState : Bundle ?) {
super . onCreate ( savedInstanceState )
binding = ActivityMainBinding . inflate ( layoutInflater )
setContentView ( binding . root )
binding . buttonSave . setOnClickListener {
savePref ()
}
binding . buttonLoad . setOnClickListener {
loadPref ()
}
}
private fun savePref () {
}
private fun loadPref () {
}
}
접근키 작성
SharedPreferences는 Key-Value 형태로 값을 저장하고 불러오기 때문에 이 때 사용할 Key 값을 companion object
로 준비해서 편하게 사용할 수 있도록 준비합니다.
1
2
3
4
5
6
7
companion object {
private const val KEY _PREFS = "game_settings"
private const val KEY _GRAPHIC = "graphic_quality"
private const val KEY _MUSIC = "music_volume"
private const val KEY _SFX = "sfx_volume"
private const val KEY _VSYNC = "vertical_sync"
}
savePref 작성
데이터를 저장하는 부분을 작성합니다. 핸들러를 만들고 Editor 객체를 만들어서 put
함수로 Key-Value 쌍을 저장하도록 했습니다. 버튼의 값은 개체가 가진 프로퍼티로 가져올 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
private fun savePref () {
val sharedPreferences = getSharedPreferences ( KEY_PREFS , Context . MODE_PRIVATE )
val editor = sharedPreferences . edit ()
editor . putInt ( KEY_GRAPHIC , binding . radioGraphics . checkedRadioButtonId )
editor . putInt ( KEY_MUSIC , binding . seekBarMusic . progress )
editor . putInt ( KEY_SFX , binding . seekBarSfx . progress )
editor . putBoolean ( KEY_VSYNC , binding . switchVsync . isChecked )
editor . apply ()
Toast . makeText ( applicationContext , "Game settings has saved" , Toast . LENGTH_SHORT ). show ()
}
loadPref() 작성
저장된 데이터를 불러오는 부분을 작성합니다. 저장할때와 마찬가지로 핸들러를 만들고 저장할때 사용한 키와 동일한 키를 사용해서 get
함수로 값을 불러온 뒤 그 값을 버튼에 반영합니다. 이 때 contains 를 사용해서 SharedPreferences 데이터가 존재하는지를 우선 확인하고 작업을 수행하도록 했습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private fun loadPref () {
val sharedPreferences = getSharedPreferences ( KEY_PREFS , Context . MODE_PRIVATE )
if ( sharedPreferences . contains ( KEY_GRAPHIC )) {
val graphicValue = sharedPreferences . getInt ( KEY_GRAPHIC , 0 )
val musicValue = sharedPreferences . getInt ( KEY_MUSIC , 50 )
val sfxValue = sharedPreferences . getInt ( KEY_SFX , 50 )
val vsyncValue = sharedPreferences . getBoolean ( KEY_VSYNC , true )
binding . radioGraphics . check ( graphicValue )
binding . seekBarMusic . progress = musicValue
binding . seekBarSfx . progress = sfxValue
binding . switchVsync . isChecked = vsyncValue
Toast . makeText ( applicationContext , "Game settings has loaded" , Toast . LENGTH_SHORT ). show ()
}
이렇게 SharedPreferences를 써서 앱의 데이터를 저장하고 불러오는 법에 대해 알아보았습니다.
VIDEO