21

When writing Views, ViewModels and LiveData are lifecycle aware. The ViewModel want's the current FragmentActivity, LiveData the current LifecycleOwner. You don't know in advance if your View will be wrapped or somewhat. So it requires a flexible function to to find the wanted context. I ended up with this two methods:

private FragmentActivity getFragmentActivity() {
    Context context = getContext();
    while (!(context instanceof FragmentActivity)) {
        context = ((ContextWrapper) context).getBaseContext();
    }
    return (FragmentActivity) context;
}

private LifecycleOwner getLifecycleOwner() {
    Context context = getContext();
    while (!(context instanceof LifecycleOwner)) {
        context = ((ContextWrapper) context).getBaseContext();
    }
    return (LifecycleOwner) context;
}

Now this is a lot of boilerplate code to put into each View. Is there a more easy way?

I don't want to use a customised View base class for this, as large hierarchy's are ugly. Composition on the other hand requires as much code as this solution.

Blcknx
  • 1,921
  • 1
  • 12
  • 38
  • 1
    use AndroidViewModel it will have the application context – Santanu Sur Mar 02 '18 at 18:50
  • IMHO, none of this code belongs in a subclass of `View`. – CommonsWare Mar 02 '18 at 19:14
  • 1
    use getActivity().getApplicationContext() to get current activity, fragment. – Dinith Rukshan Kumara Mar 02 '18 at 19:27
  • @Santanu Sur: The application context is always the same. There is no lifecycle awareness in it. – Blcknx Mar 02 '18 at 19:29
  • @CommonsWare: Depends on your architecture. Currently I plug the real model into the view and treat the view model as internal implementation. Hence, for me the `View` is the place to create the view model, so I need the lifecycle. The advantage of this approach is, that I can always use the same Interface to plug model and view independent of the usage of a view model or none. -- However, this difficulties, to get the actual lifecycle is a reason to review my pattern of architecture. – Blcknx Mar 02 '18 at 19:36
  • @ DINITH RUKSHAN KUMARA: There is no getActivity() in the View. – Blcknx Mar 02 '18 at 19:47

4 Answers4

20

you can find it lifecycle-runtime-ktx :

fun View.findViewTreeLifecycleOwner(): LifecycleOwner? = ViewTreeLifecycleOwner.get(this)
Ahmadreza Moodi
  • 463
  • 4
  • 10
6

Kotlin Extensions

fun Context.fragmentActivity(): FragmentActivity? {
    var curContext = this
    var maxDepth = 20
    while (--maxDepth > 0 && curContext !is FragmentActivity) {
        curContext = (curContext as ContextWrapper).baseContext
    }
    return if(curContext is FragmentActivity)
        curContext
    else
        null
}

fun Context.lifecycleOwner(): LifecycleOwner? {
    var curContext = this
    var maxDepth = 20
    while (maxDepth-- > 0 && curContext !is LifecycleOwner) {
        curContext = (curContext as ContextWrapper).baseContext
    }
    return if (curContext is LifecycleOwner) {
        curContext as LifecycleOwner
    } else {
        null
    }
}

Usage

val lifecycleOwner = context.lifecycleOwner()
val fragmentActivity = context.fragmentActivity()
Abhinav Atul
  • 601
  • 6
  • 14
  • 9
    What is the maxDepth = 20? Is that just an arbitrary number to reduce a large loop. Any reason why you would have a loop with this many iterations? – Stimsoni Nov 17 '20 at 00:01
1

Similar to another answer, but without needing to specify maxDepth:

 val Context.lifecycleOwner: LifecycleOwner?
    get() {
        var context: Context? = this

        while (context != null && context !is LifecycleOwner) {
            val baseContext = (context as? ContextWrapper?)?.baseContext
            context = if (baseContext == context) null else baseContext
        }

        return if (context is LifecycleOwner) context else null
    }
jc12
  • 1,411
  • 1
  • 18
  • 24
  • 2
    If view in fragment, this method will get activity's LifecycleOwner. It may cause fragment memory leak. – xymelon Jan 07 '21 at 09:01
0

If you don't want to use composition you can put this into common BaseView class. Alternatively, you can make it a static method in some utils class.

If you use Kotlin you can also make it Kotlin extension function, what is my most favourite way of doing that.

Michał Powłoka
  • 1,424
  • 1
  • 13
  • 31