RuntimeException: Exception inflating kotlin:navigation/nav_graph - android

We had a RuntimeException while we put a safeArgs into the navigation graph and this crash didn't give us any more guide to fix it.
In the first impression, we were following this to ensure that those classes were Parcelize or not.
Therefore, those classes were parceled correctly and everything seems right.

After many searches and investigations into our codes, I found that SafeArgs class exists in a sealed-class file.
Considering to this point that we can't allocate the parceled annotation to the sealed classes, we've decided to move our class outside of that sealed .kt file.
Although, I found the main reason for that runtime crash was this cause.
Also, I've provided the wrong and correct cases below, We hope this will be helpful to others:
We've moved the UserFavorite and its subclass, outside of the Response.kt file,
sealed class Response {
.
.
#Parcelize
data class UserFavorite(
#SerializedName("title") val title: String,
#SerializedName("itemType") val itemType: String,
#SerializedName("emptyIcon") val emptyIcon: String,
#SerializedName("_texts") val texts: UserFavoriteTexts
) : Response(), Parcelable
#Parcelize
data class UserFavoriteTexts(
#SerializedName("hintMessage") val hintMessage: String,
#SerializedName("add") val add: String,
#SerializedName("remove") val remove: String,
#SerializedName("edit") val edit: String
): Parcelable
.
.
}
into an independent file for that: UserFavorite.kt
#Parcelize
data class UserFavorite(
#SerializedName("title") val title: String,
#SerializedName("itemType") val itemType: String,
#SerializedName("emptyIcon") val emptyIcon: String,
#SerializedName("_texts") val texts: UserFavoriteTexts
) : Response(), Parcelable
#Parcelize
data class UserFavoriteTexts(
#SerializedName("hintMessage") val hintMessage: String,
#SerializedName("add") val add: String,
#SerializedName("remove") val remove: String,
#SerializedName("edit") val edit: String
): Parcelable
and, Respons.kt
sealed class Response {
.
.
.
.
}

Related

Model class implementations for a generic solution

I have following project in Github : https://github.com/alirezaeiii/TMDb-Paging
I have a generic solution in my classes using TmdbItem interface, such as :
abstract class BaseFragment<T : TmdbItem> : BaseNavTypeFragment() {
protected abstract val viewModel: BaseViewModel<T>
protected lateinit var tmdbAdapter: TmdbAdapter<T>
}
TmdbItem interface and the class implementations are as follow :
interface TmdbItem : Parcelable {
val id : Int
val overview: String
val releaseDate: String?
val posterPath: String?
val backdropPath: String?
val name: String
val voteAverage: Double
}
#Parcelize
data class Movie(
override val id: Int,
override val overview: String,
#SerializedName("release_date")
override val releaseDate: String?,
#SerializedName("poster_path")
override val posterPath: String?,
#SerializedName("backdrop_path")
override val backdropPath: String?,
#SerializedName("title")
override val name: String,
#SerializedName("vote_average")
override val voteAverage: Double) : TmdbItem
#Parcelize
data class TVShow(
override val id: Int,
override val overview: String,
#SerializedName("first_air_date")
override val releaseDate: String?,
#SerializedName("poster_path")
override val posterPath: String?,
#SerializedName("backdrop_path")
override val backdropPath: String?,
override val name: String,
#SerializedName("vote_average")
override val voteAverage: Double) : TmdbItem
As you see I have to use #SerializedName in both classes even if the value is the same such as poster_path and backdrop_path. Is there anyway that I could write them in one place such as a base class instead of both class implementations?
That is probably not easily possible with Gson. The closest you could get to avoid duplicating the name is to define the property names somewhere as const val and then refer to them in #SerializedName, e.g. #SerializedName(POSTER_PATH_NAME).
Another solution which is however quite complicated and error-prone would be:
Place #SerializedName on the getter functions of the interface, e.g. #get:SerializedName("..."). This is possible because #SerializedName supports METHOD as target, but Gson only considers it on fields by default. Then write a custom FieldNamingStrategy which does the following:
Check if the field has a #SerializedName annotation, in that case return its value
Otherwise, obtain the Kotlin property (if any) for the Java field and obtain its getter method:
field.kotlinProperty?.javaGetter
Check if the getter has a #SerializedName, in that case return its value
Otherwise, go through the superclasses and then superinterfaces to check if any of them declare the getter (which is overridden) and have a #SerializedName
This is how it might work in theory; I have not tried to fully implement this yet. And as you can see the logic would be quite complex, so I am not sure if that would really be worth it. Also keep in mind that Gson directly reads field values and does not call getters during serialization. Therefore using #SerializedName on getters (especially if they have a custom implementation) might cause confusion.
Otherwise you might have to look for external Gson extensions which support getter methods, or other JSON libraries.

I got Unable to invoke no-args constructor error. retrofit caused error?

I have a sealed class that contains two models.
1-DataOFEachOrder
2-List of DataOFEachOrder
sealed class OrdersHistoryDataModel
data class SingleOrderHistoryDataModel(val `data`:DataOFEachOrder , val success: Boolean): OrdersHistoryDataModel()
data class ListOrdersHistoryDataModel(val `data`:List<DataOFEachOrder> , val success: Boolean):OrdersHistoryDataModel()
DataOFEachOrder contains:
class DataOFEachOrder(
val __v: Int,
val _id: String,
val createdAt: String,
val id: String,
val restaurant: Restaurant<*>,
val restaurantName: Any,
var isexpanded:Boolean = false
)
the restaurant property is a variable could be have two schema. so I had to define it as sealed class too. now it is nested sealed class somehow.
sealed class Restaurant<T>(val value: T)
{
//model 1 is used in moreDetailFragment
data class RestaurantModel1(
val _id: String,
val id: String, val name: String, val category: String, val owner: String) : Restaurant<String>(id)
//model 2 is used in ordersStatusFragment
data class RestaurantModel2(val restaurant: String): Restaurant<String>(restaurant)
}
My API :
//getList of histories Orders- OrderStatusFragment
#POST("orders/orderlist")
#Headers("No-Authentication: false")
suspend fun getOrdersHistory(#Body status:JsonObject):Response<ListOrdersHistoryDataModel>
I just know my error comes from Restaurant sealed class or retrofit. see other Q&A , I did not get any point.
what should I do?
the message error :
java.lang.RuntimeException: Unable to invoke no-args constructor for simpleloginregister.ui.ordersStatus.TabFragments.Restaurant<?>. Registering an InstanceCreator with Gson for this type may fix this problem.

Can I use same data class for Room and API calls in Kotlin?

I have a fairly simple data class in my app:
data class ModelSlide(
val title: String,
val info: String,
val image: String
)
When I create an API call, I get back json. I use the information from the json and the ModelSlide to create ArrayList<ModelSlide>. Then I load this arrayList in a viewpager by using an adapter. Fairly simple stuff.
Now, I've just started working with Room. Sadly, due to being quite new at this, I've been creating duplicate data classes for Room like so:
#Entity
data class User(
#PrimaryKey val slideId: Int,
#ColumnInfo(name = "title") val title: String?,
#ColumnInfo(name = "info") val firstName: String?,
#ColumnInfo(name = "image") val lastName: String?
)
This approach works, but to me, a beginner at Room, this somehow seems redundant.
Can I combine both my workflows, with the same data class?
To Recap
I use the first ModelSlide class to create ArrayList<ModelSlide> and load the slides into a Viewpager with the help of an adapter
I created the second data class for ModelSlide to use with Room library
My question
Can I use the same data class for creating ArrayLists and with Room?
If so, how would the data class look like?
According your requirements you can use single data class for creating ArrayLists and with Room. Check below:
#Entity(tableName = "User")
data class ModelSlide(
val title: String?,
val info: String?,
val image: String?,
#PrimaryKey(autoGenerate = true)
val slideId: Int = 0
)

How to use a simple data class in kotlin

I am new to Kotlin and learning it, I am having a simple data class
data class Country{
#SerializedName("name")
val countryName: String?,
#SerializedName("capital")
val capital: String?,
#SerializedName("flagPNG")
val flag: String?
}
Errors I am facing:
]
Your data class should look like this:
data class Country(
#SerializedName("name")
val countryName: String?,
#SerializedName("capital")
val capital: String?,
#SerializedName("flagPNG")
val flag: String?
)
The difference is, like mentioned in the comments: I used normal parentheses around the fields while you used curly braces
Data Class in Kotiln must have a variable/value parameter in it's constructor declaration.
Official doc states that :
To ensure consistency and meaningful behavior of the generated code,
data classes have to fulfill the following requirements:
The primary constructor needs to have at least one parameter;
All primary constructor parameters need to be marked as val or var;
Data classes cannot be abstract, open, sealed or inner;
(before 1.1) Data classes may only implement interfaces.
So, your data class should be something similar like below :
data class Foo(
val bar: Any
)
Note: In Kotlin, you can declare class constructor just by placing '()' following by class name to make it as primary constructor.
You class declaration should be something like below :
data class Country(
#SerializedName("name")
val countryName: String?,
#SerializedName("capital")
val capital: String?,
#SerializedName("flagPNG")
val flag: String?
)
Refer here for more info.

Problem with #Ignore and immutable fields

I have a problem with room.
I'm using retrofit with Gson converter for the rest api, and I'd like to share the pojos with room. In general it works, but in some cases I need to ignore some fields, because I have list of objects. I tried to use the #Ignore annotation, but using it the build process fails with the following errors:
error: Entities and Pojos must have a usable public constructor. You
can have an empty constructor or a constructor whose parameters match
the fields (by name and type). public final class Service {
^ error: Cannot find setter for field.
private final java.lang.String id = null;
^ error: Cannot find setter for field.
private final java.lang.String name = null;
^ error: Cannot find setter for field.
private final java.lang.String description = null;
So, using this class, everything works:
#Entity(tableName = "services")
data class Service(
#PrimaryKey val id: String,
val name: String,
val description: String,
val parentId: String?
)
With this, fails:
#Entity(tableName = "services")
data class Service(
#PrimaryKey val id: String,
val name: String,
val description: String,
val parentId: String?,
#Ignore val test: String
)
I'm using this version of room:
implementation 'androidx.room:room-runtime:2.1.0-alpha06'
kapt 'androidx.room:room-compiler:2.1.0-alpha06'
I know that the problem could be fixed using var instead of val and adding a secondary constructor, but I don't want to do that, I prefer to preserve the immutable state of my fields.
Is it a bug of the ignore annotation? Why without it everything works?
Any help is appreciated :)
In your second example Service will be translated to a Java class which has a single constructor with four parameters. Room sees the #Ignore annotations and knows that it needs to bind 3 fields and so it needs a constructor with 3 parameters matching those fields types. As it doesn't find such a constructor(or the default one) it fails.
Try to make the last property optional and use the #JvmOverloads annotation on the constructor:
#Entity(tableName = "services")
data class Service #JvmOverloads constructor(
#PrimaryKey val id: String,
val name: String,
val description: String,
val parentId: String?,
#Ignore val test: String? = null
)
This will make the Kotlin compiler to generate a 3 parameter constructor and make Room happy again.
I'm using retrofit with Gson converter for the rest api, and I'd like
to share the pojos with room.
You should probably avoid doing this and use two sets of classes to model the API response and the database data. Even in this initial moment you need to make some "hacks" to make everything work, if in the future the API or the database changes you'll need to make more complex changes in more places.
In fact, you can retain immutability with a single class and without the #JvmOverloads trick. You simply need the only visible (to Room) constructor to match the fields of the table. For your example, it would be:
#Entity(tableName = "services")
data class Service #Ignore constructor(
#PrimaryKey val id: String,
val name: String,
val description: String,
val parentId: String?,
#Ignore val test: String
) {
constructor(id: String, name: String, description: String, parentId: String?) :
this(id, name, description, parentId, /* some default value for test */)
}

Categories

Resources