3
votes

I am working on project with Dagger2 for DI. I am injecting presenter in MVP architecture. For some reason when I build app it crashes with error: "lateinit property presenter has not been initialized". I know it means that injection is not made but I don't understand why. Here is my code:

APPLICATION CLASS

class FlowerApp : Application() {

override fun onCreate() {
    super.onCreate()
    initAppComponent()
}

private fun initAppComponent() {
    appComponent = DaggerAppComponent
        .builder()
        .appModule(AppModule(this))
        .build()
}

companion object {
    lateinit var appComponent: AppComponent
}
 }

HOMEMODULE

@Module
class HomeModule(var homeFragment: HomeContract.View) {
    @Provides
    fun providePresenter(homeInteractor: HomeInteractor): HomePresenter {
        return HomePresenter(homeFragment, homeInteractor)
    }

    @Provides
    fun provideInteractor(): HomeInteractor {
        return HomeInteractor()
    }
}

APPCOMPONENT

 @Component(
    modules = [
        (AppModule::class),
        (NetworkModule::class),
        (HomeModule::class)
    ]
)

interface AppComponent {
    fun inject(application: FlowerApp)
    fun inject(homeFragment: HomeContract.View)
}

HOMEFRAGMENT

class HomeFragment : Fragment(), HomeContract.View {
    @Inject
    lateinit var presenter: HomePresenter
    private lateinit var flowerAdapter: FlowerAdapter
    private var startingPage = 1
    private var recyclerStartPos = 0

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setAdapter()
        presenter.getFlowers(startingPage)
        setListeners()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        FlowerApp.appComponent.inject(this)
    }

If any other info about code needed just ask...

Edited: logcat error:

2020-06-06 22:12:37.513 13401-13401/? E/AndroidRuntime: FATAL EXCEPTION: main Process: element.list.flowersmvp, PID: 13401 kotlin.UninitializedPropertyAccessException: lateinit property presenter has not been initialized at element.list.flowersmvp.home.HomeFragment.onViewCreated(HomeFragment.kt:37) at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:892) at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238) at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303) at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439) at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079) at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869) at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824) at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727) at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2663) at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2613) at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246) at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542) at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1392) at android.app.Activity.performStart(Activity.java:7252) at android.app.ActivityThread.handleStartActivity(ActivityThread.java:2970) at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180) at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1831) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:201) at android.app.ActivityThread.main(ActivityThread.java:6806) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)

4
post your logcat error here - asad mahmood
there u go, I edited question - Kratos
In HomeFragment's onCreate() try calling FlowerApp.appComponent.inject(this) before super.onCreate(savedInstanceState). - Onik
it did not help - Kratos

4 Answers

2
votes

You're trying to inject the presenter provided in the HomeModule via the application's main component. This won't work because you've never provided the HomeModule to your AppComponent. HomeModule shouldn't even be a part of the AppComponent since the things it provides (presenter and interactor) only exist when the fragment exists, meaning they exist in the fragment scope, not the application scope.

What you need to do is create another component that will inject into your fragment, e.g.

@Component(
    modules = [
        (HomeModule::class)
    ]
)
interface HomeComponent {
    fun inject(homeFragment: HomeFragment)
}

And then in your HomeFragment:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    DaggerHomeComponent
        .builder()
        .homeModule(HomeModule(this))
        .build()
        .inject(this)
}

If you need things from application scope (from network and app modules), then you inject them through the AppComponent as you've previously did.

0
votes

I think problem is in var homeFragment: HomeContract.View in your HomeModule. I suggest you rewrite HomePresenter and replace passing HomeFragment via constructor to methods bindView/unbindView.

class HomePresenter(private val interactor: HomeInteractor) {
    var view: HomeView? = null

    fun bindView(view: HomeView) {
        this.view = view
    }

    fun unbindView() {
        view = null
    }
}

Storing fragment reference in presenter can cause memory leaks and null-pointer exceptions in situations when Fragment destroyed and presenter is not.

0
votes

In your component you are not injecting HomeFragment, but the base class (HomeContract.View). Meaning Dagger wont inject the properties of HomeFragment that are not in the base class.

0
votes

I think the problem, you didn't inject the HomeFragment

Add an extension method in AppComponent

@Component(
    modules = [
        (AppModule::class),
        (NetworkModule::class),
        (HomeModule::class)
    ]
)

interface AppComponent {
    fun inject(application: FlowerApp)
    fun inject(homeFragment: HomeContract.View)
}

fun HomeFragment.inject(){
    FlowApp.appComponent.inject(this)
}

Inject HomeFragment

override fun onAttach(context: Context) {
        super.onAttach(context)
        inject()
}