Lateinit properties were not initialized on Fragment - android

I have write my first Kotlin and android app and i'm facing a lot of crashes on this app.
All of them are related to the lateinitkeyword.
i get crashes like :
Caused by e.y: lateinit property coordinator has not been initialized
and:
Fatal Exception: java.lang.RuntimeException
Unable to start activity ComponentInfo{app.myapp/mypackage.myapp.Controllers.MainActivity}: e.y: lateinit property coordinator has not been initialized
Caused by e.y
lateinit property coordinator has not been initialized
myapp.com.myapp.Controllers.Fragments.Parents.MyParentFragment.getCoordinator
Last one trace for example is related to a variable i set on my fragment once i initialize it like this :
fun newInstance(mainObject: MyObject, anotherObject: AnotherObject, coordinator: FragmentCoordinator): MyFragment {
val fragment = MyFragment()
fragment.mainObject = mainObject
fragment.anotherObject = ticket
fragment.coordinator = coordinator
return fragment
}
Fragment side looks like :
class MyFragment: MyParentFragment() {
companion object {
fun newInstance(mainObject:...)
}
lateinit var mainObject: MainObject
lateinit var anotherObject: AnotherObject
...
}
What i understand is that when app changes its state (background...) this lateinit properties reference get lost, once the code call this variable property is null and app crash... Please understand that once they fragment is created all this variable are not null, they get allocated.
i have found this article : https://www.bignerdranch.com/blog/kotlin-when-to-use-lazy-or-lateinit/ which indicates what happened and can be fixed linking the variable to the app lifecycle using by lifecycleAwareLazy(lifecycle) thing is at the ends of the article he adds: These two property delegates solve one problem, but not completely.
They still contain a memory leak. Why does he says "they still contains memory leaks ?"
So how on android i can be sure that my variable always be set wether the app comes back from background or whatever, because this happen when the fragment is displayed by the app, some codes run good and then needs to call this lateinit variable and crash because this variable does not exist anymore but existed once the fragment where create.
Thank you for any help.

When your activity gets recreated, FragmentManager invokes 0-argument constructor of each attached Fragment to recreate them as well.
Your newInstance method should only create a Bundle and use it in Fragment.setArguments(Bundle) as this is the only way to ensure those arguments survive configuration changes. Then inside onCreate you can getArguments to retrieve that Bundle.
If you need arguments that cannot be put in a Bundle, you have to inject / recreate / fetch them inside Fragments onCreate or other method.

lateinit indicates that you are not assign the variable value at declaration time but you have to assign the value late in your java class before uses it.
If you tries to get the value of lateinit variable before assignment than lateinit property coordinator has not been initialized exception is occur.
If you are not sure that you lateinit varibale is assign or not you can check the value using
isInitialized method given for lateinit variable to avoid crashes.
Example :
if(::varibleName.isInitialized){
// do something
}
If you want to save the variables values after configuration changes you have to use ViewModel.
If you not uses ViewModel than pass the values by bundle and get the values again from it and re-assign the variables, but note that if you are passing too many values using bundle than you will get the TooLargeTransition exception. You can also used onSaveInstanceState to store the updated values and retrieve it in onCreate method.

fun newInstance(mainObject: MyObject, anotherObject: AnotherObject, coordinator: FragmentCoordinator): MyFragment {
val fragment = MyFragment()
fragment.mainObject = mainObject
fragment.anotherObject = ticket
fragment.coordinator = coordinator
return fragment
}
You cannot create a Fragment like this, you cannot set field variables like this.
The system recreates fragments after low memory condition using no-arg constructor via reflection. Your code, here, newInstance and stuff - that never runs. Never.
On another hand, setArguments(Bundle arguments are retained by the system, and you would be able to get them using getArguments().
So currently, if your app is put to background, killed by Android, and you open the app from launcher again, then you'll crash.
See related questions:
Singleton object becomes null after app is resumed (where I describe how you can reproduce this in dev environment easily)
also maybe App crash after activity has been killed in background

Related

What does "activity?." mean in Kotlin?

I am trying to use a code snippet from the official android documentation (https://developer.android.com/training/printing/photos#kotlin) which is the doPhotoPrint() function in the code that I have attached, in order to learn how to use the PrintHelper class in Kotlin and Android Studio. See the attached image of the the code snippet:
The problem is that when I put the code in Main Activity in my test app, it keeps showing "activity?." as red. Why is this the case and how can I get the code to work so that is provides the user with the option to print? Thanks
The code you linked is just a general how to use a library function snippet - it's not put in any context, but we can assume it's probably written with a Fragment in mind. Fragments have a getActivity() method that returns the Activity it's currently attached to (or null if it's not)
Kotlin allows you to access getter and setter functions from Java code as if they were properties - so basically, instead of having to do value = getThing()
and setThing(newValue) you can treat it like a variable: value = thing, thing = newValue etc. So when you access the activity property in this code, it's really calling getActivity(). And because the result can be null, you use the ? to null-check it before trying to do anything with it.
Really what that snippet is saying is, this code needs access to a Context, and it's using an Activity as one in the example. If you have a Fragment, you can just drop this code right in. If you're in an Activity though, then you obviously don't need to get one - this is your Activity! And you don't need to null-check it either of course. (And there's no activity property or getActivity() method to access, which is why it's showing up red and unrecognised)
So you can just replace that stuff and the checking code around it with:
private fun doPhotoPrint() {
// using 'this' to pass the current activity as the context
val printHelper = PrintHelper(this).apply {
scaleMode = PrintHelper.SCALE_MODE_FIT
}
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.droids)
printHelper.printBitmap("droids.jpg - test print", bitmap)
}
It is showing red because you have not declared the nullable object activity anyway before you use it

Why we have to add get() after a private var in fragment for view binding?

I was following the google codelab. There I came across the following code:
class Calculator: Fragment() {
private var _binding: FragmentCalculatorBinding? = null
private val binding get() = _binding!!
}
What is the need for the get()?
We can do this in the following way:
class Calculator: Fragment() {
private var _binding: FragmentCalculatorBinding? = null
private val binding = _binding!!
}
The explaination given there was:
Here, get() means this property is "get-only". That means you can get the value, but once assigned (as it is here), you can't assign it to something else.
but I don't understand it. Please help me with this.
The whole point in having a second property here is to allow the first property to be set back to null. (The second property is for convenience and should only be used when the fragment is known to be attached to an Activity.) Using a getter means it does the evaluation _binding!! each time it is accessed. Without get() it evaluates it once when the class is instantiated and assigns the result to a backing field. Since _binding is null at class instantiation time, this would be guaranteed to fail. And even if it didn’t fail, it would have an outdated reference if the fragment got detached and reattached.
Your description of what “get only” means is inverted. Either the code lab got their explanation backwards or you paraphrased it backwards.
In this case, you don't. It's very messy in my opinion.
Simply use:
private lateinit var binding: FragmentCalculatorBinding
It's does the same thing as your existing code - throws exception if you use the variable before instantiate it.
Keep in mind that you must instantiate it before using it (the whole point of lateinit var).
If you use it like this, you have a very explicit way of setting binding - you have to do it using _binding (which would probably just happen once in onCreateView(). For all the places where you actually use it, you would use binding, which both checks if _banding has actually been set, and cannot accidentally be changed as it is immutable, thereby saving you from potential bugs.
That being said - I would probably go with a lateinit var as well, and lose all the null checking, but that is a matter of personal taste I think.
using get() after the variable name makes it assign to the value when we each time trying to access this variable
as mentioned above without get() the variable will be assigned immediately when this class instantiated

Nullable var with `?` vs. lateinit var

What is the best way to define global variables in a Kotlin/Android activity/fragment?
What are the different scenarios when you should use these 2 methods for defining a global variable:
var viewpager: CustomViewPager? = null
or
lateinit var viewpager: CustomViewPager
?
If I use the former, I won't have to check for null in my code. For example if I used lateinit for the following:
viewpager = activity?.findViewById<CustomViewPager>(R.id.viewpager) then I would have to check for null.
using lateinit, you are saying that you absolutely will make sure that an instance of that variable is created somewhere (otherwise, your application will throw an exception if a lateinit has not been initialized) and then that variable also will not be null throughout the rest of your project, compared to using null, it means that this object potentially could be null somewhere in your code for the rest of the project and you will have to deal with nullability throughout.
If you are positive that you are not going to make a variable null and you require an instance of it always, use lateinit
Ask yourself this question :
Am I 100% sure that I will be using an instance of this variable somewhere in this class ?
If the answer to that is Yes, you should probably be using lateinit, as lateinit forces you to create an instance of it.
If the answer is No, you should probably be using a nullable field instead.
Taken from here : https://www.kotlindevelopment.com/lateinit-kotlin/
The lateinit keyword stands for late initialization. Lateinit comes very handy when a non-null initializer cannot be supplied in the constructor, but the developer is certain that the variable will not be null when accessing it, thus avoiding null checks when referencing it later.
i would recommend using the first method, it's better cause it eliminates app crashes if the code is trying to access the viewPager while it was not initialized, you can also use this in order to access the viewPager in the first method to make sure your app won't crash
viewPager?.let{"it"
}
this will create an instance of the viewPager if it was already initialized and use it as a val instead of a var, the instance is called "it" and u can rename it as anything by doing this after the first {
viewPager?.let{viewPager ->
}
If you make sure that variable will never be null in any place of the code then use lateinit. Here you have to initialize it before using it (in your case you can initialize it inside onCreate(..). Make sure that the variable will never become null later ELSE a null pointer exception will be fired.
In this way, you can directly use the variable without checking it if it's null or not.
Also, you can detect if the variable has initialized or not using:
if (:: viewpager.isInitialized) { .... }
On the other hand, you should use the nullable option using ?
In this case, you should check the variable before using it if it's null or not.
you can use ?. for that. You also can combine that with let for example:
viewPager?.let {
....
}

How to create variable in Kotlin without initialization?

This is simple question. In Java you can create String variable or couple of variables without adding any value to it. This is used at start of the class before onCreate() is called in Activity. I've used lateinit property in Kotlin to achieve that, but now I have a problem with changing visibility of RecyclerView. It will throw exception "lateinit property recyclerView has not been initialized".
Is there any way how to know if property is initialized? This is called at start of the parent activity in Fragment (hide recyclerView and show ProgressBar till data are binded to recyclerView).
In Java you can create String variable or couple of variables without adding any value to it
Actually in that case it is implicitly declared null. Kotlin does not do that, because of its nullability mechanism. You must explicitly declare a variable nullable to allow null:
var str: String // does not work
var str: String? // does not work
var str: String? = null // works
Also see this answer.
Your other option indeed is to mark it lateinit:
lateinit var str: String // works
If you need to make a check to see if it is initialized before using it, you use
if (::str.isInitialized)
But really you should avoid this check and just make sure it is initialized before using it.
If you need to get your UI element in Kotlin, you do not need to create variable and initialise it by using findViewById anymore (though you can). Use kotlin view binding, which works pretty well.
https://kotlinlang.org/docs/tutorials/android-plugin.html#view-binding

Kotlin !! Operator usage makes code not clear

I am writing a Kotlin application, I've been studying this language for a little bit and I noticed that to create a variable you have to explicitly define if it can be null and then you use the ? operator.
Now, my question. Sometimes I have to define a global variable (a Fragment in this case), so I need to make it null, because I cannot initialize it yet
In java I don't have this problem because the first thing I do after declaring it is initializing in the onCreate() like this
TurboFragment fragment = null;
#Override
public void onCreate(Bundle savedInstanceState) {
...
fragment = adapter.getCurrentFragment();
}
And then I can use it without syntax problems
In Kotlin is different, because I have to do like that
private var fragment: TurboFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
fragment = adapter!!.currentFragment
fragment!!.foo()
var x = fragment!!.sampleInt
}
Now, every time I call an instance of fragment i have to use the null-safe !! operator. This makes my code a mess, because for every line I have at least one !! operator and it looks really unclear if it's so frequent, especially if you have 5 or more variables like that.
Is there a way to simplify the code or the nature of this language is like that?
Consider using Late-Initialized Properties. They're intended for your use-case.
private lateinit var fragment: TurboFragment
However, you should keep in mind that accessing a lateinit property before it has been initialized throws an exception. That means you should use them only if you are absolutely sure, they will be initialized.
print(fragment) // UninitializedPropertyAccessException is thrown

Categories

Resources