Kotlin generics issue - android

The error occurs when passing this to onResume.
Somehow it doesn't recognize that this implements ActivityLifecycleType, Am I missing something?
open class BaseActivity<ViewModelType: ActivityViewModel<*>>: RxAppCompatActivity(), ActivityLifecycleType {
protected var viewModel: ViewModelType? = null
#CallSuper
override fun onResume() {
super.onResume()
viewModel?.onResume(this) ==> Error Required Nothing, Find BaseActivity<ViewModelType>
}
}
open class ActivityViewModel<in ViewType: ActivityLifecycleType> {
fun onResume(view: ViewType) {
// Do something
}
}
interface ActivityLifecycleType {
fun lifecycle(): Observable<ActivityEvent>
}

Kotlin's generics' more strict that you have you write use the code below:
open class BaseActivity<ViewModelType : ActivityViewModel<ActivityLifecycleType>> : ActivityLifecycleType, RxAppCompatActivity() {
protected var viewModel: ViewModelType? = null
#CallSuper
override fun onResume() {
super.onResume()
viewModel?.onResume(this#BaseActivity) // ==> Error Required Nothing, Find BaseActivity<ViewModelType>
}
}
open class ActivityViewModel<in ViewType : ActivityLifecycleType> {
fun onResume(view: ViewType) {
// Do something
}
}
interface ActivityLifecycleType {
fun lifecycle(): Observable<ActivityEvent>
}
What I've done is to change the declaration in the first line.
Java is too weak to check the generic type but Kotlin do.
Mention there're two things you have to do next:
implement lifecycle in BaseActivity or make it abstract.
it's recommended to use lateinit var viewModel instead of nullable types

Related

Android/Kotlin: Composing different Activity "traits" into one activity

I'm looking for a way to combine different features in an Android activity, that should be reusable for different activity classes. Specifically the problem arises from overriding open methods where the super's implementation also has to be called.
open class FirstActivity : FragmentActicity() {
override fun onStart() {
super.onStart()
doSomething()
}
}
That's simple enough, but it is not reusable. I could e.g. want to have the same behavior with a different base activity class:
open class SecondActivity : AppCompatActivity() {
override fun onStart() {
super.onStart()
doSomething()
}
}
where I'd have to duplicate the code. If I have a very basic functionality like tracking the state of the activity, I would want this in more or less all of my activities which do have different base classes.
It get's even worse when I want to create some more features that can be combined:
open class ThirdActivity : FragmentActivity() {
override fun onResume() {
super.onResume()
doSomeResuming()
}
}
open Class FirstActivityAgain : ThirdActivity {
override fun onStart() {
super.onStart()
doSomething()
}
}
class MyFragmentActivity : FirstActivity() {
override fun onStop() {
doSomethingElse()
super.onStop()
}
}
class MyFragmentActivityWithResuming : FirstActivityAgain() {
override fun onStop() {
doSomethingElse()
super.onStop()
}
}
class MyTopBarActivity : SecondActivity() {
override fun onStop() {
doSomethingElse()
super.onStop()
}
}
In Scala I can use Traits to do this stackable modification, which allows for very flexible mixins of functionality. It's even possible to modify the same method over and over again, one just has to be careful with the linearization order.
None of this is possible in Kotlin because a Scala Trait is neither equivalent to a Kotlin abstract class nor to a Kotlin Interface.
It doesn't seem to be possible with Kotlin's delegates either. I also thought about using generics, which in my limited imagination could look like this:
open class FirstActivity<BaseActivity : Activity> : BaseActivity() {
...
}
which of course is also not possible.
Is there anything I've overlooked? Can it be done by using Dagger?
What you are referring to in Kotlin called interfaces in conjunction with some basic delegation.
interface Base {
fun printMessage()
fun printMessageLine()
}
class BaseImpl(val x: Int) : Base {
override fun printMessage() { print(x) }
override fun printMessageLine() { println(x) }
}
class Derived(b: Base) : Base by b {
override fun printMessage() { print("abc") }
}
fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}
Though it won't save you from the super problem since it Android framework issue rather than Kotlin Language.
For your case I would do something like
interface BaseActivityContainer{
var activity: Activity
}
class MainActivity: BaseActivityContainer{
override var activity: Activity = this
}
interface BaseDoable: BaseActivityContainer{
fun doActivityStuff(){
activity.getString(...)
}
}
interface BaseDoableSecond: BaseActivityContainer{
fun doActivityStuff(){
activity.getDrawable(...)
}
}
class SomeActivity: MainActivity, BaseDoableSecond by this
Handle Lyfecycle events with the help of Android Lifecycle
This is not complete and barely functional but I hope it will clear some stuff for you.

I'm wondering why I have to write "private" from SUPER NEW

I'm just studying Kotlin about MVP Model.
after I made MainPresenter class, I connected with interface, mainContract
and I faced a problem. I fixed it up but I can't explain it by myself so can you explain why I have to add 'private'?
MainPresenter
class mainPresenter : mainContract.Presenter {
private lateinit var mainModel: mainModel
private lateinit var view: mainContract.View
// here's private is that i ask u
override fun setView(view: mainContract.View) {
}
override fun setModel(model: mainModel) {
}
override fun onConfirm() {
}
}
//here is interface
interface mainContract {
interface Presenter {
fun setView(view: mainContract.View)
fun setModel(model: mainModel)
fun onConfirm()
}
interface View {
fun showButtonText(text: String)
}
}
This has been answered here:
https://javarevisited.blogspot.com/2012/03/private-in-java-why-should-you-always.html
Or even here: https://softwareengineering.stackexchange.com/questions/143736/why-do-we-need-private-variables
A short answer: "none of your business how this class works, here is what you have to know". If you are writing a single app, there is no real harm in making everything public. However - if you are writing a library, you might want to keep the same user facing functions, and modify non-user facing functions - internal API.

How can inject interactor from presenter with Koin

I'm new at Koin. I have set all the stuff and is working. But I'm getting some problems when I'm trying to inject interactor and presenter at the same time. That not sure it is possible.
This is my Module
val applicationModule = module(override = true) {
factory{VoucherImpl(get())}
factory<VoucherContract.Presenter> { (view: VoucherContract.View) -> VoucherPresenter(view, get()) }
}
This is my Activity where inject the presenter
private val presenter: VoucherContract.Presenter by inject { parametersOf(this)}
This is my Presenter
class VoucherPresenter (private var view: VoucherContract.View?, private var mCodeRechargeInteract : VoucherImpl) : VoucherContract.Presenter, VoucherContract.Callback, KoinComponent {
override fun create() {
view?.initView()
view?.showProgress()
mCodeRechargeInteract.run()
}
.
.
.
Interactor class
class VoucherImpl(private var mCallback: VoucherContract.Callback?) : AbstractInteractor() {
.
.
.
contract
interface VoucherContract {
interface Presenter {
fun create()
fun destroy()
fun checkIfShoppingCartHaveItems()
fun addVoucherToShoppingCart(voucherProduct: Product)
fun onItemClick(product: Product)
}
interface Callback {
fun onResponseVouchers(vouchers: List<Product>?)
fun onError()
}
}
With this code I get
No definition found for 'xxx.voucher.VoucherContract$Callback' has been found. Check your module definitions.
Then, I try to put it in the module and I can't do it because I get: a Type mismatch. Required VoucherContract.Callback Found VoucherImpl
factory<VoucherContract.Callback> { (callBack: VoucherContract.Callback) -> VoucherImpl(callBack) }
You have a circular dependency that's why this doesn't work.
VoucherImpl(VoucherContract.Callback) and VoucherPresenter(View, VoucherImpl):VoucherContract.Callback
There are multiple ways out of this predicament.
I would recommend the following changes:
The VoucherImpl should not have the constructor parameter VoucherContract.Callback. This callback should be the parameter of a method something like this:
class VoucherImpl : AbstractInteractor(){
fun listen(VoucherContract.Callback){...}
}
This way the dependency becomes one way and you can inject them.

Best way to update a single element using Paging Library

Which is the best way to update a single element when using the new paging library?
Let's say we have the Paging with network google sample using the PageKeyedSubredditDataSource. Imagine we want to make a change of a single element of RedditPost. So, we want to check if it is in the list and if so, update it. The update should not be as easy as calling invalidate() which will make a call to the first page (maybe the RedditPost is in the 5th page. We don't want to update all elements, just one).
Please note that all this works over the Paging with network google sample. Although that, the idea is there.
#Sarquella helped me with this solution. Add this classes to your project. Basically we are extending ViewHolder to be LifeCycle Owner, as it is already done by default with Activities and Fragments.
The LifecycleViewHolder:
abstract class LifecycleViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView),
LifecycleOwner {
private val lifecycleRegistry = LifecycleRegistry(this)
fun onAttached() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
}
fun onDetached() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
}
override fun getLifecycle(): Lifecycle = lifecycleRegistry
}
LifecycleOwner is a single method interface that denotes that the class has a Lifecycle. You can find more information here.
The LifecyclePagedListAdapter:
abstract class LifecyclePagedListAdapter<T, VH : LifecycleViewHolder>(diffCallback: DiffUtil.ItemCallback<T>) :
PagedListAdapter<T, VH>(diffCallback) {
override fun onViewAttachedToWindow(holder: VH) {
super.onViewAttachedToWindow(holder)
holder.onAttached()
}
override fun onViewDetachedFromWindow(holder: VH) {
super.onViewDetachedFromWindow(holder)
holder.onDetached()
}
}
The LifecycleAdapter (in the case you need it):
abstract class LifecycleAdapter<VH : LifecycleViewHolder> :
RecyclerView.Adapter<VH>() {
override fun onViewAttachedToWindow(holder: VH) {
super.onViewAttachedToWindow(holder)
holder.onAttached()
}
override fun onViewDetachedFromWindow(holder: VH) {
super.onViewDetachedFromWindow(holder)
holder.onDetached()
}
}
Then, extends MyAdapter to LifecyclePagedListAdapter<MyEntity, LifecycleViewHolder>(MY_COMPARATOR) and MyViewHolder to LifecycleViewHolder(view). You'll have to complete your classes based on what we have changed, accordingly. Now we can observe to a liveData object on MyViewHolder class. So we can add this to MyViewHolder class (assuming you're using Dependency Injection). Basically, we'll do the same we do for Fragments or Activities:
private lateinit var myViewModel: MyViewModel
init {
(itemView.context as? AppCompatActivity)?.let{
myViewModel = ViewModelProviders.of(it).get(MyViewModel::class.java)
}
}
Then, inside the bind() method:
fun bind(myCell: MyEntity?) {
myViewModel.myLiveData.observe(this, Observer {
// Buala!! Check if it is the cell you want to change and update it.
if (it != null && myCell != null && it.id == myCell.id) {
updateCell(it)
}
})
}

Kotlin generics inheritance - Type mismatch

I am trying to build a set of providers for realm objects.
Here is an example structure I've tried to build:
Interface:
interface IDataProvider<out T : RealmObject> {
fun getRealmObject(): T
}
Base provider class with companion function for typed provider instantiation:
open abstract class BaseProvider<out T : RealmObject> constructor(protected val context: Context?) : IDataProvider<T> {
companion object {
fun <T : RealmObject, E : BaseProvider<T>> create(context: Context?): E {
if (something) {
return SomeChildProviderProvider(context)
} else {
throw TypeNotSupportedException()
}
}
}
}
And here is a child class:
class SomeChildProvider(context: Context?) : BaseProvider<ChildRealmModel>(context){
override fun getRealmObject(): ChildRealmModel {
throw UnsupportedOperationException("not implemented")
}
}
Problem I have is on the line
return SomeChildProviderProvider(context)
Type mismatch.
Required: E.
Found: SomeChildProvider.
I can't figure out why it does not see that E is actually SomeChildProvider.
Thank you.
P.S. I know that I can cast it to E, but in my opinion, it should not be needed in this situation. Maybe I am missing something obvious here or probably lack of Kotlin knowledge.
UPDATE1:
After the first answer, we have realized that code above does not make much sense since we have to define a type of returning provider and to pass it into create method. Initial idea was that create method returns some type which is BaseProvider subtype. Here are the changes I have made in order to support the initial idea:
IDataProvider
interface IDataProvider {
fun execute(realm: Realm)
fun createModel(realm: Realm): RealmObject
}
BaseProvider
open abstract class BaseProvider constructor(protected val context: Context?) : IDataProvider {
override fun execute(realm: Realm) {
realm.executeTransaction { r ->
createModel(r)
}
}
companion object {
fun create(context: Context?): IDataProvider {
if (something) {
return ChildProvider(context)
} else {
throw TypeNotSupportedException()
}
}
}
}
ChildProvider
class ChildProvider(context: Context?) : BaseProvider(context) {
override fun createModel(realm: Realm): ChildRealmModel {
var realmObject = realm.createObject(ChildRealmModel ::class.java)
//object property initialization
return realmObject
}
}
UI call
BaseProvider.create(context).execute(realm)
Although, createModel method returns RealmObject, it's instance will be of ChildRealmModel. What I don't like about it is that we have to inspect instance type and cast into if we need exact model somewhere else.
Your code is not consistent.
In the function declaration you pledge to return E, which is a subtype of BaseProvider<T> and can be chosen by the user on the call site.
But in the implementation you return SomeChildProviderProvider, which is of course a subtype of BaseProvider<T>, but still can be totally unrelated to E which was chosen by the user.
An example:
class AnotherChildProvider : BaseProvider<ChildRealmModel>(context) {...}
val x = BaseProvider.create<ChildRealmModel, AnotherChildProvider>(context)
What is the type of x? According to the function signature, it must be AnotherChildProvider. But inside the function you return SomeChildProviderProvider, which CAN NOT be casted to AnotherChildProviderProvider.

Categories

Resources