What i am trying to do is to convert the ArrayList<Model> to MutableLiveData<ArrayList<Model>> to be send as a return value. Though i am getting the ArrayList<Model> result correctly,i failed miserably in posting the value to MutableLiveData<ArrayList<Model>>.
This is what i am trying to do...
suspend fun getSeasonBodyWeight(): MutableLiveData<ArrayList<MSBodyWeight>> {
val x = MutableLiveData<ArrayList<MSBodyWeight>>()
val y:ArrayList<MSBodyWeight> = getBWeightCoroutine()
x.postValue(y)
Log.i(TAG, "Check ${y.size}")
Log.i(TAG, "Check ${x.value}")
return x
}
This is what i getting in Logcat
I/FirebaseConnect: Check 2
I/FirebaseConnect: Check null
So what am i doing wrong. Also how to convert ArrayList<Model> to MutableLiveData<ArrayList<Model>>
I am trying to learn Kotlin.. Please bear with me if its a NOOB question.
Thanks
When using postValue and if you check the source code you will find in the method description:
Posts a task to a main thread to set the given value.
Means that the value will not be immediately set, it starts a task to change it. If you mean to change the value immediately you should use:
x.value = y
The difference between the two is that you cannot call setValue from a background thread, that meaning, if you are in a background thread you should call postValue. If you are in the main thread setValue may be used
suspend fun getSeasonBodyWeight(): MutableLiveData<ArrayList<MSBodyWeight>> {
val x = MutableLiveData<ArrayList<MSBodyWeight>>()
x.value = arrayListOf();
val y:ArrayList<MSBodyWeight> = getBWeightCoroutine()
x.postValue(y)
Log.i(TAG, "Check ${y.size}")
Log.i(TAG, "Check ${x.value}")
return x
}
Related
I have an app in which gives you a certain photo based on an integer being fetched via
class RandomImageLogic(){
fun retrive(): Int{
return (1..9).random()
}
}
However, it is not professional to have a repeated outcome, as I desire a random integer to be fetched each time I call the function in order for the image to be different each time my button is pressed. How can I fetch a new random integer whenever the button calls the function?
The easy way is to pass in the last random number you received and filter it out.
fun retrive(except: Int): Int{
return ((1..9).filter {it != except}).random();
}
In your case, this method might not be called extremely often (only when the user clicks a button).
If this method was to be called more often, filter on an IntRange should be used with care (as proposed in #avalerio's answer).
This iterates the whole range (unnecessarily costing time) and it would create a temporary ArrayList on every call (creating unnecessary garbage and triggering the garbage collector more often than needed).
Here is a sample object NonRepeatingRandom (you can also implement it as a class if you wish). retrieve (which has been expanded with a max parameter and basic sanity checks) recursivly calls itself again if the same number would be generated twice in a row:
object NonRepeatingRandom {
private var previous = -1
fun retrieve(max : Int = 9): Int {
if(max < 0) {
error("Only positive numbers")
}
if(max <= 1) {
// There is nothing random about 0 or 1, do not check against previous, just return
previous = max
return max
}
val rand = (1..9).random()
return if(rand == previous) {
retrieve(max) // recursive call if two subsequent retrieve() calls would return the same number
} else {
previous = rand // remember last random number
rand
}
}
}
fun main(args: Array<String>) {
repeat(1000) {
println(NonRepeatingRandom.retrieve())
}
}
I made a quick-and-dirty performance test, calling my "recursive" method 10 million times and calling the "filter" method 10 million times.
Recursive: 125 ms (10 mio calls)
Filter: 864 ms (10 mio calls)
Pre-fill & random shuffle approach:
class RandomIntIterator(
private val range: IntRange
) : Iterator<Int> {
private lateinit var iterator: Iterator<Int>
init { randomize() }
fun randomize() {
iterator = range.shuffled().iterator()
}
override fun hasNext() = iterator.hasNext()
override fun next() = iterator.next()
}
...
val rnd = RandomIntIterator(1..9)
...
// on button click
if (rnd.hasNext()) {
val num = rnd.next()
// use num
} else {
// renew (if needed)
rnd.randomize()
}
I like using Sequences to generate unending streams of values.
In this case we'll have to write custom code, as checking for repeated values is a stateful operation, and while Sequences has a distinct() stateful filter, it applies to all generated values - we only want it to apply to a limited window.
TL;DR:
class RandomImageLogic(
private val random: Random,
/** The number of sequential values that must be distinct */
noRepeatsLimit: Int = 2
) {
private val sourceValues: List<Int> = (0..9).toList()
private fun nextValue(vararg exclusions: Int): Int =
(sourceValues - exclusions.asList()).random(random)
private val randomInts: Iterator<Int> =
generateSequence({
// the initial value just has one random int
val next = nextValue()
ArrayDeque(listOf(next))
}) { previousValues ->
// generate the next value, excluding previous values
val nextValue = nextValue(*previousValues.toIntArray())
// limit the size of previousValues, if necessary
if (previousValues.size >= noRepeatsLimit)
previousValues.removeLastOrNull()
// add the generated value to the beginning of the deque
previousValues.addFirst(nextValue)
previousValues
}
.map {
// convert the Sequence to a list of ints,
// each element is the first item in the deque
it.first()
}
.iterator()
fun retrieve(): Int {
return randomInts.next()
}
}
Testing
Let's write a test first, to make sure our solution works. Kotest has a specific property based testing subproject, and this will let us cover a wide range of test cases very quickly.
So, I ran through the setup, and started setting up the test case.
Seeding RandomImageLogic
First I modified the RandomImageLogic class so that the random selection could be seeded with a provided Random.
import kotlin.random.Random
class RandomImageLogic(private val random: Random) {
fun retrieve(): Int {
return (1..9).random(random = random)
}
}
This will help us to create a Generator for the RandomImageLogic.
Testing all values
Now we can use Kotest to write a property-based test that will assert "for all sequential values, they are different"
import io.kotest.core.spec.style.FunSpec
import io.kotest.property.arbitrary.arbitrary
import io.kotest.property.forAll
class RandomImageLogicTest: FunSpec({
// This generator will create a new instance of `RandomImageLogic`
// and generate two sequential values.
val sequentialValuesArb = arbitrary { rs ->
val randomImageLogic = RandomImageLogic(rs.random)
val firstValue = randomImageLogic.retrieve()
val secondValue = randomImageLogic.retrieve()
firstValue to secondValue
}
test("expect sequential values are different") {
forAll(sequentialValuesArb) { (firstValue, secondValue) ->
firstValue != secondValue
}
}
})
Of course, the test fails.
Property failed after 4 attempts
Arg 0: (1, 1)
Repeat this test by using seed 1210584330919845105
Caused by org.opentest4j.AssertionFailedError: expected:<true> but was:<false>
So let's fix it!
Generating Sequences
As I said earlier, I really like Sequences. They're perfect for this use-case, where we have an infinite source of values.
To demonstrate how to make a Sequence, let's convert the existing code, and use an Iterator to fetch values.
class RandomImageLogic(private val random: Random) {
private val randomInts =
// generate a sequence using values from this lambda
generateSequence { (1..9).random(random = random) }
// use an iterator to fetch values
.iterator()
fun retrieve(): Int {
return randomInts.next()
}
}
This hasn't solved the problem yet - the Sequence only generates and provides one value at a time and so cannot do any filtering. Fortunately generateSequence() has an variant with nextFunction: (T) -> T?, where we can determine the next value based on the previous value.
If we use this constructor, and do a bit of refactoring to share the source values, and a util method to generate a next value while filtering out previous values...
private val sourceValues: List<Int> = (0..9).toList()
private fun nextValue(vararg exclusions: Int): Int =
(sourceValues - exclusions.asList()).random(random)
private val randomInts: Iterator<Int> =
generateSequence({ nextValue() }) { previousValue ->
nextValue(previousValue)
}
.iterator()
and now if we run the test, it passes!
Test Duration Result
expect sequential values are different 0.077s passed
Improvement: more than two distinct sequential values
What happens if you don't just want two sequential values to be distinct, but 3? Or even more? Let's make the 'no-repeated-values' limit configurable, and I think this will demonstrate why Sequences are a good solution.
class RandomImageLogic(
private val random: Random,
/** The number of sequential values that must be distinct */
noRepeatsLimit: Int = 2
) {
// ...
}
Testing
Once again, let's write a test to make sure things work as expected.
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.collections.shouldNotContainDuplicates
import io.kotest.property.Arb
import io.kotest.property.arbitrary.int
import io.kotest.property.checkAll
import kotlin.random.Random
class RandomImageLogicTest : FunSpec({
test("expect arbitrary sequential values are different") {
checkAll(Arb.int(), Arb.int(1..10)) { seed, noRepeatsLimit->
val randomImageLogic = RandomImageLogic(Random(seed), noRepeatsLimit)
val result = List(noRepeatsLimit) { randomImageLogic.retrieve() }
withClue("Result: $result") {
result shouldHaveSize noRepeatsLimit
result.shouldNotContainDuplicates()
}
}
}
})
And of course the test fails.
Property test failed for inputs
0) -459964888
1) 5
Caused by java.lang.AssertionError: Result: [3, 8, 0, 2, 8]
Collection should not contain duplicates
There's lots of options to make a Sequence stateful - again let's just look at one.
Sequence of values
Instead of a sequence of individual values, we can have a sequence where each element is a list of not only the current value, but also previously seen values.
Let's use ArrayDeque to store these values, because it's easy to add and remove values from the start and end.
Again, we use the same generateSequence constructor with a seedFunction and nextFunction - except this time each element is deque which stores all values, and in nextFunction we add new values to the start of the deque, trimming it if it's larger than the window size noRepeatsLimit
private val randomInts: Iterator<Int> =
generateSequence({
// the initial value just has one random int
val next = nextValue()
ArrayDeque(listOf(next))
}) { previousValues ->
// generate the next value, excluding previous values
val nextValue = nextValue(*previousValues.toIntArray())
// limit the size of previousValues, if necessary
if (previousValues.size >= noRepeatsLimit)
previousValues.removeLastOrNull()
// add the generated value to the beginning of the deque
previousValues.addFirst(nextValue)
previousValues
}
.map {
// convert the Sequence to a list of ints,
// each element is the first item in the deque
it.first()
}
.iterator()
And yup, the test passes!
Test Duration Result
expect arbitrary sequential values are different 0.210s passed
Storing state
It's important to think about how state will be stored. 'State' is required by RandomImageLogic to know whether a generated value is distinct.
In the Sequence implementation, it's stored internally, and so is specifically associated with an instance of RandomImageLogic. Maybe your application only has one instance of RandomImageLogic at any one time, in which case the state will always be up to date and will be shared between all invocations.
But what happens if there's more than one instance of RandomImageLogic? Or if there's multi-threading? Or if the RandomImageLogic instance is recreated?
The answers to these depend on the implementation and situation. From your question I suspect that it's not critically important that images never repeat, but I bring this up because it is important to think about.
fun SubmitOrder(view: View) {
/* pricing of coffee */
val total = quantity * 5
val s: String = ("$$total.00")
money.text = ("Total : $s\nThank You!").toString()
//This is calling On click listener
Toast.makeText(this, "order_Submitted", Toast.LENGTH_SHORT).show()
}
In this code, I need a new line before Thank You! in money.text but I am not getting any new line I am new in android development so, am not able to point out the mistake.
Let's go thru your code line by line:
val s: String = ("$$total.00")
s is a bad variable name, as it's not descriptive at all
you don't need the (braces)
the :String here is optional. In such an obvious case i would emit it.
A $ is a sign to the kotlin compiler to include the following variable. Therefore, you can't use the $ when you mean "US-Dollar". See this post on how to escape it
While ".00" works, it's no good style. I suggest you use string formatting as described here.
can be written as val s = "\$ ${String.format("%.2f", total)}"
you should wherever possible use string resources, but thats out of the scope of this answer
money.text = ("Total : $s\nThank You!").toString()
this is correct, but unnecessary verbose:
"Total : $s\nThank You!" is already a string, so there's no need to .toString()
braces are not needed
can be written as money.text = "Total : $s\nThank You!"
I've never saw this
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun eraseOldLocations() {
val a: ArrayList<Ub> =myUbs
val minTime: Long = (now- tenDaysago)
val z = misUbs.size
for(i in 0 until z){
if( myUbs[i].time <minTime) {
a.removeAt(i)
}
}
myUbs = a
}
each time the condition is fulfilled, the element in the position i of a is removed ... BUT also from the myUbs !!! myUbs is a public object, so as the loop continues, appears an error because the variable i exceeds the size of myUbs ...
The function eraseOldLocations() must delete elements with date older than ten days ago. Why is this happening?
Do a shallow copy of myUbs, so that references of list will differ and changes won't reflect to each other:
val a: MutableList<Ub> = myUbs.toMutableList()
MutableList is essentially the same, but if you want ArrayList explicitly (althrough not recommended in Kotlin) you can call ArrayList constructor to delegate to the Collection specified. That essentially does the shallow copy as well:
val a: ArrayList<Ub> = ArrayList(myUbs)
I don't know what mistake I am doing why it is returning null, I have seen other people problems I am not getting what I need exactly. I am sending the string data in this activity
val inspenctionIntent = Intent(this, InspectActivity::class.java)
inspenctionIntent.putExtra("Particulars", estimateItem.Particulars)
inspenctionIntent.putExtra("SSRItemNO", estimateItem.SSRItemNO)
inspenctionIntent.putExtra("Quantity", estimateItem.Quantity)
inspenctionIntent.putExtra("QuantityUnit", estimateItem.QuantityUnit)
inspenctionIntent.putExtra("Times", estimateItem.Times)
inspenctionIntent.putExtra("Rates", estimateItem.Rates)
inspenctionIntent.putExtra("RatesPer", estimateItem.RatesPer)
inspenctionIntent.putExtra("Total", estimateItem.Total)
startActivity(Intent(this#CheckMeasurementActivity, InspectActivity::class.java))
and trying to get that string extra in another activity but is returning null?
val Particulars = intent.getStringExtra("Particulars")
val SSRItemNO = intent.getStringExtra("SSRItemNO")
val Quantity = intent.getStringExtra("Quantity")
val QuantityUnit = intent.getStringExtra("QuantityUnit")
val Times = intent.getStringExtra("Times")
val Rates = intent.getStringExtra("Rates")
val RatesPer = intent.getStringExtra("RatesPer")
val Total = intent.getStringExtra("Total")
I tried by replacing var also still it is returning null?
Your error is this line.
startActivity(Intent(this#CheckMeasurementActivity, InspectActivity::class.java))
You declare inspenctionIntent but never use it, so others activity will get nothing since your intent send nothing to it.
So you need to change
startActivity(Intent(this#CheckMeasurementActivity, InspectActivity::class.java))
to
startActivity(inspenctionIntent)
You are started wrong intent so should use inspenctionIntent inside startActivity()
val inspenctionIntent = Intent(this, InspectActivity::class.java)
inspenctionIntent.putExtra("Particulars", estimateItem.Particulars)
inspenctionIntent.putExtra("SSRItemNO", estimateItem.SSRItemNO)
inspenctionIntent.putExtra("Quantity", estimateItem.Quantity)
inspenctionIntent.putExtra("QuantityUnit", estimateItem.QuantityUnit)
inspenctionIntent.putExtra("Times", estimateItem.Times)
inspenctionIntent.putExtra("Rates", estimateItem.Rates)
inspenctionIntent.putExtra("RatesPer", estimateItem.RatesPer)
inspenctionIntent.putExtra("Total", estimateItem.Total)
startActivity(inspenctionIntent)
You should write
Intent.putExtra("Particulars", estimateItem.Particulars)
Instead of
inspenctionIntent.putExtra("Particulars", estimateItem.Particulars)
and for others is the same.
Just change this-:
startActivity(Intent(this#CheckMeasurementActivity, InspectActivity::class.java))
to-:
startActivity(inspenctionIntent)
in last line of your code...
I am using the FileChannel.truncate() method to remove the last part of a file, from the text "metadata" until the end:
override fun truncate(file: File) {
val fileAsText = FileUtils.readFileToString(file, Charset.defaultCharset())
val metadataPosition = fileAsText.indexOf("metadata")
val channel = FileOutputStream(file, true).channel
channel.truncate(metadataPosition.toLong())
channel.close()
}
While debugging I can see that the variable metadataPosition has a certain value, which is correct when checking it in the fileAsText String. Note that the "metadata" text only appears once in the file. However, when the channel.truncate(metadataPosition.toLong()) line is executed, the truncation is performed in a previous position and the file is smaller than expected, losing some of the information.
I have no clue about the reason for this, what could it be?