728x90
아래의 코드들을 실행시키기 위한 MainActivity.kt와 activity_main.xml
더보기
class MainActivity : AppCompatActivity() {
private val settingSwitch: SwitchCompat by lazy { findViewById(R.id.notification_setting_switch) }
private val settingTv: TextView by lazy { findViewById(R.id.textView) }
private val alarmSetting: NotificationSetting by lazy { NotificationSetting.getInstance(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
settingSwitch.setOnCheckedChangeListener { compoundButton, checked ->
alarmSetting.isEnable = checked
settingTv.text = alarmSetting.isEnable.toString()
}
}
}
<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">
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/notification_setting_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="알림 설정"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/notification_setting_switch" />
</androidx.constraintlayout.widget.ConstraintLayout>
싱글톤: 하나의 클래스가 오직 하나의 객체 인스턴스만 가지는 것
가정1 : 불필요한 객체 생성
- 알람에 대한 설정값을 관리하는 SharedPreferences가 있습니다.
- 이 SharedPreferences는 한 곳에서만 사용되는 것이 아니기 때문에 재사용성을 위해 SharedPreferences를 관리하는 클래스를 만들고 싶습니다.
- 아래와 같이 간단하게 구현할 수 있습니다.
class NotificationSetting(private val context: Context) {
private val prefs: SharedPreferences =
context.getSharedPreferences(SETTING_PREFERENCES_KEY, MODE_PRIVATE)
var isEnable: Boolean
get() = prefs.getBoolean(NOTIFICATION_KEY, false)
set(value) = prefs.edit().putBoolean(NOTIFICATION_KEY, value).apply()
companion object {
private const val SETTING_PREFERENCES_KEY = "setting_preferences_key"
private const val NOTIFICATION_KEY = "notification_key"
}
}
하지만, 여러개가 필요하지 않은 객체임에도 여러개가 생성될 수 있습니다.
가정2 : 메모리 누수
- 아래와 같이 간단하게 싱글톤으로 변경할 수 있습니다.
class NotificationSetting private constructor(private val context: Context) {
private val prefs: SharedPreferences =
context.getSharedPreferences(SETTING_PREFERENCES_KEY, MODE_PRIVATE)
var isEnable: Boolean
get() = prefs.getBoolean(NOTIFICATION_KEY, false)
set(value) = prefs.edit().putBoolean(NOTIFICATION_KEY, value).apply()
companion object {
private const val SETTING_PREFERENCES_KEY = "setting_preferences_key"
private const val NOTIFICATION_KEY = "notification_key"
private var instance: NotificationSetting? = null
fun getInstance(context: Context): NotificationSetting {
return instance ?: synchronized(this) {
instance ?: NotificationSetting(context).also {
instance = it
}
}
}
}
}
하지만, 외부에서 들어온 context(Activity)를 더이상 사용하지 않더라도 해당 싱글톤 객체에서 참조하고있기 때문에 메모리 누수가 발생합니다.
해결1 : context를 프로퍼티로 가지지 않는다.
- 프로퍼티로 가지지 않는다면, 생성자로 받은 context를 참조할 필요가 없어지고, 메모리 누수도 없습니다!
class NotificationSetting private constructor(context: Context) {
private val prefs: SharedPreferences =
context.getSharedPreferences(SETTING_PREFERENCES_KEY, MODE_PRIVATE)
var isEnable: Boolean
get() = prefs.getBoolean(NOTIFICATION_KEY, false)
set(value) = prefs.edit().putBoolean(NOTIFICATION_KEY, value).apply()
companion object {
private const val SETTING_PREFERENCES_KEY = "setting_preferences_key"
private const val NOTIFICATION_KEY = "notification_key"
private var instance: NotificationSetting? = null
fun getInstance(context: Context): NotificationSetting {
return instance ?: synchronized(this) {
instance ?: NotificationSetting(context).also {
instance = it
}
}
}
}
}
하지만, 근본적인 해결 방법이라고는 할 수 없겠죠…
해결2 : application context를 사용한다.
- context를 주입받는 싱글톤 객체가 메모리 누수 문제가 발생하는 이유는 프로그램 실행 중 사라질 수 있는 Context(ex. ActivityContext)를 참조할 수 있기 때문입니다.
- 그렇다면 최상위에 위치한 application context를 참조하면 됩니다.
class NotificationSetting private constructor(
context: Context
) {
private val prefs: SharedPreferences by lazy { context.getSharedPreferences(SETTING_PREFERENCES_KEY, MODE_PRIVATE) }
var isEnable: Boolean
get() = prefs.getBoolean(NOTIFICATION_KEY, false)
set(value) = prefs.edit().putBoolean(NOTIFICATION_KEY, value).apply()
companion object {
private const val SETTING_PREFERENCES_KEY = "setting_preferences_key"
private const val NOTIFICATION_KEY = "notification_key"
private var instance: NotificationSetting? = null
fun getInstance(context: Context): NotificationSetting {
return instance ?: synchronized(this) {
instance ?: NotificationSetting(context.applicationContext).also {
instance = it
}
}
}
}
}
참고
『안드로이드 프로그래밍 Next Step』, 255p~258p
728x90
댓글