Android: Kotlin Collections.random() is repeating random character sequences between app sessions - android

I am looking to generate a random alphanumeric value to append to a String for an Android app. We are appending 10 digits of randomness using Kotlins kotlin.collections.random
We are seeing intermittent collisions on this randomness within fairly small data sets (<10), and they only seem to occur between app sessions (e.g you can keep generating them without issue with no app restart).
Our extension method for appending randomness:
private val values = ('A'..'Z') + ('0'..'9')
internal fun String.appendRandomCharacters(charactersToAdd: Int): String {
val randomness = (1..charactersToAdd).map { values.random() }.joinToString("")
return this + randomness
}
Running this in tests I can generate a million random strings without issue or collision.
However I can see that identical random strings are generated with high frequency if the app is killed and rerun.
I have done a fair bit of googling without much success so am hoping StackOverflow might be able to shed some light.

I had the same problem, but magically it worked with java.util.Random class and with Kotlin i kept getting the same repeated 'random' values.

Related

How can we Generate Unique Number in android/Kotlin?

I was working on a project where we needed to generate a unique number for the Firebase Realtime Database. Now I want to generate a random 8- to 12-digit number that will be unique. Can anyone provide a suitable method/algorithm for obtaining the number, or can it be combined with string?
If you truly need a random number with NN digits you should use Kotlin's Random:
val random = abs((0..999999999999).random())
Of course, in this, 0 is a valid number in this sequence. So what do you do in in the 111 billion chances you get 0? well, that's up to you. You could change the range (but less numbers = less randomness)
You could "pad" 0s to your number so 123 becomes 000000000123 (as a String, that is). Ultimately what you do with the random number is up 2 you.
Keep in mind Random also takes a Seed.
So you could become more fancy by using the time at UTC as the seed (which is constantly changing every instant):
val random = abs(Random(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)).nextLong())
This will likely give you HUGE numbers so you should convert to string and take the last NN digits
If you get for eg.:
2272054910131780911
You can take: 910131780911
I have created a simple playground where you can see this in action. Please understand I made this in 10 minutes, so there may be optimizations all over the place, I don't have the Kotlin standard library in my head and the Playground's autocomplete is not the same as Android Studio.
Possibly look into SecureRandom -- Cryptographically strong random number generator (RNG): https://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html
Also, see this post regarding how long of a random number to generate by using SecureRandom: How to generate a SecureRandom string of length n in Java?
You can use Current Milli Second as Unique Number as follows
/**
* #param digit is to define how many Unique digit you wnat
* Such as 8, 10, 12 etc
* But digit must be Min 8 Max 12
*/
fun getUID(digit:Int):Long{
var currentMilliSeconds:String = ""+Calendar.getInstance().timeInMillis
var genDigit:Int = digit
if(genDigit<8)
genDigit = 8
if(genDigit>12)
genDigit = 12
var cut = currentMilliSeconds.length - genDigit
currentMilliSeconds = currentMilliSeconds.substring(cut);
return currentMilliSeconds.toLong()
}
Call it like
var UID = getUID(12)//parameter value can be 8-12

Weird crashes "java.lang.ArithmeticException divide by zero"

Recently I found out from Firebase Crashlytics that some HTC/Fortuneship
devices crashes with the next exception and I can't understand why
val formattedViews: String
get() = String.format(Locale.getDefault(), "%,d %s", viewCount, Util.getString(R.string.views))
I don't get how such exception can happen in this code
Is there something wrong with those devices?
This is a bug in the JDK shipping with Android 7: https://bugs.openjdk.java.net/browse/JDK-8167567
You are passing Locale.getDefault() to format(), which is the same as not specifying a locale at all. The only known workaround appears to be to use a known-good locale to do the formatting (e.g. Locale.US), but of course this means you won't get locale-specific thousands groupings for users outside the US.
Or you could change your format specification to not use groupings (i.e. %d without the comma).
Perhaps you could catch the exception and fall back to US formatting for users who would otherwise crash? That's what these guys did: https://github.com/wordpress-mobile/WordPress-Android/pull/5604/files

Random number gen w/ seed acting non-deterministic

I have programmed for many years, and the issue I am presenting now is probably one of the strangest I have come across.
There is a block of code in my app which randomly generates a sequence of tokens, with three possible types, let's say A, B or C.
So 10 tokens might be ABCCAAABAC.
At the beginning of the block of code, the random number generator seed is initialised like so:
math.randomseed(seed)
math.random()
Now, unsurprisingly when the seed value stays constant, I always get the same sequence of tokens, because the random generation code executes in a deterministic manner. Well, almost always.
Actually, on rare occasions, out of the blue, I will get a different random sequence given the same seed. Then it's back to normal before I know it. You're probably thinking - ah, side effects, this is probably a state related issue whereby the block of code which generates the random sequence of tokens utilises a variable which changes how many times it calls random() (for instance). However, I am 99% certain that I've controlled for all obvious side effects. There's only a few places in the block of code which access the external state, and they all remain constant.
The plot thickens even more - this issue has only been apparent to me on an Android deployment of the app that I've been building. Admittedly, this is a rare bug, and I can't seem to reliably repeat it. So it might also be present in the iOS deployments. But I've yet to come across it on other platforms. I might as well mention that I'm using lua scripting via the Corona SDK to develop the app.
I have given this issue much thought, and have narrowed it down to a few possibilities:
Interaction with another thread which uses the same random number generator, which I'm unaware of
(Is this even possible in lua?) Some sort of heap corruption is leading to strange side effects
I've messed up and there's some damn obvious reference to external state which I've missed throughout many hours of debugging
The most painful aspect of all this is the non-repeatability of the bug. Most of the time the block of code acts perfectly deterministically given a repeated seed. Then it is as though there's a phase of non-determinism, which then dissipates again after some unknown amount of time. I would love to pick the brains of an expert here.
What could be going on here? Also - is it possible there could be anything platform specific going on with this particular issue, since I've only seen it on Android deployments?
For reference, here is the full block of code. It's actually generating tokens with two random properties (one of three colours, and one of three shapes), but that doesn't mean much in terms of the essence of the problem.
math.randomseed(currentRandomSeed)
math.random()
local tokenListPlan = {}
-- randomly assign weighting distribution
local thresh1, thresh2
while (true) do
local s0 = math.random(1, 99)
local s1 = math.random(1, 99)
local c0 = s0
local c1 = s1 - c0
local c2 = 100 - c1 - c0
if (c0 >= eng.DEVIATION_THRESHOLD and c1 >= eng.DEVIATION_THRESHOLD and c2 >= eng.DEVIATION_THRESHOLD) then
thresh1 = c0
thresh2 = c0 + c1
break
end
end
-- generate tokens (deterministic based on seed)
for i = 1, sortedCountTarget do
local token
local c = 1
local rnd = math.random(1, 100)
if (rnd < thresh1) then -- skewed dist
c = 1
elseif (rnd < thresh2) then
c = 2
else
c = 3
end
if (paramGameMode == eng.GAME_MODE_COLOR) then
local rnd46 = math.random(4, 6)
token = {color = c, shape = rnd46}
elseif (paramGameMode == eng.GAME_MODE_SHAPE) then
local rnd13 = math.random(1, 3)
token = {color = rnd13, shape = c + 3}
else
local rnd13 = math.random(1, 3)
local rnd46 = math.random(4, 6)
token = {color = rnd13, shape = rnd46}
end
tokenListPlan[#tokenListPlan + 1] = token
end
https://docs.coronalabs.com/api/library/math/random.html states:
This function is an interface to the simple pseudo-random generator function rand provided by ANSI C. No guarantees can be given for its statistical properties.
This makes me wonder if other programs use the same function.
That could lead to those conflicts, while also more or less explaining why they only happen sometimes.

JodaTime: Print Period as Days:Hours:Minutes:Seconds

Oddly I'm having the inverse problem to many similar posts; I'm trying to use JodaTime (on Android) to give me a dd:hh:mm:ss type output and failing miserably. Code is;
public String GetEventTimeString(boolean withMS, int eventTime)
{
Period p = new Period(eventTime, PeriodType.yearMonthDayTime());
if (withMS)
return _formatter_withms.print(p);
else return _formatter_withoutms.print(p);
}
When I use the debugger to halt after p is set, I see that p itself has the hours value set to 568 and the days value is zero, so I've not posted the code that creates the formatters. eventTime is an int representing elapsed time in ms and has a value of 2045489151 in this case.
In case there's some oddity with the size of the number and the fact it's an int instead of a long, I tried making a new Period(1500000L, PeriodType.yearMonthDayTime()) and got a period of 0 days, 25 hours, 0 minutes and seconds.
Exploring the structure of the iType member of the period, I see an array iTypes containing elements "years", "months" etc. I also see an array iIndices which appears to map types to elements in some other array. Most indices make sense, but the index corresponding to "Days" is -1. So it seems PeriodType.yearMonthDayTime() isn't doing what I think the docs say it should.
If I try PeriodType.yearWeekDayTime I get the same result. If I try PeriodType.yearWeekDay then the three fields are zero with my 25 hour test value. So it's not just the one PeriodType that's misbehaving.
Wondering about the coincidence of getting exactly 25 hours from my fairly arbitrary (picked a number and kept adding zeroes) 1500000ms, I tried 1510000 and got 25h 10m 0s. But neither of those actually makes any sense when you work the conversion manually!
This is on Android, up to date Android Studio 1.3.2, not sure how to tell what version JodaTime I'm using but it's within the last few months and this seems a bit too glaringly obvious to be an obscure bug- more a combination of misunderstanding and digging too deep on my part I think.
Does anyone have any thoughts on what's going on?
Well, Joda-Time says about the normalization of a millisecond-based period:
Creates a period from the given millisecond duration. Only precise
fields in the period type will be used. Imprecise fields will not be
populated.
Here the question arises: Is the day unit a precise field in context of Joda-Time? No because a day can have 23 or 25 hours when it comes to switching daylight-saving time on/off in many countries (in reality there are even more possibilities like 23.5 hours etc). So the constructor you call does not fill the day-unit-field.
How can you get the desired normalization effect? Just do this recommended approach (no normalization in constructing the period):
long eventTime = 2045489151;
Period p = new Period(eventTime, PeriodType.millis());
System.out.println(p); // PT2045489.151S (no normalization)
System.out.println(p.normalizedStandard(PeriodType.dayTime()));
// P23DT16H11M29.151S
System.out.println(p.normalizedStandard(PeriodType.yearMonthDayTime()));
// P23DT16H11M29.151S ( the same result as one line before, not the best period type)
It is for debate IMHO that the Period-constructor, you used does a partial normalization. In my opinion either a full normalization using the given period type or no normalization at all (preferred by me - just leaving the milliseconds as millis, but then the second constructor argument is simply silly) would have been a wiser design decision. Interestingly (and confusing, too), the constructor without explicit period type argument does a partial normalization, too. To avoid premature incomplete normalization in constructor, you have to explicitly specify the period type as PeriodType.millis(). Sorry for your confusion.

How to send a list of items to the watch from PebbleKit for Android

I have an android app that is essentially a list of timers. Each timer can have the following fields:
title (string, can be up to 255 characters)
id (integer)
seconds (integer)
time_started (integer)
seconds_left (integer)
running (boolean)
order (integer)
There can be an unlimited number of these timers, though for the pebble watch app, it'd be ok if I only send the first n (10, 15, 20).
Currently I've been sending the items to the watch one at a time, and creating the PebbleDictionary like this:
private PebbleDictionary buildTimerDictionary(Timer timer) {
PebbleDictionary data = new PebbleDictionary();
data.addUint32(C.KEY_ID, timer.getId());
data.addUint32(C.KEY_SECONDS, (int)timer.getSeconds());
data.addString(C.KEY_DESCRIPTION, timer.getDescriptionFormatted());
data.addUint32(C.KEY_TIME_STARTED, (int)timer.getTimeStarted());
data.addUint32(C.KEY_TIME_LEFT, (int)timer.getSecondsLeft());
data.addUint8(C.KEY_RUNNING, (byte)(timer.isRunning() ? 1 : 0));
data.addUint32(C.KEY_ORDER, (int)timer.getOrder());
data.addString(C.KEY_TIME_DISPLAY, timer.getSecondsFormatted());
return data;
}
And sending it via a queue that sends the next PebbleDictionary to the watch after the previous is acked.
This works, but it's pretty slow. I feel like I could save a lot of time by packing more than one timer into each message. However, I'm not sure how to do that, considering the inbox size on the pebble watch itself, and the fact that you have to hard define the keys for the dictionary in appinfo.json (it doesn't seem like you can use arbitrary keys).
How is this sort of thing usually done?
There are two ways to do this:
One message for each item (what you are doing now)
Multiple items per message.
In this case you can pack everything into one value by using byte arrays and concatenating all your fields into the byte array.
You can then use the key to send the index of the element in the list.
A few comments:
Defining the keys in appinfo.json is optional and only useful if you are using PebbleKit JavaScript. It has absolutely no use for apps that talk with PebbleKit Android.
You can query the available buffer size on Pebble with app_message_inbox_size_maximum(). Get it when you start your app and send it from Pebble to the Android app.
The best strategy depends on the average size of your messages. Right now your items will be about 25bytes + the strings + the small overhead of a dictionary (7 bytes + 1 byte per key). If the strings are very small you might fit three items per messages, if the description is very long, you might not be able to send the item at all (you should probably truncate it).

Categories

Resources