Testing BaseActivity - android

I have a BaseActivity, which has Dagger behavior inside:
abstract class BaseActivity : DaggerAppCompatActivity(), HasSupportFragmentInjector {
#Inject
lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState, persistentState)
/**
* Method that gets called after the [onCreate] method
*/
public override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
applyDebugOverlay(this)
}
override fun supportFragmentInjector() = fragmentDispatchingAndroidInjector
}
Now I want to test, that applyDebugOverlay(this) is calling. For this, I use Robolectric in my AbstractActivityTest
#Test
fun testDebugOverlayIsShown() {
underTest.onPostCreate(Bundle())
val buildTimeStamp = underTest.inc_debugOverlay_tv_date.text
assert(buildTimeStamp.isNotEmpty())
}
But I get "No injector factory bound for Class" error, and it is not possible to bind BaseActivity with #ContributesAndroidInjector in my ActivityBuilder Class (would get another error).

Related

I cant solve this error "lateinit property dataBinding has not been initialized"

I have base activity< T : ViewDataBinding , VM : ViewModel > extends AppCompatActivity()
and i initialize view binding and view model but when run the app i get this error "lateinit property dataBinding has not been initialized"
I don't know what I miss or what the wrong
Below is Base Activity Code
open abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
lateinit var dataBinding : T
lateinit var viewModel : VM
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
dataBinding = getViewBinding()
setContentView(dataBinding.root)
viewModel = generateViewModel()
}
abstract fun getViewBinding(): T
abstract fun generateViewModel(): VM
and this My HomeActivity
class HomeActivity : BaseActivity<ActivityHomeBinding, HomeViewModel>() {
override fun getViewBinding(): ActivityHomeBinding = ActivityHomeBinding.inflate(layoutInflater)
override fun generateViewModel(): HomeViewModel {
return ViewModelProvider(this).get(HomeViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding.vm = viewModel
}
}
this is the error message
Because in your base class the onCreate() with 2 parameters is not going to get called during the activity's lifecycles. And in your subclass you override the onCreate() with a parameter.
Just simple change your base class to override the onCreate() with a parameter to fix the problem. And the other thing is you implement these class the java's way.
You can just make it better this way:
BaseClass
abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
protected abstract val dataBinding : T
protected abstract val viewModel : VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(dataBinding.root)
}
}
Subclass:
class HomeActivity : BaseActivity<ActivityHomeBinding, HomeViewModel>() {
override val viewModel get() = ViewModelProvider(this).get(HomeViewModel::class.java)
override val dataBinding get() = ActivityHomeBinding.inflate(layoutInflater)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding.vm = viewModel
}
}
When you are calling super.onCreate(savedInstanceState) in your HomeActivity, the onCreate from android's Activity is called, but not the onCreate from BaseActivity - because it expected second param persistentState.
So you can do this options to fix the issue:
call super method with 2 params in your HomeActivity
class HomeActivity ... {
...
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
dataBinding.vm = viewModel
}
OR
use onCreate with one param in your BaseActivity
open abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
lateinit var dataBinding : T
lateinit var viewModel : VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...

How to implement a BaseActivity in mvvm architecture? Without using dependency Injection

I am currently learning MVVM architecture.
I tried to make a BaseActivity class.
My BaseActivity:
abstract class BaseActivity<ViewModel : BaseViewModel, Binding : ViewDataBinding> :
AppCompatActivity(),
EventListener {
lateinit var binding: Binding
private var viewModel: ViewModel? = null
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
binding = DataBindingUtil.setContentView(this, layoutid)
this.viewModel = viewModel ?: getViewModel()
binding.setVariable(getBindingVariable(), viewModel)
binding.executePendingBindings()
}
#get: LayoutRes
abstract val layoutid: Int
abstract fun getViewModel(): ViewModel
abstract fun getBindingVariable(): Int
private fun getViewModelClass(): Class<ViewModel> {
val type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
return type as Class<ViewModel>
}
}
Now I am using this BaseActivity in my SplashActivity:
class SplashActivity : BaseActivity<SplashActivityViewModel, ActivitySplashBinding>() {
private lateinit var viewModel: SplashScreenViewModel
override fun onFailure(message: String) {}
override fun onStarted() {}
override fun onSuccess() {}
override fun getViewModel(): SplashActivityViewModel {
viewModel = ViewModelProvider(this).get(SplashActivityViewModel::class.java)
return viewModel
}
override fun getBindingVariable(): Int {
return BR.splash_viewmodel
}
override val layoutid: Int
get() = R.layout.activity_splash
}
I have used following answer as a reference to implement my BaseActivity.kt: https://stackoverflow.com/questions/55289334/how-to-have-generic-viewmodel-in-baseactivty-class
But I am getting a blank white screen while running the app.
Can someone please tell me what is the problem here or how to make this BaseActivity (without using dependency injection)?
you have overridden the wrong onCreate
override fun onCreate(savedInstanceState: Bundle?) {
I did play around with something like that few years ago, you can find my approach here

Pass data from base activity to activity that extends it

I have an Activity that extend base Activity like so:
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getInfo("Pojo")
}
}
My base activity does some API access and returns the answer like so:
abstract class BaseActivity : AppCompatActivity() {
companion object {
#JvmStatic var compositeDisposable: CompositeDisposable?=null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
compositeDisposable = CompositeDisposable()
}
override fun onDestroy() {
super.onDestroy()
compositeDisposable?.clear()
}
fun getTvShows(query: String) {
compositeDisposable?.add(
ApiClient.getClient.getTV(Params.getParamsSearch(1, query))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponseTvShows)
)
}
private fun handleResponseTvShows(result: ObjectsSearchTVShows) {
//Need to send result back to MainActivity or any activity that extends BaseActivity
}
}
I need to send the result back to MainActivity or any activity that extends BaseActivity, how I can achive this?
Make handleResponseTvShows abstract and protected:
protected abstract fun handleResponseTvShows(result: ObjectsSearchTVShows)
and implement it in MainActivity:
override protected fun handleResponseTvShows(result: ObjectsSearchTVShows) {
// process the result here
}

Simple Kotlin Project does not show any UI

I have a very simple Android Project in Kotlin. Just to dig in Kodein. I can not see the two TextViews in the main_layout?
I have used MVP pattern for the only MainActivity I have there..
The app starts without a crash and is show a blank white screen.
Any hints?
BaseActivity:
abstract class BaseActivity<V : BasePresenter.View> : AppCompatActivity(), BasePresenter.View {
protected abstract val layoutResourceId : Int
protected abstract val presenter : BasePresenter<V>
val kodeinMu = LazyKodein(appKodein)
protected abstract fun initUI()
protected abstract fun initPresenter()
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
setContentView(layoutResourceId)
initUI()
initPresenter()
}
override fun onPause() {
super.onPause()
presenter.pause()
}
override fun onStop() {
super.onStop()
presenter.stop()
}
override fun onDestroy() {
super.onDestroy()
presenter.destroy()
}
protected fun toast(s: String) {
System.out.println("TAG $s")
}
}
I have read that it is because of API 28 you only can see on API_28 devices or emulators. Either emulator or on real device were also blanked out.
You override the wrong onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) in you activity:
use this : onCreate(savedInstanceState: Bundle?)

kodein, inject data to simple class

i'm new on kotlin and kodein development.
I want to inject data to a simple class which extends nothing.
I have my MainActivity which extends KodeinAppCompatActivity(),
my fragment which extends KodeinSupportFragment() calls a function from my simple class CallType. But this function must to change a boolean from an other simple class ConnectivitySate. I don't want to use static value.
Below, my code :
class App : Application(), KodeinAware {
override val kodein by Kodein.lazy {
import(autoAndroidModule(this#App))
bind<CallType>() with instance(CallType())
bind<ConnectivityState>() with instance(ConnectivityState())
bind<ContactData>() with instance(ContactData())
}
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(androidActivityScope.lifecycleManager)
}
MainActivity :
class MainActivity : KodeinAppCompatActivity() {
My Fragment :
class JournalFragment : KodeinSupportFragment(){
private val callType: CallType by instance()
#SuppressLint("MissingSuperCall")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initializeInjector()
}
override fun onCreateView(inflater: LayoutInflater?, container:
ViewGroup?,savedInstanceState: Bundle?): View? {
// !! CALL MY FUNCTION !!
callType.call(callType.callNumber)
}
....
#SuppressLint("MissingSuperCall")
override fun onDestroy() {
super.onDestroy()
destroyInjector()
}
My simple class :
class CallType {
fun call(number: String) {
// !! I want to change gsmState value from ConnectivityState class
connectivityState.gsmState = true
}
My ConnectivityState class :
class ConnectivityState {
var gsmState = false
}
It is an example among many others, because in lots of situations, i'm blocked like that. I have try lots of things but i always have like error : value not injected
Thank you very much for your reply..
When you call super.onCreate(), it calls onCreateView, so the line callType.call(callType.callNumber) is called before initializeInjector().
Note that you should always call initializeInjector() before calling super.onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
initializeInjector()
super.onCreate(savedInstanceState)
}

Categories

Resources