Is there any possibility to trigger some default methods in Kotlin interfaces with lifecycle events of, for example, an Activity that implements that interface?
So, I have such interface, that called in Swift - protocol:
interface MyInterface {
fun showToast() {
this as MyActivity
Toast.show(this, "Welcome", Toast.LENGTH_SHORT).show()
}
}
And Activity class:
class MyActivity : AppCompatActivity(), MyInterface {
fun onResume() {
super.onResume()
showToast() //I want this method be called automatically, if possible
}
}
As you can see I should call showToast() method directly. But is there any possibility to call it automatically with, for example, LifeCycleObserver events or somehow else?
You can extend LifecycleObserver interface and use appropriate annotations, for example:
interface LifecycleInterface : LifecycleObserver{
#OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onLifeResume(){
(this as? Context).let{Toast.makeText(it, "Resumed", Toast.LENGTH_LONG).show()}
}
#OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onLifePause(){
(this as? Context).let{Toast.makeText(it, "Paused", Toast.LENGTH_LONG).show()}
}
}
Then register activity itself (or any custom object for that matter) as listener for lifecycle events:
class MainActivity : AppCompatActivity(), LifecycleInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(this) // add this to trigger lifecycle methods from interface
setContentView(R.layout.activity_main)
// rest of your onCreate...
}
}
Edit:
After showing bytecode and decompiling back to java, I end up with those two methods injected into activity:
#OnLifecycleEvent(Event.ON_RESUME)
public void onLifeResume() {
DefaultImpls.onLifeResume(this);
}
#OnLifecycleEvent(Event.ON_PAUSE)
public void onLifePause() {
DefaultImpls.onLifePause(this);
}
For anyone still facing issue due to compiler adding a parameter in default function of the interface, this is how you fix it:
Instead of using kapt to process the annotations:
kapt "androidx.lifecycle:lifecycle-compiler:$archLifecycleVersion"
Use annotationProcessor for lifecycler compiler:
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$archLifecycleVersion"
This works with kotlin code.
Related
I'm a beginner in Hilt. I have a library which takes in an interface. The library does some operation and invokes the interface callback. I have an activity which invokes this library by passing the interface implementation. I'd like to know how to inject this using Hilt.
Interface in library
interface InterfaceInLibrary() {
fun callback1()
fun callback2(/*params */)
}
Activity
class MyActivity: InterfaceInLibrary() {
override fun onCreate(savedInstanceState: Bundle?) {
//library initialization
val myLibraryClass = MyLibraryClass.getInstance(this) //passing the InterfaceInLibrary implementation
}
override fun callback1() {
Toast.makeText(this, "callback1", Toast.LENGTH_LONG).show()
}
override fun callback2() {
Toast.makeText(this, "callback2", Toast.LENGTH_LONG).show()
}
}
I would like to know how to inject MyLibraryClass in MyActivity using Hilt.
The only possible way I know (or at least how I am handling this use-case in my projects) is to field inject the concrete class that invokes the interface and then let the activity implement the concrete class and inherit from the callback. Since your interface and your concrete class look kinda weird, I will provide a full implementation here. Let's assume we have the following interface:
Interface
interface IMyCallbackInterface {
fun callbackWithoutParameters()
fun callbackWithParameters(value: String)
}
Then, you need some class to invoke this callback. In my case, this was always a recylerview.adapter, but we will use somethin easier:
Invoking class
class MyInvokingClass #Inject constructor() {
// this interface will be initialized by our activity
private lateinit var callbackListener: IMyCallbackInterface
// This function invokes the first callback
fun someFunctionThatInvokesCallbackWithoutParameters() {
// do some stuff
callbackListener.callbackWithoutParameters()
}
// This function invokes the second callback
fun someFunctionThatInvokesCallbackWithParameters() {
// do some stuff
callbackListener.callbackWithParameters(value = "Hello")
}
// This will be called from our activity to initialize the callback
fun initializeCallback(callbackOwner: IMyCallbackInterface) {
this.callbackListener = callbackOwner
}
}
Then, you need to field inject the class and inherit from the callback inside your activity
Activity or Fragment
class MyActivity : IMyCallbackInterface {
#Inject lateinit var invokingClass: MyInvokingClass
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate()
setContentView(...)
// Since MyActivity implements the interface
// it is an instance of it. So you can simply
// say, that the "owner" of the callback is the activity
invokingClass.initializeCallback(this#MyActivity)
}
override fun callbackWithoutParameters() {
// do some stuff
}
override fun callbackWithParameters(value: String) {
// do some stuff with string
}
}
Because our Activity inherits from the callback and we said in onCreate() that the interfaceOwner of MyInvokingClass is the activity, every time the callback gets invoked, the interface functions inside the activity will be invoked as well.
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.
So, I have a following BLEClient class and I'm currently setting deviceDelegate using this keyword
class BLEClient(val device: Device) : Client, DeviceDelegate {
init {
device.deviceDelegate = this
}
// client interface methods
override fun connect() {...}
override fun disconnect() {...}
override fun send() {...}
// device delegate interface methods
override fun didSend() { ... }
override fun didConnect() { ... }
override fun didReceive() { ... }
}
I was wondering if this is the best way to do it, since I could also use an inner class for DeviceDelegate instead of implementing interface directly, in my eyes this would make the code more readable and simple?
The class would look like this:
class BLEClient(val device: Device) : Client {
init {
device.deviceDelegate = DeviceDelegateInner()
}
/* client interface methods */
override fun connect() {...}
override fun disconnect() {...}
override fun send() {...}
inner class DeviceDelegateInner() : DeviceDelegate {
override fun didSend() { }
override fun didConnect() { }
override fun didReceive() { }
}
}
Are there any significant drawbacks if I set my delegate using an inner class instead of implementing an interface directly and setting it using this keyword?
What would you guys prefer? Which way is better?
It really depends on your use-case, and I'm not familiar with DeviceDelegate at all. But I'll give it a try.
Using inner class provides better encapsulation and separation of concerns.
Also, implementing less interfaces makes your class a bit easier to reason about, for the same reasons above.
You can also argue that the second approach is more "composition over inheritance".
I would use the first approach only if you see a lot of duplication in your inner class, eg:
class BLEClient(val device: Device) : Client {
fun b() { }
inner class DeviceDelegateInner() : DeviceDelegate {
override fun a() = b()
}
}
I'm trying to create an app which will use MVVM architecture and there's one thing I quite don't understand.
Official Android docs say that's not a good idea to reference activity context in ViewModel's (as ViewModel may outlive activity) so I've started to wonder about usecase when I want to execute some action when my activity is resumed.
I know ViewModel's shouldn't do business logic themselves but even if I use some service class (let's say GPSService which has to start and pauseeach time activity is resumed on paused), and inside this service I react to activity onResume (using Lifecycle observer) I will still reference this activity from ViewModel as I'm referencing service which holds reference to activity being observed, this may cause activity leak (correct me if I'm wrong).
So my question is, how to react to activity or fragment lifecycle in MVVM architecture?
If you need to have a ViewModel be lifecycle aware, then you can have it implement LifeCycleObserver and override life cycle events as necessary. Example,
public class MyModel extends ViewModel implements
LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
protected void onLifeCycleStop() {
// do something
}
}
In the activity or fragment then you can add the view model to the activity life cycle owner.
public class MyActivity extends AppCompatActivity {
protected MyModel mMyModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMyModel = ViewModelProviders
.of(this)
.get(MyModel.class);
getLifecycle().addObserver(mMyModel);
}
}
I know ViewModel's shouldn't do business logic themselves
Yes, you're right. ViewModel should not contain business logic but
it should contain UI related logic. So basically, API calls or Some
location related stuffs should be avoided in ViewModel logic.
So what if you wanna make some scenario which can react to any activity lifecycle? I'll suggest you to use LifecycleObserver.
Why?, Because LifecycleObserver will provide you callbacks once it's LifecycleOwner will change it's state.
What is LifecycleOwner here? In our case it may be Activity/Fragment.
So, how you can achieve this?
Let's say you want to make location requests during resume & pause period of any activity.
So, for that you can create a class called LocationUpdates as LifecycleObserver like below:
class LocationUpdates : LifecycleObserver {
constructor(){
// some basic location related initialization here
}
#OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun connectListener() {
// this method will respond to resume event of our Lifecycle owner (activity/fragment in our case)
// So let's get location here and provide callback
}
#OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun disconnectListener() {
// this method will respond to pause event of our Lifecycle owner (activity/fragment in our case)
// So let's stop receiveing location updates here and remove callback
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) // Optional if you want to cleanup references
fun cleanUp() {
// this method will respond to destroy event of our Lifecycle owner (activity/fragment in our case)
// Clean up code here
}
}
Now from your activity, you can directly make your LocationUpdates, and receive callback.
class MyActivity : AppCompatActivity() {
private lateinit var mLocationUpdates: LocationUpdates
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Initialize your LifecycleObserver here & assign it to this activity's lifecycle
lifecycle.addObserver(mLocationUpdates)
}
}
You can refer to how to handle Lifecycle & Codelabs example.
Edit:
If you want to have ViewModel for that job, consider this:
class MyViewModel : ViewModel {
private lateinit var mLocationUpdates: LocationUpdates
constructor() : super() {
// initialize LocationUpdates here
}
// Assign our LifecyclerObserver to LifecycleOwner
fun addLocationUpdates(lifecycle: Lifecycle){
lifecycle.addObserver(mLocationUpdates)
}
//Optional, we really don't need this.
fun removeLocationUpdates(lifecycle: Lifecycle){
lifecycle.removeObserver(mLocationUpdates)
}
}
If your LocationUpdates depends upon Context, consider using AndroidViewModel.
We can now observe our location updates # any activity/fragment using LiveData, and assign our LifecycleObserver like below:
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by lazy {
return#lazy ViewModelProviders.of(this#MyActivity).get(MyViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.addLocationUpdates(lifecycle)
}
}
Please note: there's still lot to cover but making this answer as short as possible. So, if you're still confused about something related then please feel free to ask me in comment. I will edit my answer.
with java 8 LifecycleObserver has been deprecated. According to the [docs][1] it is not recommended to use this class as it uses reflection.
Rather the docs recommend using DefaultLifecycleObserver. To do that, extend your ViewModel class with DefaultLifecycleObserver like:
class MyViewModel : ViewModel(), DefaultLifecycleObserver {//implement default lifecycle observer
override fun onCreate(owner: LifecycleOwner) {//override lifecycle events
super.onCreate(owner)
}
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
}
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
}
}
and get all the lifecycle event callbacks in your viewmodel by registering your viewmodel as lifecycle event observer in your view class (e.g. Activity class) like:
class MyActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
lifecycle.addObserver(splashViewModel)//registering observer
...
}
}
its just and update to the answer by #farid_z with kotlin and new sdk.
[1]: https://developer.android.com/reference/androidx/lifecycle/LifecycleObserver
While implementing Architecture components I am facing this issue
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleOwner
import android.os.Bundle
import com.reversebits.trendyvidz.R
class MainActivity : LifecycleOwner {
override fun getLifecycle(): Lifecycle {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
#OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme) //This shows error
setContentView(R.layout.activity_main) //This shows error
init()
}
private fun init() {
when {
isNetworkAvailable() -> toast("Yes Avail")
else -> toast("No")
}
}
}
How do I suppose to get Activity context-based methods here like setContentView() of AppCompatActivity?
As already pointed out, if you want to make an Activity you need to extend Activity.
Other than that there is a couple of issues in your code:
1)
If you use support library 26.1.0+ and lifecycles 1.0.0-alpha9-1+ lifecycles are already included because
AppCompatActivity extends FragmentActivity
FragmentActivity extends SupportActivity
SupportActivity extends Activity implements LifecycleOwner
2)
If you use older support libraries or lifecycles you had two options.
2a)
If your activity extended FragmentActivity, you would instead extend LifecycleActivity and that's it.
2b)
If you couldn't do that, you'd implement LifecycleRegistryOwner, for example:
class MyActivity extends AppCompatActivity implements LifecycleRegistryOwner {
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
#Override
public LifecycleRegistryOwner getLifecycle() {
return mLifecycleRegistry;
}
}
That's where sample codes end but I don't see any code actually dispatching the events. Upon investigating current SupportActivity it turns out it's using ReportFragment to dispatch all events. Add this to properly dispatch events:
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ReportFragment.injectIfNeededIn(this);
}
#CallSuper
protected void onSaveInstanceState(Bundle outState) {
this.mLifecycleRegistry.markState(State.CREATED);
super.onSaveInstanceState(outState);
}
Another thing, this is a mistake:
#OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate(savedInstanceState: Bundle?) {
The method onCreate triggers ON_CREATE event. Not the other way around. You'll get stack overflow error like this.
You use #OnLifecycleEvent(Lifecycle.Event.ON_CREATE) annotation on methods, that you want to trigger automatically after onCreate is called, not on onCreate method itself.
Okay, I figured it out,
I just need to do this:
class MainActivity : AppCompatActivity(), LifecycleOwner {
As LifecycleOwner is just an interface having a single method
override fun getLifecycle(): Lifecycle {
TODO("Not implemented") //To change body of created functions use File | Settings | File Templates.
}
And I am able to annotate methods with this annotations after implementing LifecycleOwner.
#OnLifecycleEvent(Lifecycle.Event.ON_START) etc.