In the MVVM structure, while using the MutableLiveData, even I put an initial value, I got the warning of Unboxing of 'loream.getValue()' may produce 'NullPointerException'. What is correct way of using getValue().
If you look at the source code of LiveData#getValue(), you can see it can return null:
#SuppressWarnings("unchecked")
#Nullable
public T getValue() {
Object data = mData;
if (data != NOT_SET) {
return (T) data;
}
return null;
}
So you always have to account for that fact. You didn't specify if you were using Kotlin or Java (or how you're using getValue() for the matter), so it's hard to tell.
In general, the value(s) of a LiveData stream are often observed and so you wouldn't likely get a null value if you don't "post (or set)" one.
Post a more concrete example if you're looking for specific advice.
If you need a sample, Google has one here.
Related
I must be doing something wrong with Kotlin implementation of view models
I have a view model that has a function to retrieve youtube video id from url.
fun getYoutubeVideoId(url: String): String?{
return "([a-zA-Z0-9_-]{11})".toRegex().find(url)?.value
}
I feel like I'm always in catch 22 because I use this function in a fragment inside with LiveData observable, which forces me to to ? on objects, which then forces me to have return type with ?, which then tirggers if statements to check if objects aren't null.
Here is the vm var
val streamUrl= mainState.getOrNull { it?.account?.streamUrl ?: 0}.distinctUntilChanged()
Here is my shortened observable
streamUrl.observe{
playVideo(getYoutubeVideoId(it))
}
The error from above statement is that it
Requires a String and I'm passing Any
Return should be String and its String?
I'm running around to make sure the types match and its always something not matching or being right. I think I could setup another streamUrl variable under the viewModel besides the observable, but I feel like I should be able to just do it of a single variable.
I hope this makes sense.
So the first thing to embrace with kotlin is: Null Safety.
Null Safety does not mean that you do not get nulls.
It means, that if something is possibly null, the compiler forces you to think about it and handle it at a point that makes sense. If you don't, you potentially get the notorious NullPointerException at an unexpected and possibly ugly point of execution.
So, to eliminate the ? think about where you want to handle the possibility of it being null -> check it -> handle it in an elegant way, and then safely pass the checked result without a ? to the rest of your code.
This question already has answers here:
What's the difference between !! and ? in Kotlin?
(6 answers)
Closed 3 years ago.
I am a bit confused about the usage of ? and !! in the following instance.
lat = mLastLocation?.latitude.toString()
longi = mLastLocation!!.longitude.toString()
Which null-safety operator should I be using?
TL;DR:
?. operator is safe. Use it when you are unsure about the chain nullability.
!!. operator is meant to be used only when you are sure that the previous chain operation result is not null. Otherwise, crash.
if mLastLocation is never null, feel safe about using !!. (And about rethinking a little bit your code), otherwise, use ?.
Introduction
You have hit one of the best (and most useful) points while coding in Kotlin.
here which null safety operator I should use?
It depends on the behavior you want to achieve.
In Kotlin, you have to be very specific about what you want to do with null values, because the language is designed to be null-safe out of the box.
Of course, targeting the JVM brings many challenges to a programming language. The eventuality of having null values is one of these, and Kotlin, as we'll see, handles this in a really smart manner.
Purpose
We could explain the full theory behind those two operators, but I believe an example is really all you need.
Suppose you have a class, called Location, which we will declare in a nullable variable.
In Kotlin, this is represented as val location: Location?
Let's also say Location class has a property called lat, which is a nullable String, and a lon non-nullable String.
data class User(val lat: String?, val lon: String)
Operator ?.
Kotlin Safe Call Operator Docs
This operator is the safe call operator.
If you use it in a call chain, it is checking that your code chain goes onto the next element just if the previous element is not null. Otherwise, null is retuned from the statement.
val location: Location? = getLocation()
println(location.lat) // Compile-time error.
println(location?.lat) // Works fine.
This happens because in the first case, the object before ?. is nullable, thus the Kotlin Compiler infers that accessing a nullable property can lead to NPEs.
location could be null or not-null.We just don't know what it will be, and the Kotlin environment strictly makes sure that you are handling the eventuality of that value being null, as the type of our references variable is defined as nullable.
However, a certain variable being null is something you developer may not know. Sometimes it is not even up to you to receive a null or non-null value.
In this case you can safely stick with ?, knowing that this operator is your friend if you are unsure about whether what you're referencing will be null.
val location: Location = getSafeLocation()
val nullableLocation: Location? = getLocation()
// Fine, may print "null" or the value, if present.
// println accepts nullable values
println(location.lar)
// 100% sure it'll print the corrisponding String value
println(location.lon)
// May print "null", "null", or the lat String value.
// The first "null" is because the ? operator will check if
// nullableLocation is null. If it is, it stops the execution
// chain and returns null. Otherwise, it assumes nullableLocation is safe
// and goes on.
//
// The second "null" is because the value itself of lat
// is declared as String? and Kotlin knows it may be null.
// If println() did not accept null values, this call would fail,
// but instead it prints "null" in case lat is indeed null.
println(nullableLocation?.lat)
// Since lat was the last element of the chain, it is not
// delivered as the parameter type anymore, and thus if we
// want to read a property from it we have to ensure that it isn't null.
println(nullableLocation?.lat?.length)
// This, as you may guess, returns wither null in case nullableLocation is null,
// otherwise 100% sure it will print lon value, since it is not a String? but a String.
println(nullableLocation?.lon)
Operator !!.
Kotlin Double-Bang Operator Docs
This is the dreaded double-bang operator.
Talking about syntax, it is very similar to ?., since it is used in the same place.
To describe it in a really simple way: if anything before the call is null, your code will crash. Instantly. Without warnings.
With !!. you're explicitly saying that
Kotlin has to ignore any type nullability marker and to perform the operation you intend, even though it enters in a kind of danger zone.
This is known as a force, because you're forcing the Kotlin environment to believe that the previous statement is not null.
This operator best use case is when porting another library to Kotlin, or while handling API RESTful responses, situations where null values may come in, but because of environment/platform reasons, you know that some value can not be null. This helps you bringing type safety in the Kotlin world in the first place, if used properly.
But for mainstream software, this feature is meant for a really specific and narrow usage: If you are 100% sure that the previous call is not null, go ahead.
val location: Location? = getLocation()
println(location!!.lon)
The previous code may crash if location is
Which one to use
Both operators are type-transforming. They both turn nullable values into non-nullable ones. The way the do it is the changing factor.
As a general rule, if you're sure the value you are targeting is not null, use !!., otherwise stick with ?.
if you define a variable as
var myFirstVar:String? = null
this means that "myFirstVar" can have a null value and when you use "myFirstVar" you should indicate whether or not it has a null value
myFirstVar!!.toString
in here you're saying that you're 100% that myFirstVar will not be null (maybe you've given it a value before calling it)
but if you use ?
myFirstVar?.toString
you're indicating that myFirstVar might have a null value, the ? will chick if myFirstVar is null or not, if it is then it will not convert it to string (the application will not crash) and if it wasn't then it will convert it to string, it is a safety check to reduce the null crashes.
If a variable is declared as nullable type, you have two options when using it.
Let's take this for example:
private var myButton: Button? = null
So you have two option for the myButton. You can evaluate it, or keep it as it is. But the program on the run doesn't know what you have done with the variable before. So, in order to be safe Kotlin language provides you with ? and !! operators. One is for the safety of program, so it won't crash and cause a KNPE:
myButton?.setOnClickListener{
}
If the button is null, the app won't crash. Now, if you are 100% sure that you have evaluated the Button with a value different from null, you can use !!:
myButton!!.setOnClickListener{
}
In this case, if you run the program and the myButton is null, you will have a crash.
Null Safety Myth
However, this is not a null safety case (I guess). What people mean by null safety in Kotlin is exactly this:
private val myButton: Button = someButtonInitialization()
If you evaluate it to null, the compiler would yell at you because Button is a non nullable type. Otherwise it would be Button?.
This is null safety IMO, and not !! or ?.
Special case: You can have:
private lateinit var myButton: Button
If you never evaluate the myButton you will never have KNPE, but an UninitializedPropertyException which has nothing to do with Null threat or null safety.
Let's have an example
var a: String? = "Hello world!"
fun test1() {
a?.trim()
}
fun test2() {
a!!.trim()
}
The first decompiled function is:
public static final void test1() {
String var10000 = a;
if (var10000 != null) {
String var0 = var10000;
StringsKt.trim((CharSequence)var0).toString();
}
}
The second decompiled function is:
public static final void test2() {
String var10000 = a;
if (var10000 == null) {
Intrinsics.throwNpe();
}
String var0 = var10000;
StringsKt.trim((CharSequence)var0).toString();
}
Where Intrinsics.throwNpe(); is defined as:
public static void throwNpe() {
throw sanitizeStackTrace(new KotlinNullPointerException());
}
So a?.trim() will do nothing if var a was null
So a!!.trim() will throw an exception if var a was null
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();
}
What's an idiomatic way to do code this in Kotlin?
private var someVar: SomeClass? = null
private fun getSomeVar(): SomeClass {
if (someVar == null) {
someVar = getDefaultSomeVar()
}
return someVar
}
Android Studio warns me about the return type. In Java this code is, however, proper and idiomatic. I mean, instead of changing the return type to SomeClass? maybe there's still a better way?
Actually, getSomeVar() can never return null
The compiler complains because, theoretically, a different thread could change someVar in between the assignment and the return statement.
The idiomatic solution here would be to use property delegation:
private val someVar: SomeClass by lazy { getDefaultSomeVar() }
This initializes the property when it is first accessed in a thread safe manner. Also note that it is now a non-nullable val, instead of a nullable var, which makes it generally easier to work with.
You do lose the ability to modify it later on. If it needs to be mutable you currently have to make that yourself. For an example implementation see this SO question: Kotlin lazy default property
The following two solutions take the method in the question (the 'java way') for granted and just show a way to prevent the compiler warning. However, in your situation these are not advised as they both have drawbacks over the lazy initialized property:
1) Introduce a local variable. This variable is safe from being mutated by other threads and allows the compiler to do a Smart Cast:
private fun getSomeVar(): SomeClass {
var value = someVar
if(value == null) {
value = getDefaultSomeVar()
someVar = value
}
return value
}
The method itself is however still not thread safe. In a multithreaded environment, getDefaultSomeVar() could be called multiple times and it is not guaranteed that the returned value of this method is equal to someVar.
2) Use !!: the double bang operator. This converts a nullable type to non-nullable. But now you lose the protection and null safety that the kotlin compiler enforces on you.
return someVar!!
As the documentation puts it: 'If you want an NPE, you can have it'
You can write:
return someVar!!
this will return a non-null value, however if it's null it will throw an NPE.
It could be shorter and without any warnings
private fun getSomeVar(): SomeClass {
return someVar?:getDefaultSomeVar()
}
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.