【问题标题】:Not navigate to fragment with @Inject constructor不使用 @Inject 构造函数导航到片段
【发布时间】:2021-03-11 09:49:12
【问题描述】:

我正在尝试创建一个简单的新闻应用程序,但是当我在片段中使用 @Inject constructor 时,导航组件不起作用并显示以下错误...而在使用注入之前,它可以正常工作

我在做什么来修复这个错误?!非常感谢您的帮助

NewsActivity:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import com.example.simplenewsapp.R
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.activity_news.*

@AndroidEntryPoint
class NewsActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_news)

        bottomNavigationView.setupWithNavController(newsNavHostFragment.findNavController())

    }
}

BreakingNewsFragment:

class BreakingNewsFragment @Inject constructor(
    val newsItemAdapter: NewsAdapter,
    var viewModel: MainViewModel? = null
) : Fragment(R.layout.fragment_breaking_news) {
...
}

错误信息:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.simplenewsapp, PID: 8558
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.simplenewsapp/com.example.simplenewsapp.ui.NewsActivity}: android.view.InflateException: Binary XML file line #17: Binary XML file line #17: Error inflating class fragment
        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 #17: Binary XML file line #17: Error inflating class fragment
     Caused by: android.view.InflateException: Binary XML file line #17: Error inflating class fragment
     Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.example.simplenewsapp.ui.fragments.BreakingNewsFragment: could not find Fragment constructor
        at androidx.fragment.app.Fragment.instantiate(Fragment.java:625)
        at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
        at androidx.fragment.app.FragmentManager$3.instantiate(FragmentManager.java:483)
        at androidx.navigation.fragment.FragmentNavigator.instantiateFragment(FragmentNavigator.java:132)
        at androidx.navigation.fragment.FragmentNavigator.navigate(FragmentNavigator.java:162)
        at androidx.navigation.fragment.FragmentNavigator.navigate(FragmentNavigator.java:58)
        at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.java:71)
        at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.java:28)
        at androidx.navigation.NavController.navigate(NavController.java:1059)
        at androidx.navigation.NavController.onGraphCreated(NavController.java:639)
        at androidx.navigation.NavController.setGraph(NavController.java:592)
        at androidx.navigation.NavController.setGraph(NavController.java:557)
        at androidx.navigation.NavController.setGraph(NavController.java:539)
        at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:248)
        at androidx.fragment.app.Fragment.performCreate(Fragment.java:2936)
        at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:472)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:278)
        at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:141)
        at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
        at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:313)
        at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:292)
        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) E/AndroidRuntime:     at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:696)
        at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:170)
        at com.example.simplenewsapp.ui.NewsActivity.onCreate(NewsActivity.kt:26)
        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.NoSuchMethodException: <init> []
        at java.lang.Class.getConstructor0(Class.java:2327)
        at java.lang.Class.getConstructor(Class.java:1725)
        at androidx.fragment.app.Fragment.instantiate(Fragment.java:610)
            ... 47 more

在 com.example.simplenewsapp.ui.NewsActivity.onCreate(NewsActivity.kt:26) 是:

    setContentView(R.layout.activity_news)

【问题讨论】:

    标签: android android-fragments dependency-injection android-jetpack dagger-hilt


    【解决方案1】:

    所有提供的答案都是错误,因为片段构造函数不必为空!您可以轻松地将依赖项构造函数注入到片段中,您必须使用片段工厂才能这样做。

    这里有一个例子:

    片段

    @AndroidEntryPoint
    class BreakingNewsFragment(
        val newsItemAdapter: NewsAdapter,
    ) : Fragment(R.layout.fragment_breaking_news) {
    ...
    }
    

    片段工厂

    class MainFragmentFactory @Inject constructor(
       private val newsAdapter: NewsAdapter
       // your dependencies
    ) : FragmentFactory() {
       override fun instantiate(classLoader: ClassLoader, className: String): Fragment = when(className) {
           BreakingNewsFragment::class.java.name -> BreakingNewsFragment(newsAdapter)
           // here you can add every other fragment 
           else -> super.instantiate(classLoader, className)
        }
    }
    

    MainNavHostFragment

    @AndroidEntryPoint
    class MainNavHostFragment : NavHostFragment() {
        @Inject lateinit var mainFragmentFactory: MainFragmentFactory
    
        override fun onAttach(context: Context) {
            super.onAttach(context)
            childFragmentManager.fragmentFactory = mainFragmentFactory
        }
    }
    

    然后为了使用你的新 NavHostFragment,你必须改变你的 activity_main.xml

    Activity_main.xml

    <fragment
        android:id="@+id/fragment_container"
        android:name="yourPath.MainNavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_main" />
    

    此处的重要行是“android:name="yourPath.MainNavHostFragment" 或例如“android:name="com.example.app.ui.MainNavHostFragment"

    有了这个,你可以在你的片段中注入构造函数。

    注意:

    您不能构造函数注入您的视图模型!您还应该小心使用此方法和构造函数注入适配器,因为它们可能会导致内存泄漏。使用适配器,我建议您现场注入它们

    【讨论】:

      【解决方案2】:

      Fragment 应该有一个空的构造函数,因为它们由 Android 管理,与活动相同。任何你想注入的东西都必须注入到片段内部,而不是通过构造函数。

      对于视图模型,通过 viewModels() API 很简单:

      private val viewModel: MainViewModel by viewModels()
      

      不要忘记用@AndroidEntryPoint 注释片段,用@HiltViewModel 注释视图模型。

      【讨论】:

      【解决方案3】:

      将@AndroidEntryPoint 添加到您的片段并在片段的onViewCreated 方法中初始化NewsAdapter()。 使用获取MainViewModel

      viewModels() 的私有 val MainViewModel

      片段必须有一个空的构造函数,因为它们是由系统操作系统创建的。

      【讨论】:

      • 不正确!使用片段工厂时,片段可以有构造函数参数!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-23
      相关资源
      最近更新 更多