Can I always merge two conditions into one row in Kotlin? - android

The Code A is good, I hope to optimize it, so I write the Code B.
I'm not sure whether the Code B is always correct.
It will be OK if Kotlin check clipboard.hasPrimaryClip() first, then check clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN) next.
It maybe crash if Kotlin check clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN) first, the check clipboard.hasPrimaryClip() next, right?
Code A
clipboard.addPrimaryClipChangedListener {
if (clipboard.hasPrimaryClip() ) {
if (clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) {
}
}
}
Code B
clipboard.addPrimaryClipChangedListener {
if (clipboard.hasPrimaryClip() && clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN) ) {
}
}

if conditions run sequentially. That means it will first check the left condition and if the operator is AND and left condition return false then it won't check the right condition. So yes, you can merge two conditions.

As mentioned in the comments, the principle behind this is described as "short circuiting":
Short-circuit evaluation [...] is the semantics of some Boolean operators in some programming languages in which the second argument is executed or evaluated only if the first argument does not suffice to determine the value of the expression.
That means clipboard.hasPrimaryClip() will always be evaluated. If it's false
, the condition fails without looking any further. If it is true though, clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN) will be evaluated as well.

Related

How to make LaunchedEffect run once and never again?

I know that LaunchedEffect(key) is executed when composition starts, and is also cancelled and re-executed again when the key changes. However, I want it to be executed only once and never again, I am using the following workaround but I feel like I am missing something:
if (!launchedBefore) {
LaunchedEffect(null) {
//stuff
launchedBefore = true
}
}
The code above works just fine. But it feels like a stretched workaround while something much simpler can be done. Am I misunderstanding how LaunchedEffect fully works?
I tried using null and Unit as keys cuz they never changed, but the code is executed every time a composition takes place.
LaunchedEffect(Unit) should execute only once when the composition starts. The only time it would get re-executed during recomposition is if it gets removed from the view tree during one of the previous recompositions. An example would be if you have it within a condition whose value changes at some point (in an if block, when block or any other conditional statement).
I would assume that the problem with recomposing lies in the other part of the code that is not shown in your snippet. Check if the LaunchedEffect is nested in a conditional block that might cause it to get executed after a recomposition.
The problem is not in the LaunchEffect or its key parameter, but in the upper composable. The upper composable is getting recomposed. Probably you should display more details how the LaunchEffect is called and calling sites.
Recomposition looks up for nearest composable that could have been affected by the change.
I usually pass a longer-living variable for the key, something like ViewModel. It will only executed when the ViewModel is being initialized. Or using remembered saveable boolean may do the trick.
val bool = rememberSaveable { true }
LaunchedEffect(key1 = bool) {
// do something
}

Kotlin when with multiple outcomes for each case

I just started learning Kotlin and I don't know to use when properly. When number is 1, the toast is there, but the image (the second 1 case) is not changed. Is there a way to do this with when?
when (number){
1 -> Toast.makeText(applicationContext,"1",Toast.LENGTH_SHORT).show()
1 -> imgview.setImageResource(R.drawable.dice_1)
2 -> Toast.makeText(applicationContext,"SAD",Toast.LENGTH_SHORT).show()
2 -> imgview.setImageResource(R.drawable.dice_2)
}
Sure, just wrap each set of statements in curly braces:
when (number){
1 -> {
Toast.makeText(applicationContext,"1",Toast.LENGTH_SHORT).show()
imgview.setImageResource(R.drawable.dice_1)
}
2 -> {
Toast.makeText(applicationContext,"SAD",Toast.LENGTH_SHORT).show()
imgview.setImageResource(R.drawable.dice_2)
}
}
when statement in Kotlin is similar to switch statement in Java. As soon as a matching case is found in the when statement, the control returns from the statement.
So in your code, the first 1 case gets executed and the control returns from the statement so it never goes to the second 1 case. I don't know your purpose for duplicating these cases instead of grouping them as shown in the below code:
when (number){
1 -> {
Toast.makeText(applicationContext,"1",Toast.LENGTH_SHORT).show()
imgview.setImageResource(R.drawable.dice_1)
}
2 -> {
Toast.makeText(applicationContext,"SAD",Toast.LENGTH_SHORT).show()
imgview.setImageResource(R.drawable.dice_2)
}
}
when (number){
1 -> {
Toast.makeText(applicationContext,"1",Toast.LENGTH_SHORT).show()
imgview.setImageResource(R.drawable.dice_1)}
2 ->{ Toast.makeText(applicationContext,"SAD",Toast.LENGTH_SHORT).show()
imgview.setImageResource(R.drawable.dice_2) }
else->{
//if both fail to mach
}
}
Add curly braces to define scope.
when looks for the first condition that matches, so once you hit the first 1 branch, it executes the code and then exits the block. It doesn't fall through to the others - you can think of it like a big if/else if/else if/else block, where the later ones can only trigger if nothing before them did.
So you need to execute all your code in the first branch that matches, like the other answers are showing!

what does it. mean in the context of alos operator

I am learning how to use the coroutines in kotlin. looking at some examples in the internet i found that within the context f the also operator the reference
it
is used. i could not find any explanation about the meaning of
it
please provide some brief explanantion about what does "it" mean
when you use the also method, it has 1 parameter.
Think of it in Java kinda like this:
foo.also(int it) {
// do stuff
}
In Kotlin, the it parameter is implicit (sometimes you might want to use it sometimes you don't).
If you want to rename it to something more readable you can
foo.also { newName ->
// do stuff with newName
}
Or just use it like it is
foo.also {
// do stuff with $it
}
So therefore when you are using a method (or a closure/lambda) if it has 1 parameter, then the implicit name of that parameter is always it.
Basically it represents the lambda parameter
let's say you want to perform anything on the variable but do to check the nullity first, you can do it like
var str:String?=null // str is of string type
now you can use it fail safe
str?.let{it:String// youll see like this
// now you can access str as **it**
}
it is the implicit name of a single parameter
For more information about it and this in scoping functions like also

RxJava2 Single.Concat for repository pattern

I am using Room with RxJava2 to implement my data layer via Repository Pattern principles.
I have the following simple code which decides where to pick data from.
#Override
public Single<Team> getTeamById(int teamId) {
return Single.
concat(local.getTeamById(teamId),
remote.getTeamById(teamId)).
filter(team -> team != null).
firstOrError();
}
The problem here is that instead of going to the remote source , it returns an error from the first source (local) if the data was not available.
android.arch.persistence.room.EmptyResultSetException: Query returned empty result set: select * from teams where id = ?
How should I instruct the concat to forgo any error that is received and continue its concatenation?
Aslong you're not sure if you can receive at least one Team from you data provider, you should probably think of using Maybe instead of Single.
You can lookup the definition here:
Single as it states:
it always either emits one value or an error notification
Use Maybe instead:
Maybe
there could be 0 or 1 item or an error signalled by some reactive
source
As your error already states there seems to be a problem while extracting results from your query.
Handle your result extraction correctly, so that you check if there are results before trying extracting any. Therefor the Maybe would either return 0 or 1 item, and not throw any error at all when no Team was found.
You cannot pass null in RxJava2. So whenever your local repo is empty you just can't return null in your single. There was a question o stack about handling null objects: Handle null in RxJava2
Also here you can find an article showing you preferred implementation of repository pattern using RxJava2:
https://android.jlelse.eu/rxjava-2-single-concat-sample-for-repository-pattern-1873c456227a
So simplifying - instead of returning null from both local and remote repo pass some sort of "empty" object. That will be useful also in your business logic allowing you to recognize empty set of data.
If you want to continue when the first source errors (instead of completing as empty), you can use onErrorResumeNext instead of concat (I assume both get calls return Observable, adjust as necessary):
return local.getTeamById(teamId)
.onErrorResumeNext(error -> {
if (error instanceof EmptyResultSetException) {
return remote.getTeamById(teamId));
}
return Observable.error(error);
})
.firstOrError();
I used Maybe to solve my Rxjava2 repository pattern problem.
In your case, I would use the following code to sort it out:
//you may need to rewrite your local.getTeamById method
protected Maybe<Team> getTeamById(int teamId) {
Team team = localDataHelper.getTeamById(teamId);
return team != null ? Maybe.just(team) : Maybe.empty();
}
#Override
public Single<Team> getTeamById(int teamId) {
Maybe<Team> cacheObservable = local.getTeamById(teamId);
Maybe<Team> apiCallObservable = remote.getTeamById(teamId).toMaybe();
return Maybe.concat(cacheObservable, apiCallObservable)
.toSingle();
}

Rx-java pass by reference or pass by value?

In java methods everything is passed-by-value so i can change the object attributes passed to the method and expect that the original object attributes are changed. but in this method i get different result:
I have this method:
public Observable<Menu> makeMenu(Menu menu, NumberSettingChanges.MenuChanges changes) {
// Start flow with added and edited extensions
return Observable.from(changes.added.entrySet())
.mergeWith(Observable.from(changes.edited.entrySet()))
//Upload announcement voices or do nothing if extension is not an announcement
.flatMap(e -> {
if (AppTypeContract.APP_TYPE_ANNOUNCEMENT.equals(e.getValue().type)) {
return mMediaManager.uploadAsync(e.getValue().config.localPrompt)
.doOnNext(response -> {
//Update extension prompt with the storage path.
menu.config.extensions.get(e.getKey()).config.prompt = response.mPath;
menu.config.extensions.get(e.getKey()).config.localPrompt = "";
})
.flatMap(response -> Observable.just(e));
} else {
return Observable.just(e);
}
}
)
}
and i manipulate menu attributes in the flatmap:
menu.config.extensions.get(e.getKey()).config.localPrompt = "";
I call the method in the same class:
public Observable<NumberSetting> saveSettings(NumberSetting o, NumberSetting n) {
NumberSettingChanges changes = compareNumberSetting(o, n);
return makeMenu(n.day, changes.day)
.mergeWith(makeMenu(n.night, changes.night));
}
and finally:
saveSettings(ns, mNumberSettingNew).subscribe();
What i expect is that the mNumberSettingNew.menu.config.extensions.get(e.getKey()).config.prompt is changed but no change is happening after this call and the mNumberSettingNew has no change at all.
Note that i am sure that changing prompt line is done in the debug.
I don't think I could explain Java's parameter semantics any better than (or even half as good as) the link you referenced in your first paragraph so I won't try. The main point is: Everything in Java is passed by value (i. e. copied) but with objects what is copied is not the object itself but the reference to the object. So in other words the reference is passed by value.
So with respect to your particular problem: Yes, if you pass a reference to a mutable object to some rx-java code that reference will point to the same instance of the object. If you mutate the instance then the caller code will also be able to see the changes because they were made on the same instance. That's because rx-java is still only Java and cannot change the language semantics on that level.
Without seeing the whole code I am unsure what could be the problem here... When are you checking whether mNumberSettingsNew actually has the changes you were making in your doOnNext? If you check that immediately after saveSettings(ns, mNumberSettingNew).subscribe(); your uploadAsync may not have returned yet. You could try adding an actual Subscriber in your subscribe and check the result there.
On a more general note, I think you should try to avoid side-effects like this as much as you can when using rx-java. Your case - taking an input object, applying a set of (possibly asynchronous) changes to that object, and waiting for the changed output object - is a bit tricky, but I think it could be done with scan. Maybe something vaguely like this:
Observable.from(changes.added.entrySet())
.mergeWith(Observable.from(changes.edited.entrySet()))
.scan(menuBeforeAnyChanges, new Func2<Menu, Change, Menu>() {
public Menu call(final Menu previousVersionOfTheMenu, final Change nextChange) {
// since I don't know of a version of scan that can return
// an Observable you would I think you would have to adapt
// your code in here to be fully synchronous - but of
// course the scan itself could run asynchronously
final newVersionOfTheMenu = previousVersionOfTheMenu.applyChange(nextChange);
return newVersionOfTheMenu;
}
)
This would take the original Version of the menu, consecutively apply all the changes from added and edited and /emit/ every updated version of menu. So you would not have any side effects but simply subscribe to that observable with a Subscriber<Menu> and then take the last() Menu and that would be the one with all changes applied.
EDIT: Oh, I just saw that there is another method called reduce that does just that: first scan and then last or takeLast.

Categories

Resources