Why `sortedBy` function is throwing Null pointer e...
# announcements
k
Why
sortedBy
function is throwing Null pointer even if my List type is not nullable
g
Could you show orginal stacktrace, maybe it throws because context is null
k
actually 'sortByCompleteness' is inside suspending function inside fragment and list is loaded from network while loading list android activity may be recreated through 'recreate()', so whenever activity recreated it throw this exception and it don't throw everytime activity recreated, so that's why its also difficult to reproduce
g
I just think that without real stacktrace hard to say something
I think problem somewhere else, not in this snippet
k
Copy code
Fatal Exception: kotlin.KotlinNullPointerException
       at FragmentName$sortByCompleteness$$inlined$sortedBy$1.compare(Comparisons.kt:320)
       at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
       at java.util.TimSort.sort(TimSort.java:220)
       at java.util.Arrays.sort(Arrays.java:1432)
       at kotlin.collections.ArraysKt___ArraysJvmKt.sortWith(_ArraysJvm.kt:1737)
       at kotlin.collections.CollectionsKt___CollectionsKt.sortedWith(_Collections.kt:926)
       at FragmentName.sortByCompleteness(FragmentName.kt:224)
       at FragmentName.loadNativeAds(FragmentName.kt:95)
       at FragmentName$loadNativeAds$1.invokeSuspend(Unknown Source:12)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
       at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:285)
       at android.os.Handler.handleCallback(Handler.java:790)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:164)
       at android.app.ActivityThread.main(ActivityThread.java:6494)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
g
it’s because
context
is null
k
android
context
?
g
yes
most probably
this is KotlinNullPointerException, so it means it came from Kotlin and thrown explicitly by kotlin null check, not from java
also you can see, it thrown from your lambda, not from Java sort function: FragmentName$sortByCompleteness$$inlined$sortedBy$1.compare
one more reason avoid
!!
you definitely have some lifecycle bug and call this function when fragment is not attached
if you replace
context!!
with requireContext() you will get much better error report, like:
Fragment FragmentName not attached to a context
k
Also i am using `LifecycleScope' class to launch coroutine so it can cancel coroutine whenever fragment destroyed but in this case when activity is recreated it is not cancelling coroutine i guess ?
ohh sorry i forgot that i'm using
context!!
in
Prefs.getPercentageOfPractice
thanks for your time
g
hard to say, this code has no suspend functions, so it’s not cancellable, hard to say without full reproducible sample where is problem
k
Copy code
uiScope.launch{
	loadListItems()?.let { loadNativeAds(it) }
}

private suspend fun loadNativeAds(categories: List<Category2>) {
	val list: List<Any> = try {
		nativeAdsManager.loadAdsSuspended()
		nativeAdsManager.fillAdsInList(list = categories, isItAdPosition = ::isAdPosition)
        } catch (e: IllegalStateException) {
            	categories
        }
        displayCategories(list.sortByCompleteness())
}

private fun List<Any>.sortByCompleteness(): List<Any> {
	return sortedBy { it is Category2 && Prefs.getPercentageOfPractice(context!!, it) == 100 }//i will change this to requireContext()
}
please check i have pasted sample code
g
where do you call
uiScope.launch{
? How do you create it?
k
uiScope
is field of fragment created using
private val uiScope by lazy { LifecycleScope(lifecycle) }
g
looks wrong for me, you actually cache scope with particular lifecycle and do not update it on fragment attach/detach
why not just use
lifecycleOwner.lifecycleScope.launch
directly
k
Copy code
class LifecycleScope(lifecycle: Lifecycle) : DefaultLifecycleObserver, CoroutineScope {
    private val job = Job()
    override val coroutineContext: CoroutineContext = job + Dispatchers.Main


    init {
        lifecycle.addObserver(this)
    }


    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        job.cancel()
    }

}
g
So, it’s your own scope?
but you cancel it only onDestroy, but it’s not the case for fragment that detached and attached again
k
so what would be best solution here ?
g
depends on case
I don’t see reason to use own lifecyclescope, just use lifecycle-ktx lifecycleOwner.lifecycleScope
to cancel coroutine on viewdestroy and detach, use viewLifecycleOwner instead of lifecycleOwner
otherwise coroutine will not be cancelled on fragment detach, only of fragemnt destroy
k
ok, can you please tell me in which dependency its added, i have used
'androidx.lifecycle:lifecycle-extensions-ktx:2.0.0'
but it not working ?, please give send me any documentation page if you know?
g
you probably need 2.1 for this
k
do you know if i can use 2.1 in pre-x ?
looks like 2.1 is only available for androidx
I have updated my code, now i'm canceling coroutine whenever lifecycle's
onStop
is called, so can you please tell me do it going to work as i'm expecting?, also i have converted
uiScope
as local variable
Copy code
val uiScope = LifecycleScope(lifecycle, cancelChildrenOnStop=false)
uiScope.launch{
    loadListItems()?.let { loadNativeAds(it) }
}

class LifecycleScope(
        lifecycle: Lifecycle,
        private val cancelChildrenOnStop: Boolean = false
) : DefaultLifecycleObserver, CoroutineScope {
    private val job = Job()
    override val coroutineContext: CoroutineContext = job + Dispatchers.Main


    init {
        lifecycle.addObserver(this)
    }


    //added this method 
    override fun onStop(owner: LifecycleOwner) {
        super.onStop(owner)
        if (cancelChildrenOnStop)
            job.cancelChildren()
    }

    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        job.cancel()
    }

}
g
I don’t think that it’s right implementation, it dangerous, because you can start new coroutine after onStop, and if assume that your coroutine should run when fragment is attached it will crash on runtime if you touch context
to run coroutine that alive only while fragment is attachd, do not use lifecyle of Fragment, use viewLifecycleOwner.lifecycle
You can check how it implemented in ktx
k
do you have reference to ktx implementation>
g
you can find sources in aosp, don’t have link