我已经阅读了很多stackoverflow的文章和其他关于它的博客文章,尝试了不同的解决方案来解决类似(但不相同)的问题。
我得到以下错误消息:
Process: com.myapp, PID: 23553
java.lang.RuntimeException: Unable to invoke no-args constructor for androidx.arch.core.internal.SafeIterableMap$SupportRemove<androidx.lifecycle.Observer<? super java.lang.Integer>, androidx.lifecycle.LiveData$ObserverWrapper>. Registering an InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:228)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:186)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.Gson.fromJson(Gson.java:932)
at com.google.gson.Gson.fromJson(Gson.java:897)
at com.google.gson.Gson.fromJson(Gson.java:846)
at com.myapp.ui.customGame.CustomGameViewModel.initialize(CustomGameViewModel.kt:46)
我正在写一个应用程序,作为学习科特林编程和制作应用程序的项目,我对制作应用程序和科特林相对较新,请记住,如果你看到我犯了愚蠢的错误;)。在我的应用程序中,我有一个活动,其中包含一个片段,让您选择排球比赛的设置(CustomGameSetupFragment)。这些设置包括简单的事情,比如球队最终获胜的分数,名字等。在选择和保存设置后,应用程序创建一个应用设置的游戏类对象,并将其保存到房间数据库中。我的数据库表中的实体基本上包含一个ID、一些其他信息和游戏对象的JSON字符串(通过Google的Gson包创建)。然后,该活动将片段替换为允许您计算游戏得分并查看名称和内容的片段(CustomGameFragment)。新片段创建了一个ViewModel对象,然后该对象再次从数据库中读取游戏,选择最后保存的实体,然后尝试从保存的JSON字符串中重新创建游戏对象。这是通过执行:
val gameType = object: TypeToken<Game>() {}.type
this.game = Gson().fromJson<Game>(
gameRepo.ongoingGameData[0].gameJson,
gameType
//Game::class.java // this was an other method I tried. Also didnt work
)
以前,Game类不包含LiveData/MutableLiveData,但这导致必须将属性强制转换到ViewModel类中的LiveData/MutableLiveData中,这导致了很多bug。但成功了!。我重构了Game类,所以它主要是LiveData/MutableLiveData属性(我需要的是LiveData),因为在CustomGameFragment及其ViewModel中,我可以直接观察游戏的属性。但是在我重构了这个类之后,Gson再也不能加载它了。
我不确定这仅仅是因为我使用了LiveData,他们不知何故需要在ViewModel中隐式获取的上下文或viewLifeCylceOwner之类的东西。
package com.myapp.data
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "gameData_table")
data class GameData(
@PrimaryKey(autoGenerate = true) val id: Int?,
@ColumnInfo(name = "gid") val gid: String?,
@ColumnInfo(name = "is_over") val isOver : Boolean?,
@ColumnInfo(name = "team1_name") val team1Name: String?,
@ColumnInfo(name = "team2_name") val team2Name: String?,
@ColumnInfo(name = "team1_sets") val team1Sets: Int?,
@ColumnInfo(name = "team2_sets") val team2Sets: Int?,
@ColumnInfo(name = "total_sets") val totalSets: Int?,
@ColumnInfo(name = "game_json") val gameJson : String?
)
道:
package com.myapp.data
import androidx.room.*
@Dao
interface GameDao {
@Query("SELECT * FROM gameData_table")
suspend fun getAll(): List<GameData>
@Query("SELECT * FROM gameData_table WHERE id = (:id)")
fun loadAllByIds(id: Array<Int>): List<GameData>
@Query("SELECT * FROM gameData_table WHERE is_over = 0")
suspend fun getOngoing() : List<GameData>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(vararg game: GameData)
@Delete
suspend fun delete(game: GameData)
@Query("DELETE FROM gameData_table WHERE is_over = 0")
suspend fun deleteOngoing()
@Query("UPDATE gameData_table SET game_json = (:json) WHERE gid = (:gameId)")
suspend fun updateJSON(json: String, gameId : String)
}
数据库:
package com.myapp.data
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [GameData::class], version = 1, exportSchema = false)
abstract class GameDatabase : RoomDatabase() {
abstract fun gameDao() : GameDao
companion object {
//Singleton pattern to prevent multiple instances of the database
@Volatile
private var INSTANCE: GameDatabase? = null
fun getDatabase(context: Context) : GameDatabase {
return INSTANCE ?: synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
GameDatabase::class.java,
"game_database"
).build()
INSTANCE = instance
return instance
}
}
}
存储库:
package com.myapp.data
import kotlinx.coroutines.runBlocking
class GameRepository (private val gameDao: GameDao){
val allGameData: List<GameData> = runBlocking { gameDao.getAll()}
val ongoingGameData : List<GameData> = runBlocking { gameDao.getOngoing() }
suspend fun insert(gameData : GameData){
gameDao.insertAll(gameData)
}
suspend fun deleteOngoing() {
gameDao.deleteOngoing()
}
suspend fun updateGame(gameData : GameData){
gameDao.updateJSON(gameData.gameJson!!, gameData.gid!!)
}
}
package com.myapp.game
import android.app.Application
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.myapp.data.GameData
import com.myapp.values.Values
import com.google.gson.Gson
class Game {
/*
No live data needed or possible?
*/
private var sets : MutableList<Set>
private val pointGap : Int = Values.DEFAULT_POINT_GAP
private val gid : String = this.toString()
/*
Live data needed or possible
*/
// private MutableLiveData
private var _team1Name : MutableLiveData<String> = MutableLiveData(Values.DEFAULT_TEAM1_NAME)
(more strings ...)
private var _setWinScore : MutableLiveData<Int> = MutableLiveData(Values.DEFAULT_WIN_SCORE)
(...)
// public LiveData
val team1Name : LiveData<String>
(more strings ...)
val setWinScore : LiveData<Int>
(...)
init{
team1Name = _team1Name
(more strings ...)
setWinScore = _setWinScore
(...)
}
constructor(gameSettings: GameSettings = GameSettings()){
this._team1Name.value = gameSettings.team1Name
(more strings...)
this._setWinScore.value = gameSettings.setWinScore
(...)
}
}
还是我做了什么愚蠢的错误?
提前感谢您牺牲了时间!
附:我不是英语母语的人,所以很抱歉语法和拼写不好。
下面展示了如何反序列化LiveData
,但是在您的用例中,将Game
数据共享为ViewModel
可能更合适?参见Android开发者页面。
当没有自定义或内置类型适配器匹配时,Gson使用基于反射的类型适配器。问题是您要求Gson将JSON反序列化为LiveData
。如果您查看LiveData
的源代码,您将看到它有多个私有字段,对于其中一个字段的类型,Gson无法创建实例。
一般来说,不鼓励对任何第三方类(这里是LiveData
)使用GSON的基于反射的序列化或反序列化,因为这样您就依赖于它们的内部实现细节,这些细节可能会在任何时候发生变化。
class LiveDataTypeAdapterFactory implements TypeAdapterFactory {
public static final LiveDataTypeAdapterFactory INSTANCE = new LiveDataTypeAdapterFactory();
private LiveDataTypeAdapterFactory() { }
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<?> rawType = type.getRawType();
// Only handle LiveData and MutableLiveData
if (rawType != LiveData.class && rawType != MutableLiveData.class) {
return null;
}
// Assumes that LiveData is never used as raw type but is always parameterized
Type valueType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0];
// Get the adapter for the LiveData value type `T`
// Cast TypeAdapter to simplify adapter code below
@SuppressWarnings("unchecked")
TypeAdapter<Object> valueAdapter = (TypeAdapter<Object>) gson.getAdapter(TypeToken.get(valueType));
// Is safe due to `type` check at start of method
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) new TypeAdapter<LiveData<?>>() {
@Override
public void write(JsonWriter out, LiveData<?> liveData) throws IOException {
// Directly write out LiveData value
valueAdapter.write(out, liveData.getValue());
}
@Override
public LiveData<?> read(JsonReader in) throws IOException {
Object value = valueAdapter.read(in);
return new MutableLiveData<>(value);
}
};
return adapter;
}
}
(注意,这不会保留LiveData
的观察者)
然后,可以使用GSONBuilder
创建GSON
实例,并注册工厂:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(LiveDataTypeAdapterFactory.INSTANCE)
.create();
当反序列化game
时,不需要使用typeToken
,直接使用该类也可以。typeToken
用于泛型类型。
理想情况下,还可以创建TypeAdapterFactory
,以便MutableList
不依赖其内部实现。
该片段仅停止(当您从详细信息返回到列表时恢复)。它不是分离的,不是被破坏的,它的视图仍然存在,等等,所以observeForever-observer可以很好地更新UI(即使不可见)。 但是我想使用普通的“添加并忘记”方法,在这里我不需要删除观察者。 有人知道为什么不叫观察者吗?如前所述,片断只是停止了,视图仍然存在(当更改发生在相同的活动中而不是在不同的活动中时,才调用它)。 由于正在工作,我只
我已经将我们的一个Android项目从旧的Groovy和和。 虽然“它在我的机器上工作”——特别是:我从Groovy转换为静态编程语言的原始项目在其原始目录下工作正常——但我所有的同事在克隆回购时都无法打开该项目。重要的是,当我克隆回购到其他目录时,我也不能自己打开项目(在同一台机器上安装相同的AS)。所以,我怀疑有一些额外的细节丢失在一些配置文件,但我似乎无法找出哪个... 详情: 当我只使用
我正在学习静态编程语言,所以我创建了一个带有main方法的文件,里面有简单的println方法调用,然后按下运行为静态编程语言应用程序,得到了异常错误:无法找到或加载主类learn.varabls.变量Kt-1/>ClassNotFoundExc0019:learn.varabls.变量Kt错误:无法找到或加载主类Kt导致:java.lang.ClassNotFoundExc0019: Kt
我有一个AS3/gradle plugin 3项目,我们最近在其中引入了一个本地简单的注释处理器。适用于gradles annotationProcessor Dependency指令。 我开始添加Kotlin代码,并将所有annotationProcessor指令移动到kapt指令。Glide和logan square对Kotlin使用注释处理器没有问题,但我们当地的AP项目无法由kapt执行:
我正面临一个让我发疯的问题。 我尝试在onDestroyView()中删除观察者,它可以工作,观察者被删除,但是一旦片段再次进入onActivityCreated()中,并且我观察到livedata,观察者就会立即被触发...我总是使用“ViewLifecyCleOwner”作为所有者。 如果我返回实例化片段,是否有任何方法可以取消liveData的执行? 我所有的frag都扩展了ScopeFra
我对科特林来说是全新的。我已经安装了kotlin插件来eclipse。我在下面的一个教程中找到了一个简单的例子。问题是,当我运行项目时,我收到了以下声明的错误。 为了解决这个问题,我试图运行项目作为kotlin应用程序,但我找不到这个选项。 请让我知道如何修复此错误? 代码: 错误: 更新: 为了解决这个问题,我完全按照本教程中的内容,安装了最新版本的eclipse PHOTON,但问题仍然存在。