skip to Main Content

I had this simple viewmodel provider factory code (borrowed from one of Google’s code samples), which happily obliged and compiled perfectly…

fun <VM : ViewModel> viewModelProviderFactoryOf(
    create: () -> VM
): ViewModelProvider.Factory = SimpleFactory(create)


private class SimpleFactory<VM : ViewModel>(
    private val create: () -> VM
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val vm = create()
        if (modelClass.isInstance(vm)) {
            @Suppress("UNCHECKED_CAST")
            return vm as T
        }
        throw IllegalArgumentException("Can not create ViewModel for class: $modelClass")
    }
}

… Until i introduced this library:

implementation "androidx.navigation:navigation-compose:2.5.0-rc02"

Now all of a sudden compilation fails with:

Inheritance from an interface with ‘@JvmDefault’ members is only allowed with -Xjvm-default option

(error pointing to : ViewModelProvider.Factory )

Why? What did navigation bring in with it? (I did confirm 100% it’s the navigation lib causing it, remove it, and the error is gone)

Note: Q is not about how to solve it, compiler suggests it clearly, adding these args – freeCompilerArgs += "-Xjvm-default=all". The Q is about why this is happening.

4

Answers


  1. Chosen as BEST ANSWER

    OK, found it. The problem is that bringing in compose navigation, version 2.5.0-rc also updates androidx.lifecycle to 2.5.0-rc (from, in my case, 2.3.0), and in there, they changed the Factory interface by adding a method with implementation (and also adding implementation to the existing method in the interface).

    Compare:

    Factory implementation before 2.5.0:

    public interface Factory {
            /**
             * Creates a new instance of the given {@code Class}.
             * <p>
             *
             * @param modelClass a {@code Class} whose instance is requested
             * @param <T>        The type parameter for the ViewModel.
             * @return a newly created ViewModel
             */
            @NonNull
            <T extends ViewModel> T create(@NonNull Class<T> modelClass);
        }
    

    Note one method, create, without implementation.

    Here is the rewrite they did in 2.5.0

    public interface Factory {
            /**
             * Creates a new instance of the given `Class`.
             *
             * Default implementation throws [UnsupportedOperationException].
             *
             * @param modelClass a `Class` whose instance is requested
             * @return a newly created ViewModel
             */
            public fun <T : ViewModel> create(modelClass: Class<T>): T {
                throw UnsupportedOperationException(
                    "Factory.create(String) is unsupported.  This Factory requires " +
                        "`CreationExtras` to be passed into `create` method."
                )
            }
    
            /**
             * Creates a new instance of the given `Class`.
             *
             * @param modelClass a `Class` whose instance is requested
             * @param extras an additional information for this creation request
             * @return a newly created ViewModel
             */
            public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
                create(modelClass)
    
           
              ...
            }
    

    So that explains it, this interface is now a default implementation interface, and to inherit from it, need to add the compiler args, as suggested by the compiler (freeCompilerArgs += "-Xjvm-default=all").


  2. We encountered this also when we added compose libraries. We solved it by adding the xjvm-default option to our gradle file.

    android {
      kotlinOptions {
        freeCompilerArgs +=  "-Xjvm-default=all"
      }
    }
    
    Login or Signup to reply.
  3. first, upgrade all of your fragment and activity dependencies to 1.5.0 and your lifecycle dependencies to 2.5.0. by doing so, your activities and fragments can handle the creationExtras part without requiring you to do anything. then in your factory method, replace this line:

    //replace this line
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
    //with this one
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
    
    

    and you are good to go. source: These extras are provided automatically by your Activity or Fragment when using Activity 1.5.0 and Fragment 1.5.0, respectively.

    Login or Signup to reply.
  4. this code worked for me

    val viewModel: ViewModel by viewModels(
            ownerProducer = { requireParentFragment() }, 
            factoryProducer = { VMFactory }
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search