In a fragment,
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var args = arguments
args?.getParcelable<Jar>("jar").let {
viewModel.setJar(it as Jar)
}
setObservers()
}
Above code is working fine but,
args?.getParcelable<Jar>("jar").let {
viewModel.setJar(it as Jar)
}
is deprecated. So I tried
args?.getParcelable("jar", Jar::class.java).let {
viewModel.setJar(it as Jar)
}
But its throwing error.
java.lang.NoSuchMethodError: No virtual method getParcelable(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; in class Landroid/os/Bundle; or its super classes (declaration of 'android.os.Bundle' appears in /system/framework/framework.jar!classes2.dex)
Parcelable Object:
#Parcelize data class Jar(
#field:SerializedName("id")
val id: String? = null,
#field:SerializedName("name")
val name: String? = null,
#field:SerializedName("description")
val description: String? = null,
#field:SerializedName("imageUrl")
val imageUrl: String? = null,
) : Parcelable
nav_version = "2.5.3"
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
plugins {
id 'kotlin-parcelize'
Can someone help me, how to retrieve Parcelable objects correctly with latest way and correct way.
Related
Released APK crashes with next error:
Serializer for class 'User' is not found.
Mark the class as #Serializable or provide the serializer explicitly.
#Keep
#Serializable
data class User(
val id: Long = 0,
val name: String? = null,
val token: String? = null
)
#ExperimentalSerializationApi
object UserSerializer : Serializer<User> {
override val defaultValue: User
get() = User()
override suspend fun readFrom(input: InputStream): User {
return ProtoBuf.decodeFromByteArray(input.readBytes())
}
override suspend fun writeTo(t: User, output: OutputStream) {
output.write(ProtoBuf.encodeToByteArray(t))
}
}
I added official proguard rules from
https://github.com/Kotlin/kotlinx.serialization#android
(with my package of course)
Even added #Keep to User class but still it crashes
What is the problem here?
UPDATE
Found the issue. I actually had one companion object inside User and had to add #Keep for this one as well to fix the issue:
#Keep
#Serializable
data class User(
val id: Long = 0,
val name: String? = null,
val token: String? = null,
...
) {
#Keep
companion object {
const val ...
...
}
}
I have to send complex object into a fragment.
I used this code but it does not work.
companion object {
#JvmStatic
fun newInstance(param1: CustomerWithAccounts, param2: String) = TransactionsListFragment().apply {
arguments = Bundle().apply {
putParcelable(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
println("$param1 $param2")
}
}
private const val dialogTag = "transaction_list_dialog"
fun display(fragmentManager: FragmentManager): TransactionsListFragment {
val transactionListDialog = TransactionsListFragment()
transactionListDialog.show(fragmentManager, dialogTag)
return transactionListDialog
}
}
and I send it like this.
private val onItemClickListener = object : TransactionAdapter.OnItemClickListener {
override fun onItemClick(view: View?, obj: CustomerWithAccounts?, position: Int) {
if (obj != null) {
val fragmentManager: FragmentManager = childFragmentManager
val fragment = TransactionsListFragment
fragment.newInstance(obj, "DIAA")
fragment.display(fragmentManager)
}
}
}
This is my Modal class
class CustomerWithAccounts(
#Embedded val customerAccounts: CustomerAccounts,
#Relation(parentColumn = "customerOwnerId", entityColumn = "customerId")
val customer: Customer
)
How to do that please help.
Writing code that can be generated from XML is questionable.
Use navigation with safe-args plugin, which is rather the framework way of passing Object.It's a code-generator plugin which utilizes the arguments defined in the navigation graph XML.
buildscript {
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.3"
}
}
Per module:
// apply plugin: "androidx.navigation.safeargs"
apply plugin: "androidx.navigation.safeargs.kotlin"
The outcome are generated bindings alike: TransactionFragmentArgs implements NavArgs.
See: Pass data between destinations for how to declare these argument-definitions ...
You messed up in creating Object . you are basically creating two objects right now . Fix it .
private val onItemClickListener = object : TransactionAdapter.OnItemClickListener {
override fun onItemClick(view: View?, obj: CustomerWithAccounts?, position: Int) {
if (obj != null) {
val fragment = TransactionsListFragment.newInstance(obj, "DIAA")
fragment.display(childFragmentManager)
}
}
}
Well you need to fix your display method too . Why r u creating another Object in Display ?
fun display(fragmentManager: FragmentManager) {
show(fragmentManager, dialogTag)
}
I am trying to get an object from a MutableLiveData<Resource<ObjectIWant>> to a var hereIwantTheObject : ObjectIWant in my HomeFragment, apparently use the Resource class is the recommended way if you use coroutines and LiveData. The thing is that I want this object on my variable and disatached from MutableLiveData, when I try it says the variable hasn't been initialized. Here my classes and what I have tried
ViewModel class:
class HomeFragmentViewModel : ViewModel() {
val mutableVar: MutableLiveData<Resource<ObjectIwant>> = MutableLiveData()
init {
getObjectIwantData()
}
fun getObjectIwantData() = viewModelScope.launch {
mutableVar.postValue(Resource.Loading())
val response = AppRepository().getObjectIwantDataInRepository()
mutableVar.postValue(handleResponse(response))
}
private fun handleResponse(response: Response<ObjectIwant>): Resource<ObjectIwant> {
if (response.isSuccessful) {
response.body()?.let { resultResponse ->
return Resource.Success(resultResponse)
}
}
return Resource.Error(response.message())
}
HomeFragment class and where I am trying to get the info:
class HomeFragment : Fragment() {
lateinit var viewModel: HomeFragmentViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
viewModel = ViewModelProvider(this).get(HomeFragmentViewModel::class.java)
binding.lifecycleOwner = this
var hereIwantTheObject: ObjectIwant? = null
viewModel.brastlewarkTownData.observe(viewLifecycleOwner, Observer { response ->
response.data?.let { brastlewarkTown ->
hereIwantTheObject = brastlewarkTown
}
})
Log.i(TAG, "onCreateView: ${hereIwantTheObject!!.name")
return binding.root
}
}
Here the Resource class:
sealed class Resource<T>(
val data: T? = null,
val message: String? = null
) {
class Success<T>(data: T) : Resource<T>(data)
class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)
class Loading<T> : Resource<T>()
}
When onCreateView() is executed, several things happen:
a local var hereIwantTheObject is declared and initialised with null
an observer for the LiveData<Resource<ObjectIwant>> object is registered
the value of the local variable is still null but you promise it is not null when writing to Logcat
so at this point your app will crash...
But why is hereIwantTheObject still null? That's because the observation results for any LiveData objects can only come in on the UI thread after execution of the current function onCreateView() has finished.
At this point however the local variable will have been discarded, so if you want to be able to assign a value to it later on you need to make it a property:
private var hereIwantTheObject: String? = null
Now if you delete the line with the local declaration for hereIwantTheObject and move the Log.i(...) statement to the block which is executed if there are changes to the LiveData object, everything will work as desired
response.data?.let { brastlewarkTown ->
hereIwantTheObject = brastlewarkTown
Log.i(TAG, "onCreateView: ${hereIwantTheObject!!.name}")
}
Just a solution that someone wrote to me that I think could be useful:
Background
I just wanted to know if the table was ever been modified, so I thought that maybe by getting the last generated ID this could be a way to do it, and since I know this value should be saved somewhere, it should be possible to get it.
My idea :
Using Room, get the last inserted auto-generated ID (even if nothing was ever inserted) , without modifying the table or create any additional table.
The problem
I couldn't find a way to do it, so I requested about this here and asked here.
What I've found
There were some answers over StackOverflow for using "last_insert_rowid", but I failed to use it.
Here's a sample Room Database :
build.gradle
repositories {
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}
...
apply plugin: 'kotlin-android-extensions'
...
final def room_version = '2.1.0'
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
...
DBProvider.kt
object DBProvider {
const val DB_NAME = "room.db"
lateinit var DB: MainDatabase
fun init(context: Context) {
// Log.d("AppLog", "DBProvider init")
DB = Room.databaseBuilder(context.applicationContext, MainDatabase::class.java, DB_NAME)
.addCallback(object : RoomDatabase.Callback() {
}).build()
}
#Database(
entities = [FavoriteSpeedDialDTO::class],
version = DB_VERSION,
exportSchema = false
)
abstract class MainDatabase : RoomDatabase() {
abstract fun favoriteSpeedDialDao(): FavoriteSpeedDialDao
abstract fun dao(): SpecialDao
}
}
FavoriteSpeedDialDao.kt
#Dao
abstract class FavoriteSpeedDialDao {
#Query("SELECT * FROM favorite_speed_dial")
abstract fun getFavoritesList(): MutableList<FavoriteSpeedDialDTO>
#Insert
abstract fun insert(item: FavoriteSpeedDialDTO): Long
#Query("DELETE FROM favorite_speed_dial")
abstract fun deleteAll(): Int
}
FavoriteSpeedDialDTO.kt
#Entity(tableName = "favorite_speed_dial")
#Parcelize
data class FavoriteSpeedDialDTO(#ColumnInfo(name = COL_ID) #PrimaryKey(autoGenerate = true) var id: Long,
#ColumnInfo(name = COL_ASSIGNED_PHONE_NUMBER) var phoneNumber: String) : Parcelable {
companion object {
const val COL_ID = BaseColumns._ID
const val COL_ASSIGNED_PHONE_NUMBER = "assigned_phone_number"
}
}
The question
Using Room, how to get the last generated ID of a table, even if nothing was added to it, ever, and also when it gets emptied?
Since I already found an answer, I posted it, but if you think there is another nice solution, feel free to post it.
And the answer:
SpecialDao.kt
#Suppress("AndroidUnresolvedRoomSqlReference")
#Dao
abstract class SpecialDao {
#Query("SELECT seq FROM sqlite_sequence WHERE name = :tableName")
abstract fun getSequenceNumber(tableName: String): Long?
}
Sample to show it works:
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DBProvider.init(this)
AsyncTask.execute {
val dao = DBProvider.DB.dao()
var sequenceNumber = dao.getSequenceNumber("favorite_speed_dial")
Log.d("AppLog", "id:$sequenceNumber")
val favoriteSpeedDialDao = DBProvider.DB.favoriteSpeedDialDao()
var favoritesList = favoriteSpeedDialDao.getFavoritesList()
Log.d("AppLog", "favoritesList:${favoritesList}")
Log.d("AppLog", "deleting all and inserting a new item...")
favoriteSpeedDialDao.deleteAll()
favoriteSpeedDialDao.insert(FavoriteSpeedDialDTO(0L, "123"))
favoritesList = favoriteSpeedDialDao.getFavoritesList()
Log.d("AppLog", "favoritesList:${favoritesList}")
sequenceNumber = dao.getSequenceNumber("favorite_speed_dial")
Log.d("AppLog", "id:$sequenceNumber")
}
}
companion object {
fun toString(collection: Collection<*>?): String {
if (collection == null)
return "null"
val sb = StringBuilder("{")
var isFirst = true
for (`object` in collection) {
if (!isFirst)
sb.append(',')
else
isFirst = false
sb.append(`object`)
}
sb.append('}')
return sb.toString()
}
}
}
I'm new to kotlin and trying to pass an object from my adapter to a fragment. But I am getting a type mismatch in the companion object for booklist. It says Required: Parceleable Found: List<ResourcesList>?
I've also tried using putParcelableArrayList and putParcelableArray and Serializable but also with the same type mismatch.
My data model looks like this:
#Parcelize
class ResourcesList (val id: Int,
val name: String,
val content: Contents,
val tags: List<Tags>) : Parcelable
#Parcelize
class Contents (val id: Int,
val producers: List<Producers>,
val categories: List<Categories>,
val isAvailable: Boolean): Parcelable
#Parcelize
class Producers (val name: String,
val role: String): Parcelable
#Parcelize
class Categories (val id: Int,
val name: String): Parcelable
Fragment
class SeeAllFragment: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.see_all_layout, container, false)
val bookslist = arguments.getParceleable("bookslist")
return view
}
companion object {
fun newInstance(bookslist: List<ResourcesList>?): SeeAllFragment {
val args = Bundle()
args.putParcelable("bookslist", bookslist)
val fragment = SeeAllFragment()
fragment.arguments = args
return fragment
}
}
}
for putting list in bundle :
args.putParcelableArrayList("bookslist", bookslist as ArrayList<out Parcelable>?)
for getting list :
val bookslist = arguments.getParcelableArrayList<ResourcesList>("bookslist")
In my coding I using Gson.
companion object {
const val BOOK_LIST = "bookslist"
fun getFragment(bookslist: List<ResourcesList>): SeeAllFragment {
return SeeAllFragment().apply { arguments = Bundle().apply {
putString(BOOK_LIST, Gson().toJson(bookslist))
} }
}
}
Then get data like this.
val bookslist = Gson().fromJson(arguments?.getString(BOOK_LIST), object : TypeToken<List<ResourcesList>>() {}.type)