Reputation: 111
I'm trying to create a simple news app, but when I used of @Inject constructor
in my fragment, the navigation component not worked and show the below error... while, before used injection, it's worked without any problem
what I doing to fix this error?! thank you so much for your help
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) {
...
}
Error message:
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
at com.example.simplenewsapp.ui.NewsActivity.onCreate(NewsActivity.kt:26) is:
setContentView(R.layout.activity_news)
Upvotes: 1
Views: 2095
Reputation: 25873
Fragments should have an empty constructor since they are managed by Android, same as activities. Anything you want to inject you have to inject inside the fragment, not through the constructor.
For view-models, it's trivial through the viewModels() API:
private val viewModel: MainViewModel by viewModels()
Don't forget to annotate the fragment with @AndroidEntryPoint
and the view-model with @HiltViewModel
.
Upvotes: 3
Reputation: 4712
All the provided answers are wrong as fragments constructor do NOT have to be empty! You can easily constructor inject your dependencies inside your fragments, but you have to use a fragment factory in order to do so.
Here is an example for you:
@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)
}
}
@AndroidEntryPoint
class MainNavHostFragment : NavHostFragment() {
@Inject lateinit var mainFragmentFactory: MainFragmentFactory
override fun onAttach(context: Context) {
super.onAttach(context)
childFragmentManager.fragmentFactory = mainFragmentFactory
}
}
Then in order to use your new NavHostFragment, you have to change your 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" />
The important line being here "android:name="yourPath.MainNavHostFragment" or for example "android:name="com.example.app.ui.MainNavHostFragment"
With this you can constructor inject inside your fragment.
You can NOT constructor inject your viewmodel! You should also be careful with using this method and constructor injecting adapters as they can possibility cause a memory leak. With adapters, I would advise you to field inject them
Upvotes: 5
Reputation: 123
Add @AndroidEntryPoint to your fragment and initialize NewsAdapter() in onViewCreated method of fragments. Get MainViewModel by using
private val MainViewModel by viewModels()
Fragments MUST have an empty constructor since they are created by System OS.
Upvotes: 0