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
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:
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.
}
}