fun launchNextScreen(context: Context, people: People): Intent {
val intent = Intent(context, NextScreenActivity::class.java)
intent.putExtra(EXTRA_PEOPLE, (Parcelable) people)
//intent.putExtra(EXTRA_PEOPLE, people as Parcelable)
//intent.putExtra(EXTRA_PEOPLE, people)
// tried above all three ways
return intent
}
I tried the above code to pass an instance of the People class via intent using kotlin, but I am getting an error.
What am I doing wrong?
First, make sure the People class implements the Serializable interface:
class People : Serializable {
// your stuff
}
Inner fields of People class must also implement the Serializable interface, otherwise you'll get runtime error.
Then it should work:
fun launchNextScreen(context: Context, people: People): Intent {
val intent = Intent(context, NextScreenActivity::class.java)
intent.putExtra(EXTRA_PEOPLE, people)
return intent
}
To receive people back from Intent you'll need to call:
val people = intent.getSerializableExtra(EXTRA_PEOPLE) as? People
Implement Serializable in the object:
data class Object (
var param1: Int? = null,
var param2: String? = null
) : Serializable
or
class Object : Serializable {
var param1: Int? = null,
var param2: String? = null
}
Then, you can pass the object using Intent:
val object = Object()
...
val intent = Intent(this, Activity2::class.java)
intent.putExtra("extra_object", object as Serializable)
startActivity(intent)
Finally, in Activity2 you get the object with:
val object = intent.extras.get("extra_object") as Object
Found a better way of doing this:
In your gradle:
apply plugin: 'org.jetbrains.kotlin.android.extensions'
android {
androidExtensions {
experimental = true
}
}
In your data class:
#Parcelize
data class Student(val id: String, val name: String, val grade: String) : Parcelable
In source activity:
val intent = Intent(context, Destination::class.java)
intent.putExtra("student_id", student)
context.startActivity(intent)
In destination activity:
student = intent.getParcelableExtra("student_id")
Parcelize is no longer experimental, as of Kotlin 1.3.60+!
Define a data class, adding #Parcelize annotation and extending Parcelable:
#Parcelize
data class People(val id: String, val name: String) : Parcelable
To add to intent:
val intent = Intent(context, MyTargetActivity::class.java)
intent.putExtra("people_data", student)
context.startActivity(intent)
In MyTargetActivity:
val people: People = intent.getParcelableExtra("people_data")
If you haven't yet, enable extensions in your app's build.gradle. This also enables data binding and other great advanced features
apply plugin: 'kotlin-android-extensions'
I found a better way to pass an object from one activity to another using Parcelable, it is faster than Serialization
Android: Difference between Parcelable and Serializable?
first, add these lines of code inside build.gradle(app)
apply plugin: 'kotlin-android-extensions' in app.grable
#Parcelize
class Model(val title: String, val amount: Int) : Parcelable
passing with Intent--->
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra(DetailActivity.EXTRA, model)
startActivity(intent)
Getting from intent --->
val model: Model = intent.getParcelableExtra(EXTRA)
This post might be useful for what you intend to do:
Is there a convenient way to create Parcelable data classes in Android with Kotlin?
Basically, kotlin provides some little extras:
#Parcelize
class User(val firstName: String, val lastName: String) : Parcelable
See the post for more info.
in kotlin to start activity and pass some data you could try something like this:
startActivity(intentFor<NameOfActivity>(STRING to data))
Have Fun
you have to add the type inside <>...its working for me
val people = intent.getParcelableExtra<People>(EXTRA_PEOPLE) as People
In your PeopleDetailActivity activity, initialize viewModel in onCreate
viewModel = this#PeopleDetailActivity.viewModel.apply { postPeopleId(getPeopleFromIntent().id) }
And also initialize this method :
private fun getPeopleFromIntent() = intent.getParcelableExtra<People>(peopleId) as People
Write below code for passing the intent extras :
companion object {
private const val objectId = "people"
fun startActivityModel(context: Context?, people: People) {
if (context != null) {
val intent = Intent(context, PeopleDetailActivity::class.java).apply {
putExtra(
objectId,
people
)
}
context.startActivity(intent)
}
}
}
Call the above method in your PeopleListViewHolder onClick
override fun onClick(v: View?) = PeopleDetailActivity.startActivityModel(context(), people)
Your People class needs to implement Parcelable like this:
class People(): Parcelable{
// stuff
}
android studio will show you an error. Simple move your cursor to "People" and hit alt+enter. It should now show the option to generate a Parcelable implementation.
This generated code will contain something like
parcel.writeString(member1)
parcel.writeInt(member2)
and somewhere else
member1=parcel.readString()
member2=parcel.readInt()
Make sure that all members are contained in these methods and when modifying it, make sure that read and write is happening in the exact same order.
Related
I have a parcelable team class
#Parcelize
class Team(var name: String, var teamMembers: List<String>, var id: UUID): Parcelable
I have a service that returns a list of (currently hardcoded) Teams:
#Module
class TeamInfoModule #Inject constructor(): ITeamInfoModule {
#Provides
override fun getAllTeamData(): List<Team> { ... }
}
I want to be able to pass this list of teams into a Fragment from an activity like so:
class MainActivity: AppCompatActivity() {
#Inject
lateinit var teamInfoModule: TeamInfoModule;
lateinit var team: Team;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DaggerServiceModuleComponent.create().inject(this)
val bundle = Bundle()
val teamArrayList: List<Team> = this.teamInfoModule.getAllTeamData()
val homeFragment = HomeFragment()
bundle.putParcelable("teamData", teamArrayList)
homeFragment.arguments = bundle
}
}
This throws an error of: Type Mismatch. Required: Parcelable? Found: List<Team>.
I know that that a single team can be passed to my Fragment as it doesn't throw an error.
My question is, is there a utility that I haven't found that can somehow serialize a List to a Parcelable? I had the idea of creating a custom TeamListClass that also implements #Parcelize but I wanted to ask here before I went off and wrote code that I didn't need. Maybe something similar to a JS' Array.map() that will pass each Parcelable into the bundle?
You should use:
bundle.putParcelableArrayList("teamData", ArrayList(teamArrayList))
Convert the list to arrayList using ArrayList(teamArrayList)
bundle.putParcelableArrayList("teamData", ArrayList(teamArrayList))
I am developing Android project in Kotlin. I want to create a model class that implements Parcelable interface. This is what I tried:
#Parcelize
data class School(
#Expose val name: String?,
#Expose val address: String?,
): Parcelable
But I get compiler error saying that "Class School is not abstract and does not implement abstract memeber public abstract fun writeToParcel(p0: Parcel!, p1: Int):Unit defined in android.os.Parcelable".
I understand what the error is saying. But how to get rid of this error? My Kotlin version is 1.3.50
Add
androidExtensions {
experimental = true
}
to your Android block within your app build.gradle.
check you have Added this
androidExtensions {
experimental = true
}
and try to implement Parcelable like this
class School(var name:String, var address:String):Parcelable
pass data like this
val school = School("demo","demo")
val intent = Intent(this, activityB::class.java)
intent.putExtra("schooldata",school)
startActivity(intent)
get like this
var school = intent.extras.getParcelable<School>("schooldata")
schooldata.setText("${school.name}"+"\n"+"${school.address}")
I have a sealed class like so:
sealed class SealedClass {
object Object1 : SealedClass()
object Object2 : SealedClass()
object Object3 : SealedClass()
data class DataClass(val sealedClass: SealedClass, val anotherDataType: AnotherDataType? = null)
}
I would like to pass my data class in a Bundle like we normally pass values to a new fragment like so:
#JvmStatic
fun newInstance(dataClass: DataClass): Fragment {
val fragment = Fragment()
val args = Bundle(1)
args.putParcelable("DATA_CLASS", dataClass)
fragment.arguments = args
return fragment
}
I'm not sure how to go about this. So far what I've read is that people use an #Parcelize annotation, which is an experimental feature of Kotlin that I'm trying to avoid. Another approach is to extend the data class by Parcelable and implement the Parcelable methods, but since I use custom classes as parameters in the DataClass (for instance, SealedClass), I don't know how to read/write those values inside Parcelable implementation. Is this not a right approach to go about it?
I think this can be simpler now with recent Kotlin using Parcelable:
#Parcelize
data class TimeSeries(
val sourceInfo: SourceInfo? = null,
val variable: Variable? = null,
val values: List<Value_>? = null,
val name: String? = null
) : Parcelable
Then pass it in your bundle:
val intent = Intent(context, DetailsActivity::class.java).apply {
putExtra(MY_DATA, mydata[position])
}
context.startActivity(intent)
Then bring it in through your bundle:
mydata = intent?.getParcelableExtra<TimeSeries>(MY_DATA)
If you want instead to pass a Bundle you can also just use bundleOf(MY_DATA to mydata[position]) when putting the Extra, and intent?.getBundleExtra(MY_DATA)?.getParcelable<TimeSeries>(MY_DATA) when getting it, but looks like adding another layer.
If you want to transform the sealed class as parcelable, you can do the following:
sealed class SealedClass : Parcelable {
#Parcelize
object Object1 : SealedClass()
#Parcelize
object Object2 : SealedClass()
#Parcelize
object Object3 : SealedClass()
#Parcelize
data class DataClass(val sealedClass: SealedClass, val anotherDataType: AnotherDataType? = null) : SealedClass()
}
Serializable while using reflection and causing a bit more garbage collection, is easier to implement.
I find it easiest to use GSON.
https://github.com/google/gson
First, add it to your data class like this:
data class TimeSeries(
#SerializedName("sourceInfo")
val sourceInfo: SourceInfo? = null,
#SerializedName("variable")
val variable: Variable? = null,
#SerializedName("values")
val values: List<Value_>? = null,
#SerializedName("name")
val name: String? = null
) : Serializable
Then pass it in your bundle:
val intent = Intent(context, DetailsActivity::class.java).apply {
putExtra(MY_DATA, Gson().toJson(mydata[position]))
}
context.startActivity(intent)
Then bring it in through your bundle:
mydata = Gson().fromJson(intent?.extras?.getString(MY_DATA), TimeSeries::class.java)
I need to pass several arguments as input for my WorkManager, but don't know how. Also, I want to pass Repository.
val data = workDataOf("cabinId" to task.cabinId)
val data2 = workDataOf("repository" to repository)
val uploadWorkRequest = OneTimeWorkRequestBuilder<WManager>()
.setInputData(data)
.setInputData(data2)
.build()
class WManager(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
override fun doWork(): Result {
val input = inputData.getInt("cabinId", 99)
val input2 = inputData("repository")
return Result.success(outputData)
}}
I tried to implement interface serializable for Repository but it does not work
You are doing the right thing. You can pass in multiple key value pairs to workDataOf.
So something like:
val data = workDataOf("cabinId" to task.cabinId, "repository" to repository)
val request = OneTimeWorkRequestBuilder<WManager>()
.setInputData(data)
.build()
WorkManager.getContext(context).enqueue(request)
This is assuming that repository is a serializable type. If not, you will need to do dependency injection.
The class MySettings include two embed class in Code A, how can I create a oject of MySettings ?
It seems that fun initA() can't instance of two embed class.
I realize that the construct of Code A is wrong, so I write Code B, I don't know whether there is a good way!
Code A
class MySettings(val _id: Long) {
data class MyBluetooth(
val status: Boolean = false
)
data class WiFiDef(
val name: String,
val status: Boolean = false
)
}
fun initA(){
var myObject =MySettings(10L)
}
Code B
class MySettings(val _id: Long) {
var aMyBluetooth: MyBluetooth? =null
var aWiFiDef: WiFiDef?=null
}
data class MyBluetooth(
val status: Boolean = false
)
data class WiFiDef(
val name: String,
val status: Boolean = false
)
fun initB(){
var myObject =MySettings(10L)
myObject.aMyBluetooth=MyBluetooth(false)
myObject.aWiFiDef=WiFiDef("name",true)
}
As far as your question is concerned Code A is absolutely right.
But if we draw a comparison between your Code A and Code B, there are different scenarios in which they are best suited.
Scenario 1: Good Programming Practice
If you want to follow good programming practice in Kotlin, then follow Code B as it is a good practice to write code that is reusable. And one more thing, you can instantiate your object in code B like this.
fun initB(){
var myObject =MySettings(10L, aMyBluetooth=MyBluetooth(false),
aWiFiDef=WiFiDef("name",true))
}
Scenario 2: Inner Class
Your Code A is useful in a situation where you want to implement inner class in kotlin. Inner class is able to access members of outer class. But in this scenario, data class cannot be inner.
class MySettings(val _id: Long) {
inner class MyBluetooth(
val status: Boolean = false
)
inner class WiFiDef(
val name: String,
val status: Boolean = false
)
}
fun initA(){
var myObject =MySettings(10L)
}
I hope this clears your query.
You can keep your data classes inside MySettings if you combine somehow Code A and Code B:
class MySettings(val _id: Long) {
val blueTooth = MyBluetooth(true)
val wiFiDef = WiFiDef("wifi", true)
data class MyBluetooth(
val status: Boolean = false
)
data class WiFiDef(
val name: String,
val status: Boolean = false
)
}
fun initA(){
val myObject = MySettings(10L)
val myBlueTooth = myObject.blueTooth
val myWiFiDef = myObject.wiFiDef
}
After that it's up to you to decide by changing all or any of val to var inside MySetings how to use the instances of the data classes. But this is a way to expose them.
I'd go with approach like this:
Make use of Kotlin constructors, which can take class-scope variables. Just mark those nullable (via ?) inside constructor and let the user decide wether he/she wants to initialise those variables or not
Make all settings (bluetooth and wifi) extend from sealed class, especially since you have common variables between them.
To sum up, I would write this code like this:
class MySettings(val id: Long, val bluetooth: Setting.MyBluetooth? = null, val wifi: Setting.WifiDef? = null) {
sealed class Setting(val status: Boolean = false) : Serializable {
class WifiDef(
status: Boolean,
val name: String
): Setting(status)
class MyBluetooth(
status: Boolean
): Setting(status)
}
}
fun init() {
val mySettings = MySettings(10L, bluetooth = MySettings.Setting.MyBluetooth(true))
val bluetooth = mySettings.bluetooth
val wifi = mySettings.wifi
}
Talking about other ways to do this, I would highly recommend not to hard code any values inside your class, especially not when you have some instances of classes inside. This will be hideous to refactor later on, if something changes