2

I am using extension function to bind list data to recyclerview , with pagedlist it doesn't need any coroutine

@BindingAdapter("pagedListAdapterData")
fun <T : Any> submitPagedList(recyclerView: RecyclerView, list: PagedList<T>?) {
    if (list != null && recyclerView.adapter is PagedListAdapter<*, *>) {
        (recyclerView.adapter as? PagedListAdapter<T, RecyclerView.ViewHolder>)?.submitList((list))
    }
}

        pagedListAdapterData="@{viewModel.list}"

but as im upgrading to Paging 3.0 it requires the use of coroutine

@BindingAdapter("pagingDataAdapter")
fun <T : Any> submitPagingDataList(recyclerView: RecyclerView, list: PagingData<T>?) {
    if (list != null && recyclerView.adapter is PagingDataAdapter<*, *>) {
        GlobalScope.launch {
            (recyclerView.adapter as? PagingDataAdapter<T, RecyclerView.ViewHolder>)?.submitData((list))
        }
    }
}

this works just fine, but my worry is on the use of Globalscope, seems like there is a better way to do it since globalScope is not recomended

joghm
  • 569
  • 3
  • 20
  • 1
    The recommended way is to start a Coroutine and keep track of it, as you have to destroy it when your view is destroyed. I don't know, if a BindingAdapter is a good purpose for this, because without it, it would be easier – Andrew Oct 28 '20 at 13:23
  • exactly , i can submitData in the fragment on a viewmodel scoped coroutine....but i wanted to know if there's maybe a way to use coroutine in a databindingAdapter – joghm Oct 28 '20 at 13:55
  • 1
    Well you **could** use Coroutines, but you **shouldn't** do it. Reason for this: It might be very hard or even impossible to keep a good track of your started Coroutine outside your BindingAdapter. You might have to do some magic, to properly achieve is, but magic is **bad**. I know, BindingAdapters can be very handy, but sometimes they are overkill. If you'd like, I can share my way to easily submit the paging Data to the adapter. – Andrew Oct 28 '20 at 14:14
  • Please do , this is how i did it outside the BindingAdapter viewModel.list.observe(viewLifecycleOwner, Observer { lifecycleScope.launch { adapter.submitData(it) } }) – joghm Oct 28 '20 at 14:26

2 Answers2

5

androidx.lifecycle has an extension function View.findViewTreeLifecycleOwner, so you don't have to create your own:

view.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
  ...
}
Torkel Velure
  • 1,217
  • 10
  • 17
2

Indeed, it isn't a good idea to use the GlobalScope, according to the documentation submitData() should be run inside 'lifecycleScope'. You can get 'lifecycleScope' from the view, but it requires a little bit of boilerplate:

package com.test.pagingadapter

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.View
import androidx.activity.ComponentActivity
import androidx.databinding.BindingAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.findFragment
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.paging.PagingData
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch

/**
 * https://stackoverflow.com/a/58249983/4858777
 */
tailrec fun Context?.getActivity(): Activity? = when (this) {
    is Activity -> this

    else -> {
        val contextWrapper = this as? ContextWrapper
        contextWrapper?.baseContext?.getActivity()
    }
}

val View.lifecycleOwner: LifecycleOwner? get() = try {
    val fragment = findFragment<Fragment>()
    fragment.viewLifecycleOwner
} catch (e: IllegalStateException) {
    when (val activity = context.getActivity()) {
        is ComponentActivity -> activity
        else -> null
    }
}

@BindingAdapter("pagingDataAdapter")
fun <T : Any> submitPagingDataList(recyclerView: RecyclerView, data: PagingData<T>?) {
    val adapter = recyclerView.adapter
    if (data != null && adapter is PagingDataAdapter<*, *>) {
        // but it isn't a perfect solution because the cast is required
        @Suppress("UNCHECKED_CAST")
        val castedAdapter = adapter as PagingDataAdapter<T, RecyclerView.ViewHolder>

        recyclerView.lifecycleOwner?.lifecycleScope?.launch {
            castedAdapter.submitData(data)
        }
    }
}
Valeriy Katkov
  • 33,616
  • 20
  • 100
  • 123