I used to access application class private variables by using a
public methods in java
How to do the same using kotlin
App.kt
class App : Application() {
private var app: App? = null
private var movieAppComponent: MovieAppComponent? = null
override fun onCreate() {
super.onCreate()
app = this
movieAppComponent = DaggerMovieAppComponent.builder()
.applicationModule(ApplicationModule(this))
.netModule(NetModule(Keys.BASE_URL, this))
.build()
}
fun getApp(): App? {
return app
}
fun getMovieAppComponent(): MovieAppComponent? {
return movieAppComponent
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
App.getApp().getMovieAppComponent().inject(this)
}
}
Error:
App.getApp().getMovieAppComponent().inject(this)
Here getApp() i am getting as unresolved reference
Solution 1:
class App : Application() {
private var movieAppComponent: MovieAppComponent? = null
companion object {
private var app: App? = null
fun getApp(): App? {
return app
}
}
override fun onCreate() {
super.onCreate()
app = this
movieAppComponent = DaggerMovieAppComponent.builder()
.applicationModule(ApplicationModule(this))
.netModule(NetModule(Keys.BASE_URL, this))
.build()
}
fun getMovieAppComponent(): MovieAppComponent? {
return movieAppComponent
}
}
Solution 2:
No need to create such method. You can use type casting in your Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
(application as? App)?.getMovieAppComponent()?.inject(this)
}
}
Related
when I run this class, I'm always got test failed in method verify_on_success_is_called() with error,
Actually, there were zero interactions with this mock.
but if I run method only, test will passed.
#Mock
lateinit var mDummy: Dummy
private lateinit var mainViewModel: MainViewModel
#Mock
lateinit var main: MainViewModel.IMain
#Before
#Throws(Exception::class)
fun setup() {
MockitoAnnotations.initMocks(this)
MainViewModel.mIMain = main
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
mainViewModel = MainViewModel(mDummy)
}
#Test
fun verify_on_success_is_called() {
val mockList: ArrayList<Employee> = ArrayList()
mockList.add(Employee(1, "a", 20000.0, 22))
val list: List<Employee> = mockList
`when`(mDummy.getEmployees()).thenReturn(Observable.just(Response.success(list)))
mainViewModel.getEmployees()
Mockito.verify(main, times(1)).onSuccess()
}
#Test
fun verify_on_onError_is_called() {
MainViewModel.mIMain = main
`when`(mDummy.getEmployees()).thenReturn(Observable.error(Throwable()))
mainViewModel.getEmployees()
Mockito.verify(main, times(1)).onError()
}
this the viewModel class I want to test
class MainViewModel(private val mDummy: Dummy) : ViewModel() {
companion object {
lateinit var mIMain: IMain
}
interface IMain {
fun onSuccess()
fun onError()
}
fun getEmployees() {
mDummy.getEmployees()
.observeOn(SchedulerProvides.main())
.subscribeOn(SchedulerProvides.io())
.subscribe({ response ->
if (response.isSuccessful) {
mIMain.onSuccess()
} else {
mIMain.onError()
}
}, {
mIMain.onError()
})
}
and this my mainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MainViewModel.mIMain = mIMainViewModelIniliazed()
}
private fun mIMainViewModelIniliazed() = object :MainViewModel.IMain{
override fun onSuccess() {
}
override fun onError() {
}
}
Please correct me if am wrong but i think your problem is because you're setting
MainViewModel.mIMain = main
before creating your viewmodel instance, shouldn't be as below?
mainViewModel = MainViewModel(mDummy)
mainViewModel.mIMain = main
I want to use the below object in another class:
private var mSingleAccountApp: ISingleAccountPublicClientApplication? = null
PublicClientApplication.createSingleAccountPublicClientApplication(
this,
R.raw.auth_config_single_account,
object : IPublicClientApplication.ISingleAccountApplicationCreatedListener {
override fun onCreated(application: ISingleAccountPublicClientApplication) {
mSingleAccountApp = application
// loadAccount()
}
override fun onError(exception: MsalException) {
//txt_log.text = exception.toString()
}
}
)
I need to call the below method from another class
fun performOperationOnSignOut() {
mSingleAccountApp!!.signOut(object : ISingleAccountPublicClientApplication.SignOutCallback {
override fun onSignOut() {
}
override fun onError(exception: MsalException) {
//displayError(exception)
}
})
}
I tried to call but mSingleAccountApp always throws NullPointerException
How can I pass or use the mSingleAccountApp variable in another class?
ISingleAccountPublicClientApplication this is an interface
This is the simplest example I could come up with:
//class with one property
class Foo(val name : String){
}
class Bar(){
constructor(foo: Foo) : this() {
//access the property of the foo object
println(foo.name)
}
}
fun main() {
val foo = Foo("John Doe")
//pass foo-object in the constructor
val bar = Bar(foo)
}
see also: https://kotlinlang.org/docs/reference/properties.html
class CustomClass(var listener: ListenerAppImp){
private var mSingleAccountApp: ISingleAccountPublicClientApplication? = null
PublicClientApplication.createSingleAccountPublicClientApplication(
this,
R.raw.auth_config_single_account,
object : IPublicClientApplication.ISingleAccountApplicationCreatedListener {
override fun onCreated(application: ISingleAccountPublicClientApplication) {
mSingleAccountApp = application
listener.getAppValue(mSingleAccountApp) // todo mSingleAccountApp Value through interface
// loadAccount()
}
override fun onError(exception: MsalException) {
//txt_log.text = exception.toString()
}
}
)
}
// todo for example
interface ListenerAppImp {
fun getAppValue(mSingleAccountApp : ISingleAccountPublicClientApplication)
}
class User {
var customClass = CustomClass(object : ListenerAppImp{
override fun getAppValue(mSingleAccountApp: ISingleAccountPublicClientApplication) {
}
})
}
hope that may help you
In your case just implement interface ISingleAccountPublicClientApplication in another class for your understanding let's keep class name is Another
class Another : ISingleAccountPublicClientApplication {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
.....InitialiseYourInterfaceHere.....
}
override fun signOut(..yourcode...){
.......yourcode.......
}
}
I am getting an exception in the first line of the code below
viewModel.homeLiveData.observe(this, Observer { list ->
list?.let {
mList.addAll(list)
adapter.notifyDataSetChanged()
}
})
java.lang.ClassCastException: java.lang.Class cannot be cast to androidx.lifecycle.ViewModel
The Whole code is Below
what is wrong with the cast ? is there anything wrong of I am creating my ViewModel?
My BaseActivity
abstract class BaseActivity<V : ViewModel> : DaggerAppCompatActivity(), HasSupportFragmentInjector {
#Inject
lateinit var fragmentAndroidInjector: DispatchingAndroidInjector<Fragment>
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
#LayoutRes
abstract fun layoutRes(): Int
protected lateinit var viewModel : V
protected abstract fun getViewModel() : Class<V>
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == android.R.id.home)
onBackPressed()
return super.onOptionsItemSelected(item)
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(layoutRes())
viewModel = ViewModelProviders.of(this, viewModelFactory).get(getViewModel())
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentAndroidInjector
}
then My Activity
class MainActivity : BaseActivity<MainViewModel>() {
override fun layoutRes(): Int = R.layout.activity_main
override fun getViewModel(): Class<MainViewModel> = MainViewModel::class.java
private val mList = mutableListOf<Any>()
private lateinit var adapter: DataAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setSupportActionBar(toolbar)
recyclerView.apply {
layoutManager = GridLayoutManager(applicationContext, 2)
adapter = DataAdapter(context, mList)
}
viewModel.homeLiveData.observe(this, Observer { list ->
list?.let {
mList.addAll(list)
adapter.notifyDataSetChanged()
}
})
viewModel.getHomeItems()
}
and this is my ViewModel
class MainViewModel #Inject constructor() : ViewModel() {
val homeLiveData: MutableLiveData<List<HomeScreenModel>> = MutableLiveData()
fun getHomeItems() {
Handler().post {
val homeModleList = listOf(
HomeScreenModel(R.drawable.ic_launcher_background, MyApplication.instance.getString(R.string.settings))
)
homeLiveData.setValue(homeModleList)
}
}
}
In my opinion, your viewModel property, which you try to access in viewModel.homeLiveData is shadowed by getViewModel() abstract function that you declare in BaseActivity. This is because Kotlin thinks that getXyZ() is a getter for the field xyZ and thus when you access viewModel, the compiler thinks you want to call getViewModel, which is of type Class<V>. I suggest renaming either the function or the property and it should work then.
Check your viewModel factory if you implemented it correctly
I want to get a variable from an activity and use it in another class.
This variable will be filled by an user in a editText that is called editTextSerie
override fun searchSeries(listener: OnDataListener) {
val retrofit = Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://api.themoviedb.org/3/")
.build()
val client = retrofit.create(MovieDBApiInterface::class.java)
val objetoClasse1 = SearchActivity()
var nomeS = objetoClasse1.editTextSerie.text.toString().trim()
val responseCall = client.searchSeries("API_KEY", "pt-BR", nomeS)
responseCall.enqueue(object : Callback<AllSeriesResponse> {
override fun onResponse(call: Call<AllSeriesResponse>?, response1: Response<AllSeriesResponse>?) {
listener.onSuccess(response1!!.body()!!.results)
}
override fun onFailure(call: Call<AllSeriesResponse>?, t: Throwable?) {
listener.onFailure(t!!.message.toString())
}
})
}
This function "searchSeries" is from the class "Series".
I want to get the "editTextSerie" from another class called "Search Activity",
so i created the variable "nomeS" to receive the value of it.
class SearchActivity : AppCompatActivity() {
var botaoSearch: AppCompatImageButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
botaoSearch = findViewById(R.id.btn_search)
botaoSearch?.setOnClickListener {
var nomeSerie = editTextSerie.text.toString().trim()
}
}
}
I want to receive this value (value of editTextSerie comes from the XML of SearchActivity ) and use it at responseCall with the "nomeS" variable
What is OnDataListener? Not really sure it is interface or abstract class, so I' ll write some pseudo code.
First change your function searchSeries's params to
searchSeries(text: String, listener: OnDataListener)
So in the class Series, you can get the data in your function searchSeries:
override fun searchSeries(text: String, listener: OnDataListener) {
// ...
// you can get the "text" string
}
Then edit your SearActivity's listener:
class SearchActivity : AppCompatActivity() {
var botaoSearch: AppCompatImageButton? = null
// create class "Series"
val series = Series()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
botaoSearch = findViewById(R.id.btn_search)
botaoSearch?.setOnClickListener {
var nomeSeries = editTextSerie.text.toString().trim()
searchSeries(nomeSeries)
}
}
private fun searchSeries(text: String) {
series.searchSeries(text, object : OnDataListener {
override onSuccess(a0: ...) {
}
override onFailure(message: String) {
}
})
}
}
If OnDataListener is a abstract class:
series.searchSeries(text, object : OnDataListener() {
override onSuccess(a0: ...) {
}
override onFailure(message: String) {
}
})
I am trying to write a base activity with code like this
abstract class BaseActivity<T : ViewDataBinding, V : BaseViewModel> : AppCompatActivity(), HasSupportFragmentInjector {
val NO_VIEW_MODEL_BINDING_VARIABLE = -1
private lateinit var mViewModel: V
private lateinit var mViewDataBinding: T
#Inject
lateinit var mViewModelFactory : ViewModelProvider.Factory
#Inject
lateinit var mFragmentInjector: DispatchingAndroidInjector<Fragment>
abstract fun getViewModelBindingVariable() : Int
#LayoutRes
abstract fun getLayoutId() : Int
fun getDataBinding() : T {
return mViewDataBinding
}
fun getViewModel() : V {
return mViewModel
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
performDataBinding()
provideViewModel()
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return mFragmentInjector
}
private fun performDataBinding() {
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId())
if (getViewModelBindingVariable() != NO_VIEW_MODEL_BINDING_VARIABLE) {
setViewModelBindingVariable()
}
}
private fun setViewModelBindingVariable() {
mViewDataBinding.setVariable(getViewModelBindingVariable(), mViewModel)
mViewDataBinding.executePendingBindings()
}
private fun provideViewModel() {
val clazz: Class<V> = getViewModelClass(javaClass)
mViewModel = ViewModelProviders.of(this, mViewModelFactory).get(clazz)
}
private fun getViewModelClass(aClass: Class<*>): Class<V> {
val type = aClass.genericSuperclass
return if (type is ParameterizedType) {
type.actualTypeArguments[1] as Class<V>
} else {
getViewModelClass(aClass.superclass)
}
}
}
and I also using databinding here, the problem I encountered is whenever I tried to reference the viewmodel class in my xml and return the generated value of the viewmodel by using BR.viewModel in getViewModelBindingVariable it gave an error saying that
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mViewModel has not been initialized
at com.rahmat.app.newsapp.features.base.BaseActivity.setViewModelBindingVariable(BaseActivity.kt:70)
at com.rahmat.app.newsapp.features.base.BaseActivity.performDataBinding(BaseActivity.kt:65)
at com.rahmat.app.newsapp.features.base.BaseActivity.onCreate(BaseActivity.kt:53)
at com.rahmat.app.newsapp.features.main.MainActivity.onCreate(MainActivity.kt:31)
This error didn't happen if I return NO_VIEW_MODEL_BINDING_VARIABLE but this made me cannot reference the viewmodel class that I want in my xml. I think there is some problem in my provideViewModel() function but I still don't know why. Any help would be much appreciated. thank you