Injecting dependencies beyond Activity and Fragment?

Asked

Viewed 85 times

1

It takes something special to inject a dependency with Dagger into an Android project other than Activity, Fragment, Service etc????

Example, if I have: Activityman -> Fragmentlistcake -> Presentercake -> Dbcakehelper

If I want to inject something into Activity or Fragment that’s easy, it always works. But if I want to do this on Presentercake or Dbcakehelper it doesn’t work at all.

Error:

05-26 15:22:45.663 13451-13451/myapplication E/Androidruntime: FATAL EXCEPTION: main Process: myapplication, PID: 13451 java.lang.Runtimeexception: Unable to resume Activity {myapplication.ui.Dashboard.Dashboardactivity}: Kotlin.Uninitializedpropertyaccessexception: lateinit Property compositeDisposable has not been initialized at android.app.Activitythread.performResumeActivity(Activitythread.java:3581) at android.app.Activitythread.handleResumeActivity(Activitythread.java:3621) at android.app.Activitythread.handleLaunchActivity(Activitythread.java:2862) at android.app.Activitythread. -wrap11(Unknown Source:0) at android.app.Activitythread$H.handleMessage(Activitythread.java:1589) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:164) at android.app.Activitythread.main(Activitythread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.Internal.os.Runtimeinit$Methodandargscaller.run(Runtimeinit.java:440) at com.android.Internal.os.Zygoteinit.main(Zygoteinit.java:807) Caused by: Kotlin.Uninitializedpropertyaccessexception: lateinit Property compositeDisposable has not been initialized at myapplication.ui.common.BasePresenter.getCompositeDisposable(Basepresenter.kt:31) at myapplication.ui.Dashboard.view.Repository.ListRepositoryPresenter.initRepositoryPopJava(Listrepositorypresenter.kt:25) at myapplication.ui.Dashboard.view.Repository.ListRepositoryFragment.onResume(Listrepositoryfragment.kt:73) at android.support.v4.app.Fragment.performResume(Fragment.java:2387) at android.support.v4.app.Fragmentmanagerimpl.moveToState(Fragmentmanager.java:1467) at android.support.v4.app.Fragmentmanagerimpl.moveFragmentToExpectedState(Fragmentmanager.java:1752) at android.support.v4.app.Fragmentmanagerimpl.moveToState(Fragmentmanager.java:1821) at android.support.v4.app.Fragmentmanagerimpl.dispatchStateChange(Fragmentmanager.java:3251) at android.support.v4.app.Fragmentmanagerimpl.dispatchResume(Fragmentmanager.java:3215) at android.support.v4.app.Fragmentcontroller.dispatchResume(Fragmentcontroller.java:217) at android.support.v4.app.Fragmentactivity.onResumeFragments(Fragmentactivity.java:509) at android.support.v4.app.Fragmentactivity.onPostResume(Fragmentactivity.java:498) at android.support.v7.app.Appcompatactivity.onPostResume(Appcompatactivity.java:171) at android.app.Activity.performResume(Activity.java:7131) at android.app.Activitythread.performResumeActivity(Activitythread.java:3556) at android.app.Activitythread.handleResumeActivity(Activitythread.java:3621)  at android.app.Activitythread.handleLaunchActivity(Activitythread.java:2862)  at android.app.Activitythread. -wrap11(Unknown Source:0)  at android.app.Activitythread$H.handleMessage(Activitythread.java:1589)  at android.os.Handler.dispatchMessage(Handler.java:106)  at android.os.Looper.loop(Looper.java:164)  at android.app.Activitythread.main(Activitythread.java:6494)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.Internal.os.Runtimeinit$Methodandargscaller.run(Runtimeinit.java:440)  at com.android.Internal.os.Zygoteinit.main(Zygoteinit.java:807)

Folder Structure of DI App

Estrutura de Pastas de DI App

Diappcomponent:

@Singleton
@Component(modules = arrayOf(
            AndroidSupportInjectionModule::class,
            DIAppModule::class,
            DIBuilderModule::class)
    )
interface DIAppComponent {

    @Component.Builder
    interface Builder  {
        @BindsInstance
        fun application(application: Application): Builder

        fun build(): DIAppComponent
    }

     fun inject(instance: GitApplication)

}

Diappmodule:

@Module
class DIAppModule {

        @Provides
        @Singleton
        internal fun provideGson(): Gson = GsonBuilder()
                .create()

        @Provides
        @Singleton
        internal fun provideOkHttpCliente(): OkHttpClient = OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .build()

        @Provides
        @Singleton
        internal fun provideRxJavaCallAdapterFactory(): RxJava2CallAdapterFactory = RxJava2CallAdapterFactory.create()


        @Provides
        @Singleton
        internal fun provideFirebaseHelper(firebase: FirebaseHelper): FirebaseContract = firebase

        @Provides
        @Singleton
        internal fun provideVerifyConnectionUtil(verifyConnectionUtil: VerifyConnectionUtil): VerifyConnectionUtil = verifyConnectionUtil

        @Provides
        @Singleton
        internal fun providePreferences(preferences: Preferences): Preferences = preferences

        @Provides
        @Singleton
        internal fun provideGitHelper(gitHelper: GitHelper): GitHelper = gitHelper


        @Provides
        @Singleton
        internal fun provideApp(app: GitApplication): Application = app

}

Dibuildermodule:

@Module
abstract class DIBuilderModule {


    @PerActivity
    @ContributesAndroidInjector(modules = arrayOf(DIDashboardModule::class))
    abstract fun dashboardActivityInjector(): DashboardActivity


    @PerListRepository
    @ContributesAndroidInjector(modules = arrayOf(DIListRepositoryModule::class))
    abstract fun listRepositoryFragmentInjector(): ListRepositoryFragment

}

Gitapplication:

class GitApplication : Application(), HasActivityInjector {

    @field: Inject
    internal lateinit var androidInjectorActivity : DispatchingAndroidInjector<Activity>

    val component : DIAppComponent by lazy {
              DaggerDIAppComponent
                .builder()
                .application(this)
                .build()
    }

    override fun onCreate() {
        super.onCreate()

        component.inject(this)
    }

    override fun activityInjector() = androidInjectorActivity

}

Common DI UI Folder Structure:

Estrutura de Pastas de DI UI Common

Didashboardmodule :

@Module(includes = arrayOf(DIBaseActivityModule::class ))
class DIDashboardModule {

    @PerActivity
    @Provides
    fun provideAppCompact(dashboardActivity : DashboardActivity): AppCompatActivity = dashboardActivity

}

Dibaseactivitymodule :

@Module
class DIBaseActivityModule {

    companion object {
        const val ARCHIVE_NAME = "app.preferences"
        const val MODE = Context.MODE_PRIVATE
    }


    @Provides
    @PerActivity
    internal fun provideFragmentManager(activity: AppCompatActivity):  FragmentManager = activity.supportFragmentManager


    @Provides
    @PerActivity
    internal fun provideRetrofit(client: OkHttpClient,
                        converterFactory: GsonConverterFactory,
                        adapterFactory: RxJava2CallAdapterFactory): Retrofit = Retrofit.Builder()
            .baseUrl(AppConstants.NetworkConstants.BASE_URL_GIT_SEARCH)
            .addConverterFactory(converterFactory)
            .addCallAdapterFactory(adapterFactory)
            .client(client)
            .build()


    @Provides
    @PerActivity
    internal fun provideSharedPreferences(context: Context): SharedPreferences = context.getSharedPreferences(ARCHIVE_NAME, MODE)

    @Provides
    @PerActivity
    internal fun provideSharedPreferencesEditor(shPreference: SharedPreferences) : SharedPreferences.Editor = shPreference.edit()


    @Provides
    @PerActivity
    internal fun context(activity: AppCompatActivity): Context = activity



    @PerActivity
    @Provides
    internal fun provideRepositoryAPI(retrofit: Retrofit): RepositoryAPI = retrofit.create(RepositoryAPI::class.java)


}

Dilistrepositorymodule:

@Module
class DIListRepositoryModule {

        @PerListRepository
        @Provides
        fun provideActivity(fragment: ListRepositoryFragment): ListRepositoryMVPView = fragment


        @PerListRepository
        @Provides
        fun provideRepositoryInteractor(): ListRepositoryMVPInterator = ListRepositoryInteractor()


        @PerListRepository
        @Provides
        fun provideRepositoryPresenter(fragment : ListRepositoryMVPView): ListRepositoryMVPPresenter {
             return ListRepositoryPresenter<ListRepositoryMVPView, ListRepositoryMVPInterator>(fragment)
        }


        @PerListRepository
        @Provides
        @Named(NamedDisposableForDagger.LIST_REPOSITORY_COMPOSITE_DISPOSABLE)
        fun provideListRepositoryCompositeDisposable(): CompositeDisposable = CompositeDisposable()


}

Basectivity:

open abstract class BaseActivity : AppCompatActivity(), MVPView, HasSupportFragmentInjector{    

    @field: Inject
    internal lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>    

    @field: Inject
    internal lateinit var mFragmentManager : FragmentManager        

    lateinit var mProgressDialog : ProgressDialog    

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
        setContentView(getContentView())
    }


    override fun showLoading(@NonNull msg: String) {
        if (mProgressDialog == null) {
            mProgressDialog = ProgressDialog(this)
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER)
            mProgressDialog.setCancelable(true)
        }
        mProgressDialog.setMessage(msg)
        mProgressDialog.show()
    }

    override fun hideLoading() {
        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            mProgressDialog.dismiss()
        }
    }

    override fun showDialog(@NonNull title: String, @NonNull  message: String) {
        if (mProgressDialog == null) {
            mProgressDialog = ProgressDialog(this)
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER)
            mProgressDialog.setCancelable(true)
        }

        mProgressDialog.setTitle(title)
        mProgressDialog.setMessage(message)
        mProgressDialog.show()
    }

    override fun hideDialog() {
        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            mProgressDialog.dismiss()
        }
    }

    override fun onError(@NonNull resId: Int) {

    }

    override fun onError(@NonNull message: String) {

    }

    override fun showMessage(@NonNull titulo: String, @NonNull mensagem: String, buttonConfirm: DialogInterface.OnClickListener, buttonNegative: DialogInterface.OnClickListener, textButtonConfirm: String, textButtonNegative: String) {

    }

    override fun showMessage(@NonNull titulo: String){

    }

    override fun showMessage(title: String, message: String){

    }

    override fun hideKeyboard() {
        val view = this.currentFocus
        if (view != null) {
            val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(view.windowToken, 0)
        }
    }

    @LayoutRes
    protected abstract fun getContentView():  Int

    override fun openActivityLogin() {
        val intent = Intent( this, MainActivity::class.java )
        startActivity(intent)
    }

    override fun addFragment(containerViewID: Int, fragment: Fragment,  @Nullable param : Bundle?) {
        if (param!=null){
            fragment.arguments = param
        }

        mFragmentManager!!.beginTransaction()
                .add(containerViewID, fragment)
                .commit()
    }

    override fun supportFragmentInjector() = fragmentInjector
}

Basefragment:

open class BaseFragment : Fragment(), MVPView, HasSupportFragmentInjector  {


    @field: Inject
    internal lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>

    override fun supportFragmentInjector(): AndroidInjector<Fragment> {
        return fragmentInjector
    }

    protected lateinit var mContext: Context

    private var mProgressDialog: ProgressDialog?=null

    private lateinit var mAlertDialog: AlertDialog

    private lateinit var mActivityMom : MVPView


    override fun onAttach(activity: Activity) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            AndroidSupportInjection.inject(this)

        }
        super.onAttach(activity)
    }

    override fun onAttach(context: Context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            AndroidSupportInjection.inject(this)
        }
        super.onAttach(context)
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mActivityMom = activity as MVPView
        mContext = activity as Context
    }

    override fun showLoading(msg: String) {
        if (mProgressDialog == null) {
            mProgressDialog = ProgressDialog(mContext)
            mProgressDialog!!.setProgressStyle(ProgressDialog.STYLE_SPINNER)
            mProgressDialog!!.setCancelable(true)
        }
        mProgressDialog!!.setMessage(msg)
        mProgressDialog!!.show()
    }

    override fun hideLoading() {
        if (mProgressDialog != null && mProgressDialog!!.isShowing()) {
            mProgressDialog!!.dismiss()
        }
    }




    override fun showDialog(title: String, message: String) {
        mAlertDialog = AlertDialog(mContext, title, message)
        mAlertDialog.create()


    }

    override fun hideDialog() {
        mAlertDialog.dismiss()
    }

    override fun onError(resId: Int) {

    }

    override fun onError(message: String) {

    }

    override fun showMessage(   @NonNull titulo: String,
                                @NonNull mensagem: String,
                                @NonNull buttonConfirm: DialogInterface.OnClickListener,
                                @Nullable buttonNegative: DialogInterface.OnClickListener,
                                @NonNull textButtonConfirm: String,
                                @Nullable textButtonNegative: String ) {
        mAlertDialog = AlertDialog( mContext, titulo, mensagem, buttonConfirm, buttonNegative, textButtonConfirm, textButtonNegative )
        mAlertDialog.create()
    }

    override fun showMessage( @NonNull titulo: String ){

    }

    override fun showMessage( title: String, message: String ){

    }

    override fun hideKeyboard() {
        val view = activity!!.currentFocus
        if (view != null) {
            val imm = mContext.getSystemService( Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(view.windowToken, 0 )
        }
    }


    override fun openActivityLogin() {
        val intent = Intent( mContext, MainActivity::class.java )

        startActivity(intent)
    }


    override fun addFragment(containerViewID: Int, fragment: Fragment, param: Bundle?) {
        mActivityMom.addFragment(containerViewID, fragment, param) // Chamei o método da classe
    }



}

Basepresenter:

open class BasePresenter<V : MVPView, I : MVPInteractor> @Inject internal constructor(view : V) : MVPPresenter {


    private var stateConnection = false

    @DefNamedDisposableForDagger
    protected lateinit var named : String

    @DefStateLogginUser
    private var stateLoggin : Int = StateLogginUser.LOGGED_OUT

    @field: Inject
    lateinit var mContext : Context

    @field: Inject
    @field: Named (NamedDisposableForDagger.LIST_REPOSITORY_COMPOSITE_DISPOSABLE)
    lateinit var compositeDisposable: CompositeDisposable


    internal var mView :  V = view

    @field: Inject
    internal lateinit var mInteractor : I    


    init {
        lazy {
            mInteractor.getConnectionInternet().subscribe { stateConnection = it } }
        lazy {
            mInteractor.getStateLogginUser().subscribe { it -> stateLoggin = it }
        }
    }


    override fun showDialog(message: String) = if (!message.isEmpty()) mView.showDialog("", message) else throw Exception("Falha")

    override fun hideDialog() = mView.hideDialog()

    override fun showMessage(message: String) = mView.showMessage(message)

    override fun showMessage(title: String, message: String) = mView.showMessage(title, message)

    override fun onError(message: String) = mView.onError(message)

    override fun isNetworkConnected() {
        mView.showLoading("Aguarde!")
        compositeDisposable.add(mInteractor.getConnectionInternet()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { valid ->
                mView.hideLoading()
                if (valid) {
                     mView.openActivityLogin()
                } else {
                    var title = mContext.getString(R.string.communication_opensystem_not_connection_title)
                    var message = mContext.getString(R.string.communication_opensystem_not_connection_message)
                    mView.showMessage(title, message)
                }
            })
    }

    override fun checkStateLogginUser() : Single<Int> = mInteractor.getStateLogginUser()


    override fun changeNetworkConnected() {
       // verificar conexão 
    }

    override fun addDisposable(disposable: Disposable) {
        compositeDisposable.add(disposable)
    }

    override fun stop() {
        compositeDisposable.dispose()
        compositeDisposable.clear()    

    }

    override fun setNamedDisposable(@DefNamedDisposableForDagger nameDispose: String) {
        named = nameDispose
    }


    override fun getView(): V = mView

}

Baseinteractor:

open class BaseInteractor @Inject internal constructor() : MVPInteractor{

    @Inject
    protected lateinit var mGitHelper : GitHelper

    @Inject
    lateinit var mPreferences: Preferences

    @Inject
    lateinit var mValidatorConnection: VerifyConnectionUtil

    override fun performLogout(): Maybe<Boolean> = Maybe.create<Boolean> { it ->
        mPreferences.let { pref ->
            try {
                TODO("Chamar metodo Firebase para deslogar e atualizar as Preferencias")
                pref!!.logout()
                it.onSuccess(true)
            }catch (e : Exception){
                it.onSuccess(false)
            }
        }
    }


    override fun getStateLogginUser(): Single<Int> = mPreferences.getStateUserLogged()

    override fun getConnectionInternet(): Maybe<Boolean> = mValidatorConnection.verifyConnectionInternet()



}

Listrepositoryfragment:

class ListRepositoryFragment: BaseFragment(), ListRepositoryMVPView {



    @field: Inject
    internal lateinit var mPresenter : ListRepositoryMVPPresenter

    private lateinit var recyclerViewListRepo : RecyclerView

    private lateinit var listRepos : ArrayList<Repo>

    private lateinit var adapterRepo : RepositoryAdapter

    private lateinit var layoutManager: LinearLayoutManager




    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {

        val view  = inflater!!.inflate(R.layout.fragment_list_repository, container, false)

        recyclerViewListRepo = view.findViewById(R.id.rv_repository)



        //init()


        return view
    }




    private fun init(){
        adapterRepo = RepositoryAdapter(listRepos)
        layoutManager = LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false)
        recyclerViewListRepo.adapter = adapterRepo
    }

    override fun setListRepository(repos: List<Repo>) {
        listRepos.addAll(repos)
    }   



    override fun onResume() {
        super.onResume()
        mPresenter.initRepositoryPopJava()
    }

    override fun setPresenter(presenter: ListRepositoryPresenter<ListRepositoryMVPView, ListRepositoryInteractor>) {
        this.mPresenter = presenter
    }

}

Listrepositorypresenter:

class ListRepositoryPresenter <V : ListRepositoryMVPView, I: ListRepositoryMVPInterator> : BasePresenter<V , I>, ListRepositoryMVPPresenter{


    @Inject
    constructor(view : V) : super(view) {

        setNamedDisposable(
                NamedDisposableForDagger
                        .LIST_REPOSITORY_COMPOSITE_DISPOSABLE
        )
    }


    override fun initRepositoryPopJava() {
        getView().showLoading("Carregando Repositórios!")
        compositeDisposable!!.add(mInteractor.let {
            it!!.getAllRepositories()
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe { listRepo ->
                        getView().let { v ->
                            v.setListRepository(listRepo)
                            v.hideLoading()
                        }
                    }
        })
    }


    fun setPresenter(presenter: ListRepositoryPresenter<ListRepositoryMVPView, ListRepositoryInteractor>) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }    
}

1 answer

0

Discover, or rather I reminded that there are two options:

Injection of dependency by constructor using Dagger of course. or Injection of fields (that’s what I wanted) but to function I have to build Subcomponents and in them overwrite the inject method by passing the class that can receive these "injections", in the class as in the presenter I call the Xxxxxcomponent.inject(this) and so I can do the injection.

I chose to do the injection by the manufacturer!!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.