ClassCastException in parcel - kotlin

When I try to use the ArrayList in Second_Activity, I get an error.
I can't find a solution.
I've try:
data class Site(
var site: String? = null,
var imageArray: ArrayList<Uri>? = null,
var date: String? = null
) : Parcelable {
constructor(parcel: Parcel) : this(parcel.readString(),
parcel.readArrayList(ClassLoader.getSystemClassLoader()) as ArrayList<Uri>?,
parcel.readString())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(site)
parcel.writeArray(arrayOf(imageArray))
parcel.writeString(date)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Site> {
override fun createFromParcel(parcel: Parcel): Site {
return Site(parcel)
}
override fun newArray(size: Int): Array<Site?> {
return arrayOfNulls(size)
}
}
}
var mArray = ArrayList<Site>()
I try to use for 'mArray[0].date'
This is the error I get:
java.lang.RuntimeException: Unable to start activity ComponentInfo{kr.ac.***.Second_Activity}:
java.lang.ClassCastException: kr.ac.***.First_Activity$Site cannot be cast to kr.ac.***.Second_Activity$Site
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2950)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3085)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6853)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)
I'm just not exactly sure how to read / write the ArrayList when parceling.
How can I use ArrayList in Second_Activity?
Thanks.

Related

Reading data from SQLCipher without Creating DBHelper Class; Error: Could not open database

I build two apps with the same package name:
In the first app, I use SQLiteCipher using "Pass_Phrase" (Version_1 DB) for CRUD operations using Java.
In the second app, I just wanted to read data (without making a DBHelper class) from SQLiteCipher and put it into Room (Version_2 DB) while Migrating using Kotlin.
Note: Changes would be affected on App Update.
I completed the first app and was stuck at sqliteCipherDataList() in the second app. Need your help. Thanks a lot!
Code: UserDB.kt
#Database(entities = [User::class], version = 2, exportSchema = false)
abstract class UserDB : RoomDatabase() {
abstract fun userDAO(): UserDAO
companion object {
private var instance: UserDB? = null
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
val db = SQLiteDatabase.openOrCreateDatabase("User.db", passphrase, null)
db.rawExecSQL("ATTACH DATABASE '${encryptedDbPath}' AS encrypted KEY '${passphrase}'")
db.rawExecSQL("SELECT sqlcipher_export('encrypted')")
db.rawExecSQL("DETACH DATABASE encrypted")
db.close()
val version = database.version
Log.v("Old version", version.toString() + "")
}
}
// return userList
fun sqliteCipherDataList(): List<User> {
val userList: MutableList<User> = ArrayList()
val db = SQLiteDatabase.openDatabase(encryptedDbPath, passphrase, null, SQLiteDatabase.OPEN_READONLY)
val cursor: Cursor = db.query("Contacts", null, null, null, null, null, null)
if (cursor.moveToFirst()) {
do {
#SuppressLint("Range")
val emails = User(cursor.getString(cursor.getColumnIndex("Email")))
userList.add(emails)
} while (cursor.moveToNext())
}
cursor.close()
db.close()
return userList
}
fun getDatabase(context: Context): UserDB? {
if (instance == null) {
synchronized(this) {
Log.d("UserDB", "Creating Database")
instance = Room.databaseBuilder(
context.applicationContext,
UserDB::class.java, "RoomDB"
)
.addMigrations(MIGRATION_1_2)
.allowMainThreadQueries()
.build()
}
}
return instance!!
}
}
}
Code: MainAcitvity.kt
class MainActivity : AppCompatActivity() {
private var userDB: UserDB? = null
private var userList: List<User>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
SQLiteDatabase.loadLibs(this)
val roomTextView = findViewById<TextView>(R.id.roomText)
userList = UserDB.sqliteCipherDataList()
Log.d("SQLiteCipherData", userList.toString())
userDB = UserDB.getDatabase(this)
userDB?.userDAO()!!.insertProductListToRoom(userList)
val u: User = userDB?.userDAO()!!.getProductFromRoom()[0]
Log.v("RoomDBData", u.Email)
roomTextView.text = u.Email
}
}
Exception:
2022-07-22 16:24:21.085 18889-18889/com.example.sqlitecipherjava E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.sqlitecipherjava, PID: 18889
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sqlitecipherjava/com.example.sqlitecipherjava.MainActivity}: net.sqlcipher.database.SQLiteDiskIOException: error code 10: Could not open database
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3114)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7050)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
Caused by: net.sqlcipher.database.SQLiteDiskIOException: error code 10: Could not open database
at net.sqlcipher.database.SQLiteDatabase.dbopen(Native Method)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2412)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1149)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1116)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1065)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1019)
at com.example.sqlitecipherjava.UserDB$Companion.sqliteCipherDataList(UserDB.kt:41)
at com.example.sqlitecipherjava.MainActivity.onCreate(MainActivity.kt:22)
at android.app.Activity.performCreate(Activity.java:7327)
at android.app.Activity.performCreate(Activity.java:7318)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3094)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:214) 
at android.app.ActivityThread.main(ActivityThread.java:7050) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 

Room cannot verify the data integrity. Looks like you've changed schema.... What's wrong?

I am using Room in my app with two entities. The whole implementation is below.
The Problem is, the given scheme is fixed, which means I do not change anything regarding DB. When I provide a new version of my app to Users over Google Play Console, I get the following issue in Cryshlytics although I did not change anything for DB, just edited UI or another things, which definetly nothing have to do with DB:
Fatal Exception: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
at androidx.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:154)
at androidx.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:135)
.......
Now I am not sure if I change the version of DB, it would work. What is wrong here?
BTW the DB is called from a Fragment like this
val mainDb: MainRepository by lazy { MainRepository(requireContext()) }
val stateDb: StateRepository by lazy { StateRepository(requireContext()) }
What's wrong here?
AppDatabase:
#Database(entities = [Main::class, State::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract val mainDao: MainDao
abstract val stateDao: StateDao
companion object {
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context): AppDatabase? =
INSTANCE ?: synchronized(AppDatabase::class) {
INSTANCE = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
MY_DB
).allowMainThreadQueries()
.build()
return INSTANCE
}
}
}
Dao:
#Dao
interface StateDao {
#Query("SELECT * FROM $STATE")
fun getAll(): List<State>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(state: State)
#Update
fun update(state: State)
#Query("DELETE FROM $STATE")
fun drop()
}
#Dao
interface MainDao {
#Query("SELECT * FROM $MAIN")
fun getAll(): List<Main>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(main: Main)
#Update
fun update(main: Main)
#Query("DELETE FROM $MAIN")
fun drop()
}
Main:
#Entity(tableName = MAIN)
data class Main(
#PrimaryKey #ColumnInfo(name = NUMBER) val number: Int,
#ColumnInfo(name = CARD) val car: String? = EMPTY,
#ColumnInfo(name = MODEL) val model: String? = EMPTY
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString(),
parcel.readString()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(number)
parcel.writeString(car)
parcel.writeString(model)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Main> {
override fun createFromParcel(parcel: Parcel): Main {
return Main(parcel)
}
override fun newArray(size: Int): Array<Main?> {
return arrayOfNulls(size)
}
}
}
State:
#Entity(tableName = STATE)
data class State(
#PrimaryKey #ColumnInfo(name = NUMBER) val number: Int,
#ColumnInfo(name = STATE) val state: String? = EMPTY
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(number)
parcel.writeString(question)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<State> {
override fun createFromParcel(parcel: Parcel): State {
return State(parcel)
}
override fun newArray(size: Int): Array<State?> {
return arrayOfNulls(size)
}
}
}
Repository:
class MainRepository(context: Context) {
private val mainDao = AppDatabase.getInstance(context)?.mainDao
fun getAll(): List<Main>? {
return mainDao?.getAll()
}
fun insert(main: Main) {
AsyncInsert(mainDao).execute(main)
}
fun update(main: Main) {
mainDao?.update(main)
}
fun drop() {
mainDao?.drop()
}
private class AsyncInsert(private val dao: MainDao?) : AsyncTask<Main, Void, Void>() {
override fun doInBackground(vararg p0: Main?): Void? {
p0[0]?.let { dao?.insert(it) }
return null
}
}
}
class StateRepository(context: Context) {
private val stateDao = AppDatabase.getInstance(context)?.stateDao
fun drop() {
stateDao?.drop()
}
fun getAll(): List<State>? {
return stateDao?.getAll()
}
fun insert(state: State) {
AsyncInsert(stateDao).execute(state)
}
fun update(state: State) {
stateDao?.update(state)
}
private class AsyncInsert(private val dao: StateDao?) : AsyncTask<State, Void, Void>() {
override fun doInBackground(vararg p0: State?): Void? {
p0[0]?.let { dao?.insert(it) }
return null
}
}
}
Now I am not sure if I change the version of DB, it would work. What is wrong here?
Changing the version would probably not work as the schema, as far as Room is concerned, has changed.
There is either a bug or the schema has been changed.
However, changing the version, would, with a Migration that does nothing (so as to not get a "no migration specified" error), then fail but importantly with an expected (what Room expects the schema to be according to the Entities) found (the schema that exists) discrepancy. This, if there is no bug, could then be used to ascertain what has been changed.

org.koin.core.error.InstanceCreationException: Could not create instance for [Factory:'com.pokemonsearch.presentation.search.SearchViewModel']

I am using Koin as depenedency Injection framework in my Kotlin Application. I am trying to save data to Room database using coroutines. I have a usecase "AddToFavourite" class which was calling from viewmodel. While running the app the app crash with below error. When I check I understand that koin could not find some dependency. Can any one please help me out with a solution. Please find the repository, usecase, viewmodel, database and di modules as below
Process: com.debin.pokemonsearch, PID: 16027
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.debin.pokemonsearch/com.debin.pokemonsearch.HomeActivity}: org.koin.core.error.InstanceCreationException: Could not create instance for [Factory:'com.debin.pokemonsearch.presentation.search.SearchViewModel']
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817`
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: org.koin.core.error.InstanceCreationException: Could not create instance for [Factory:'com.debin.pokemonsearch.presentation.search.SearchViewModel']
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:59)
at org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:36)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:87)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:214)
at org.koin.core.scope.Scope.get(Scope.kt:181)
at org.koin.android.viewmodel.ViewModelFactoryKt$defaultViewModelFactory$1.create(ViewModelFactory.kt:13)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at org.koin.android.viewmodel.ViewModelResolutionKt.get(ViewModelResolution.kt:21)
at org.koin.android.viewmodel.ViewModelResolutionKt.resolveInstance(ViewModelResolution.kt:10)
at org.koin.android.viewmodel.scope.ScopeExtKt.getViewModel(ScopeExt.kt:68)
at org.koin.android.viewmodel.scope.ScopeExtKt.getViewModel(ScopeExt.kt:56)
at org.koin.android.viewmodel.koin.KoinExtKt.getViewModel(KoinExt.kt:34)
at org.koin.android.viewmodel.ext.android.ViewModelStoreOwnerExtKt.getViewModel(ViewModelStoreOwnerExt.kt:66)
at com.debin.pokemonsearch.presentation.search.SearchFragment$$special$$inlined$viewModel$1.invoke(ViewModelStoreOwnerExt.kt:71)
at com.debin.pokemonsearch.presentation.search.SearchFragment$$special$$inlined$viewModel$1.invoke(Unknown Source:0)
at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:81)
at com.debin.pokemonsearch.presentation.search.SearchFragment.getViewModel(Unknown Source:2)
at com.debin.pokemonsearch.presentation.search.SearchFragment.observePokemon(SearchFragment.kt:39)
at com.debin.pokemonsearch.presentation.search.SearchFragment.onViewCreated(SearchFragment.kt:31)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:332)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2625)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2577)
at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2722)
at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:346)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1188)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
2021-01-24 19:49:54.088 16027-16027/com.debin.pokemonsearch E/AndroidRuntime: at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2625)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2577)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:247)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1333)
at android.app.Activity.performStart(Activity.java:6992)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2780)
... 9 more
Caused by: org.koin.core.error.InstanceCreationException: Could not create instance for [Factory:'com.debin.pokemonsearch.pokemoncore.interactors.AddToFavourites']
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:59)
at org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:36)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:87)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:214)
at org.koin.core.scope.Scope.get(Scope.kt:181)
at com.debin.pokemonsearch.di.ViewModelModuleKt$viewModelModule$1$1.invoke(ViewModelModule.kt:24)
at com.debin.pokemonsearch.di.ViewModelModuleKt$viewModelModule$1$1.invoke(Unknown Source:4)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:50)
... 49 more
Caused by: org.koin.core.error.InstanceCreationException: Could not create instance for [Single:'com.debin.pokemonsearch.pokemoncore.domain.repository.IPokemonCoreRepository']
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:59)
at org.koin.core.instance.SingleInstanceFactory.create(SingleInstanceFactory.kt:40)
at org.koin.core.instance.SingleInstanceFactory.get(SingleInstanceFactory.kt:48)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:87)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:214)
at org.koin.core.scope.Scope.get(Scope.kt:181)
at com.debin.pokemonsearch.di.UseCaseModuleKt$useCaseModule$1$3.invoke(UseCaseModule.kt:23)
at com.debin.pokemonsearch.di.UseCaseModuleKt$useCaseModule$1$3.invoke(Unknown Source:4)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:50)
... 56 more
Caused by: org.koin.core.error.InstanceCreationException: Could not create instance for [Single:'com.debin.pokemonsearch.pokemoncore.data.datasource.PokemonCoreDataSource']
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:59)
at org.koin.core.instance.SingleInstanceFactory.create(SingleInstanceFactory.kt:40)
at org.koin.core.instance.SingleInstanceFactory.get(SingleInstanceFactory.kt:48)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:87)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:214)
at org.koin.core.scope.Scope.get(Scope.kt:181)
at com.debin.pokemonsearch.di.RepositoryModuleKt$repositoryModule$1$3.invoke(RepositoryModule.kt:19)
at com.debin.pokemonsearch.di.RepositoryModuleKt$repositoryModule$1$3.invoke(Unknown Source:4)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:50)
... 64 more
Caused by: org.koin.core.error.NoBeanDefFoundException: No definition found for class:'com.debin.pokemonsearch.framework.db.PokemonDatabase'. Check your definitions!
at org.koin.core.scope.Scope.throwDefinitionNotFound(Scope.kt:246)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:216)
at org.koin.core.scope.Scope.get(Scope.kt:181)
at com.debin.pokemonsearch.di.DataSourceModuleKt$dataSourceModule$1$3.invoke(DataSourceModule.kt:19)
at com.debin.pokemonsearch.di.DataSourceModuleKt$dataSourceModule$1$3.invoke(Unknown Source:4)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:50)
... 72 more
AddToFavourites Usecase:
class AddToFavourites(private val coreRepository: IPokemonCoreRepository) {
suspend fun invokeAddToFavourites(pokemon: Pokemon) {
coreRepository.addPokemonToFavourites(pokemon)
}
}
Repository interface and model in domain layer
interface IPokemonCoreRepository {
suspend fun addPokemonToFavourites(pokemon: Pokemon)
suspend fun getFavouritePokemon() : List<Pokemon>
suspend fun removePokemonFromFavourite(pokemon: Pokemon)
}
data class Pokemon(
val id : Int,
val name : String,
val description : String,
val imageUrl : String
)
Repository Implementation and datasourse in data layer:
class PokemonCoreRepository(private val dataSource: PokemonCoreDataSource) : IPokemonCoreRepository {
override suspend fun addPokemonToFavourites(pokemon: Pokemon) {
return dataSource.addPokemonToFavourites(pokemon)
}
override suspend fun getFavouritePokemon(): List<Pokemon> {
return dataSource.getFavouritePokemon()
}
override suspend fun removePokemonFromFavourite(pokemon: Pokemon) {
return dataSource.removePokemonFromFavourite(pokemon)
}
}
interface PokemonCoreDataSource {
suspend fun addPokemonToFavourites(pokemon: Pokemon)
suspend fun getFavouritePokemon() : List<Pokemon>
suspend fun removePokemonFromFavourite(pokemon: Pokemon)
}
Datasourse implementaation in framework layer:
class PokemonCoreDataSourceImpl(private val database : PokemonDatabase) : PokemonCoreDataSource{
override suspend fun addPokemonToFavourites(pokemon: Pokemon) {
return database.pfDao.addToFavourite(PokemonFavouriteEntity(id = pokemon.id, name = pokemon.name,
description = pokemon.description, imageUrl = pokemon.imageUrl))
}
override suspend fun getFavouritePokemon(): List<Pokemon> {
return database.pfDao.getFavouritePokemon().map {
Pokemon(id = it.id, name = it.name, description = it.description, imageUrl = it.imageUrl)
}
}
override suspend fun removePokemonFromFavourite(pokemon: Pokemon) {
return database.pfDao.removeFromFavourite(PokemonFavouriteEntity(
id = pokemon.id, name = pokemon.name,
description = pokemon.description, imageUrl = pokemon.imageUrl
))
}
}
Koin di Modules :
val useCaseModule = module {
factory { AddToFavourites(get()) }
factory { GetFavourites(get()) }
factory { RemoveFromFavourite(get()) }
}
val repositoryModule = module {
single<IPokemonCoreRepository> { PokemonCoreRepository(get()) }
}
val dataSourceModule = module {
single<PokemonCoreDataSource> { PokemonCoreDataSourceImpl(get()) }
}
val databaseModule = module {
single { PokemonDatabaseFactory.getDBInstance(get()) }
}
val viewModelModule = module {
viewModel { SearchViewModel(get(), get(), get()) }
}
Database and entity in framework layer:
#Database(entities = [PokemonFavouriteEntity::class], version = 1, exportSchema = false)
abstract class PokemonDatabase : RoomDatabase() {
abstract val pfDao : PokemonFavouriteDao
}
object PokemonDatabaseFactory {
fun getDBInstance(context: Context) {
Room.databaseBuilder(context, PokemonDatabase::class.java, "PokemonDB")
.fallbackToDestructiveMigration()
.build()
}
}
#Dao
interface PokemonFavouriteDao {
#Insert(onConflict = REPLACE)
suspend fun addToFavourite(pokemon : PokemonFavouriteEntity)
}
#Entity(tableName = "favourites")
data class PokemonFavouriteEntity(
#PrimaryKey(autoGenerate = true) val id : Int,
#ColumnInfo(name = "pokemonName") val name : String = "",
#ColumnInfo(name = "pokemonDescription") val description : String = "",
#ColumnInfo(name = "pokemonImage")val imageUrl : String = ""
)
Viewmodel in the presentation layer:
class SearchViewModel (private val getPokemonDescription: GetPokemonDescription,
private val getPokemonSprites: GetPokemonSprites,
private val addToFavourites: AddToFavourites) : ViewModel() {
private val _pokemon = MutableLiveData<Resource<PokemonResponse>>()
private val _pokemonSprites = MutableLiveData<Resource<List<String>>>()
private val _pokemonSpecies = MutableLiveData<Resource<PokemonSpeciesResponse>>()
val pokemon: LiveData<Resource<PokemonResponse>> get() = _pokemon
val pokemonSpecies: LiveData<Resource<PokemonSpeciesResponse>> get() = _pokemonSpecies
fun getPokemonDetails(pokemonName: String) {
_pokemon.value = Resource.Loading()
getPokemonSprites.execute(PokemonSubscriber(), pokemonName)
}
fun getPokemonSpeciesDetails(pokemonName: String) {
_pokemonSpecies.value = Resource.Loading()
getPokemonDescription.execute(PokemonSpeciesSubscriber(), pokemonName)
}
fun addToFavourite() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
addToFavourites.invokeAddToFavourites(getPokemonDetails())
}
}
}
inner class PokemonSubscriber : DisposableSingleObserver<PokemonResponse>() {
override fun onSuccess(pokemonResponse: PokemonResponse) {
_pokemon.value = Resource.Success(pokemonResponse)
}
override fun onError(error: Throwable) {
_pokemon.value = Resource.Error(error.message)
}
}
inner class PokemonSpeciesSubscriber : DisposableSingleObserver<PokemonSpeciesResponse>() {
override fun onSuccess(pokemonSpeciesResponse: PokemonSpeciesResponse) {
_pokemonSpecies.value = Resource.Success(pokemonSpeciesResponse)
}
override fun onError(error: Throwable) {
_pokemonSpecies.value = Resource.Error(error.message)
}
}
private fun getPokemonDetails(): Pokemon {
var pokemonId = 0
var pokemonName = ""
var pokemonDescription = ""
var pokemonImage = ""
_pokemon.observeForever {
when (it) {
is Resource.Success -> {
pokemonImage = it.result.sprites.front_default
}
else -> {
}
}
}
_pokemonSpecies.observeForever {
when (it) {
is Resource.Success -> {
pokemonId = it.result.id
pokemonName = it.result.name
pokemonDescription = it.result.flavor_text_entries[0].flavor_text
}
else -> {
}
}
}
return Pokemon(pokemonId, pokemonName, pokemonDescription, pokemonImage)
}
override fun onCleared() {
super.onCleared()
getPokemonDescription.dispose()
getPokemonSprites.dispose()
}
}
You are not returning the created Database instance. Add return statement to the factory function or return using =
Like this:
object PokemonDatabaseFactory {
fun getDBInstance(context: Context) =
Room.databaseBuilder(context, PokemonDatabase::class.java, "PokemonDB")
.fallbackToDestructiveMigration()
.build()
}
Update
Actually, you don't need a factory function in Database. You can directly create an DB object from module through Koin, like this :
single {
Room.databaseBuilder(context, PokemonDatabase::class.java, "PokemonDB")
.fallbackToDestructiveMigration()
.build()
}

Deserialize generic object using Kotlin Serialization

I am trying to replace Gson library by kotlin serialization to handle JSON serialization/deserialization.
I am facing some issues to deserialize generic objects I have setup a simple example of what I am trying to achieve:
#Serializable
data class ContentMessageDto<T>(
val method: String,
val content: T
)
#Serializable
private data class DummyObjectNonNullProperties(
val value: Int,
#SerialName("aaa") val someProp: String,
val bbb: Boolean,
val ccc: Double
)
interface MyParser {
fun <T> parseContentMessage(
json: String
): ContentMessageDto<T>
}
class MyParserImpl(private val jsonSerialization: Json) : MyParser {
override fun <T> parseContentMessage(json: String): ContentMessageDto<T> {
return jsonSerialization.decodeFromString<ContentMessageDto<T>>(json)
}
}
fun main() {
println("start processing...")
val jsonToParse = """
{
"method":"get",
"content":{
"value":345,
"aaa": "some string",
"bbb": true,
"ccc": 23.4
}
}""".trimIndent()
val parser:MyParser = MyParserImpl(Json)
val result = parser.parseContentMessage<DummyObjectNonNullProperties>(jsonToParse)
println("result -> $result")
}
But when I run the main method, I get the following error:
Exception in thread "main" java.lang.IllegalStateException: Only KClass supported as classifier, got T
at kotlinx.serialization.internal.Platform_commonKt.kclass(Platform.common.kt:102)
at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:52)
at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
at kotlinx.serialization.SerializersKt__SerializersKt.builtinSerializerOrNull$SerializersKt__SerializersKt(Serializers.kt:79)
at kotlinx.serialization.SerializersKt__SerializersKt.serializerByKTypeImpl$SerializersKt__SerializersKt(Serializers.kt:69)
at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:54)
at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
But I am not sure why. Can someone provide me an explanation and if possible some tips on how I can implement this?
It would have worked if you've simply done:
val result = Json.decodeFromString<ContentMessageDto<DummyObjectNonNullProperties>>(jsonToParse)
But with all this wrapping, type information about T was lost. The problem is that you can't simply use reified generics here, cause inline functions can't be non-final.
Possible workarounds:
Define parseContentMessage as extension function so that it could have inline modifier (and T could be reified):
interface MyParser {
val jsonSerialization: Json
}
inline fun<reified T> MyParser.parseContentMessage(json: String): ContentMessageDto<T> {
return jsonSerialization.decodeFromString(json)
}
class MyParserImpl(override val jsonSerialization: Json) : MyParser
//Usage will be the same
Manually pass serializer for T into parseContentMessage:
interface MyParser {
fun <T> parseContentMessage(json: String, contentSerializer: KSerializer<T>): ContentMessageDto<T>
}
class MyParserImpl(private val jsonSerialization: Json) : MyParser {
override fun <T> parseContentMessage(json: String, contentSerializer: KSerializer<T>): ContentMessageDto<T> {
return jsonSerialization.decodeFromString(ContentMessageDto.serializer(contentSerializer), json)
}
}
//Usage:
val result = parser.parseContentMessage(jsonToParse, DummyObjectNonNullProperties.serializer())

how to get the data from an getParcelableArrayListExtra<Parcelable> in kotlin

How to get the data from an getParcelableArrayListExtra in kotlin?.
I have a Parceable data class called FoodParceable
data class FoodParceable(val idCover: Int, val name: String, val
price: Double):Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString(),
parcel.readDouble()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(idCover)
parcel.writeString(name)
parcel.writeDouble(price)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<FoodParceable> {
override fun createFromParcel(parcel: Parcel): FoodParceable {
return FoodParceable(parcel)
}
override fun newArray(size: Int): Array<FoodParceable?> {
return arrayOfNulls(size)
}
}
}
In the current Acitvity A, fill in the arrayListParceable and send it to activity B.
val arrayListParceable = ArrayList<FoodParceable>()
for (Food in listFood) {
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Carne", 9.99))
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Vegetales", 29.99))
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Frutas", 39.99))
}
val intent = Intent()
intent.putParcelableArrayListExtra(LIST_PRODUCT,arrayListParceable)
activity?.setResult(Activity.RESULT_OK, intent)
When I get the value in activity B, in list item, I can not enter its content.
if(resultCode == Activity.RESULT_OK){
if(data != null){
val listItems = data.getParcelableArrayListExtra<Parcelable>(LIST_PRODUCT)
listItems[0]. //ERROR
}
}
I am new to kotlin, thank you for your comments. Regards
You have to give Kotlin more information about the type of your object list. Try this:
val listItems : ArrayList<FoodParceable> = data.getParcelableArrayListExtra(LIST_PRODUCT)