我正在开发一个 Android Studio 项目,其中使用单例类来跟踪数据(我已经对单例对象的优缺点进行了研究,我认为这是我的项目的最佳解决方案) 。然而,我遇到了一些问题,这些问题似乎指向我的单例对象,对此我无法在 StackOverflow 或其他开发者论坛上找到任何好的解决方案。
注意:在使用这个单例类之前我不会实例化它,因为如果我正确理解 Kotlin 单例,你就不必实例化它。
for (item in items) {
with(item.device) {
if (name == "BLE_DEVICE") {
count++
Data.addresses.add(address) >>> This is where I call the singleton object <<<
}
}
}
var sharedPref: SharedPreferences? = MainActivity().getSharedPreferences("MySharedPreferencesFile", Context.MODE_PRIVATE)
fun saveSharedPreferences() {
for ((key, value) in names) {
if (sharedPref != null) {
if (!sharedPref?.contains(key)!!) {
sharedPref
?.edit()
?.putString(key, value)
?.apply()
}
}
}
}
供参考:
a.这是我的堆栈跟踪中的重要行...
2022-08-30 16:07:05.422 9946-9946/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.punchthrough.blestarterappandroid, PID: 9946
>>> java.lang.ExceptionInInitializerError
>>> at com.punchthrough.blestarterappandroid.ScanResultAdapter.getItemCount(ScanResultAdapter.kt:62)
...
...
>>> Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
>>> at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:174)
>>> at com.punchthrough.blestarterappandroid.Data.<clinit>(Data.kt:35)
at com.punchthrough.blestarterappandroid.ScanResultAdapter.getItemCount(ScanResultAdapter.kt:62)
b.这是我用于跟踪数据的单例类。
object Data {
// Format: <Device Address, Name>
// Used for keeping a record of all the devices, keeping
// duplicate advertisements off the screen, and saving the
// user-inputted names to the MAC address
var names: MutableMap<String, String> = mutableMapOf()
// Format: <Device Address>
// Used for keeping a count of how many views should be populated
// in the RecyclerView
var addresses = mutableSetOf<String>()
// TODO: Fix this line to resolve an initialization error
var sharedPref: SharedPreferences? = MainActivity().getSharedPreferences("MySharedPreferencesFile", Context.MODE_PRIVATE)
fun saveSharedPreferences() {
for ((key, value) in names) {
if (sharedPref != null) {
if (!sharedPref?.contains(key)!!) {
sharedPref
?.edit()
?.putString(key, value)
?.apply()
}
}
}
}
}
永远不要实例化 Activity。它根本不起作用,也不能用于任何有用的目的。活动充满了操作系统为您创建活动时设置的属性。如果您自己实例化它,您将拥有一个充满 null 属性的死对象,这些属性不应为 null。
Kotlin 的内置单例 (
object
) 不适合依赖于其他东西的单例,因为它没有构造函数供您调用来初始化依赖项。
在这种情况下,您的单例必须依赖于
Context
才能使用共享首选项,因此 Kotlin object
不适合。
这是创建需要上下文的单例的方法:
class Data private constructor(val context: Context) {
companion object {
private var instance: Data? = null
fun getInstance(context: Context): Data {
return instance ?: synchronized(this) {
instance ?: Data(context.applicationContext).also { instance = it }
}
}
}
val names: MutableMap<String, String> = mutableMapOf()
val sharedPref: SharedPreferences = context.getSharedPreferences("MySharedPreferencesFile", Context.MODE_PRIVATE)
fun saveSharedPreferences() { // I simplified this function a bit
sharedPref.edit {
for ((key, value) in names) {
if (!sharedPref.contains(key)) {
putString(key, value)
}
}
}
}
}
每次使用它时,您都需要将 Context 传递给
Data.getInstance
。
顺便说一句,我强烈反对将
var
与 MutableList 或 MutableSet 结合使用。它会引发错误,因为外部类不知道他们是否应该将集合替换为新实例,或者当他们想要进行更改时是否应该就地对其进行变异。其他类无法知道缓存列表的副本是否安全,因为它可能会或可能不会根据其他类正在执行的操作而从它们下面更改。
真的,我不建议从任何类公开公开 MutableCollection(MutableList 或 MutableSet)或
var
只读 Collection。当外部类可以更改类在内部使用的集合或由多个类使用的集合时,它会让您面临许多可能类型的错误。相反,我会将集合设为私有并公开间接修改它们的函数,例如 addName()
。