Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
173 views
in Technique[技术] by (71.8m points)

android - how to scope the SharedViewModel after migrating an existing app to use the Navigation component

I've been struggling on this the whole day and still can't make it work. In an existing Single Activity app, I'm trying to replace all fragment transactions by implementing the Navigation component.

I've already created a Navigation graph and included the FragmentContainerView as a child of the root ViewGroup as it was proposed in the official docs.

activity_main.xml

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/appBarLayout">

            <androidx.fragment.app.FragmentContainerView
                android:id="@+id/main_content"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                app:defaultNavHost="true"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/appBarLayout"
                app:navGraph="@navigation/nav_graph" />
        </FrameLayout>

Now the problem that I'm currently facing is that the SharedViewModel which I am using through the whole app can't be instantiated in the BaseFragment anymore.

BaseFragment.kt

abstract class BaseFragment<E : ViewDataBinding, T : BaseViewModel> : Fragment() {

    lateinit var viewModel: T

    lateinit var dataBinding: E

    abstract fun getLayoutResource(): Int

    abstract fun getViewModelClass(): Class<T>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.let {
            // java.lang.RuntimeException: Cannot create an instance of class SharedViewModel
            viewModel = ViewModelProvider(it).get(getViewModelClass())
            // viewModel = ViewModelProvider(getParentFragment()).get(getViewModelClass()) also doesn't work
        }
    }
...
}

BaseViewModel.kt

abstract class BaseViewModel : ViewModel(), LifecycleObserver

SharedViewModel.kt

abstract class SharedViewModel : BaseViewModel() {
...
}

SharedViewModelImpl.kt

class SharedViewModelImpl(private val repository: AppRepository) : SharedViewModel(){
...
}

I've read posts on SO and as well as Medium articles that recommend creating the SharedViewModel by using by navGraphViewModels like this

val viewModel: SharedViewModel by navGraphViewModels(R.id.navigation_graph)

but it seems that I still can't combine this approach with the generics that I already have.

My assumption is that the Navigation component can't deal with abstract classes.

How can I overcome this problem by keeping my existing app architecture? Many thanks in advance.

UPDATE:

@ianhanniballake asked me to post the entire stack trace. So here it is:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: at.bwappsandmore.doitagain, PID: 13528
    java.lang.RuntimeException: Unable to start activity ComponentInfo{at.bwappsandmore.doitagain/at.bwappsandmore.doitagain.ui.MainActivity}: android.view.InflateException: Binary XML file line #44: Binary XML file line #44: Error inflating class androidx.fragment.app.FragmentContainerView
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.view.InflateException: Binary XML file line #44: Binary XML file line #44: Error inflating class androidx.fragment.app.FragmentContainerView
     Caused by: android.view.InflateException: Binary XML file line #44: Error inflating class androidx.fragment.app.FragmentContainerView
     Caused by: java.lang.RuntimeException: Cannot create an instance of class at.bwappsandmore.doitagain.viewModel.SharedViewModel
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:221)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:278)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
        at at.bwappsandmore.doitagain.base.BaseFragment.onCreate(BaseFragment.kt:31)
        at at.bwappsandmore.doitagain.ui.DisplayDataFragment.onCreate(DisplayDataFragment.kt:85)
        at androidx.fragment.app.Fragment.performCreate(Fragment.java:2684)
        at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:280)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1175)
        at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
        at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2629)
        at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:2571)
        at androidx.fragment.app.Fragment.onCreate(Fragment.java:1685)
        at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:264)
        at androidx.fragment.app.Fragment.performCreate(Fragment.java:2684)
        at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:280)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1175)
        at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
        at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1818)
        at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:303)
        at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:166)
E/AndroidRuntime:     at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:51)
        at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
        at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:356)
        at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:335)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:780)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:866)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
        at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:696)
        at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:170)
        at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:303)
        at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:284)
        at at.bwappsandmore.doitagain.base.BaseActivity.onCreate(BaseActivity.kt:27)
        at at.bwappsandmore.doitagain.ui.MainActivity.onCreate(MainActivity.kt:46)
        at android.app.Activity.performCreate(Activity.java:7136)
        at android.app.Activity.performCreate(Activity.java:7127)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.InstantiationException: java.lang.Class<at.bwappsandmore.doitagain.viewModel.SharedViewModel> cannot be instantiated
        at java.lang.Class.newInstance(Native Method)
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:219)
            ... 60 more
question from:https://stackoverflow.com/questions/65847588/how-to-scope-the-sharedviewmodel-after-migrating-an-existing-app-to-use-the-navi

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.9k users

...