Lazybones
Ah... I'm a super lazy person.
I just want to declare initialization and disposition together.
Including in your project
Gradle
Add below codes to your root build.gradle
file (not your module build.gradle file).
allprojects {
repositories {
mavenCentral()
}
}
And add a dependency code to your module's build.gradle
file.
dependencies {
implementation "com.github.skydoves:lazybones:1.0.2"
}
SNAPSHOT
Snapshots of the current development version of Lazybones are available, which track the latest versions.
repositories {
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
Usage
lifecycleAware
We can initialize a lifecycle-aware object lazily using the lifecycleAware
keyword. The lifecycleAware
functionality can be used to register & unregister listeners, clear something, show & dismiss, and dispose of disposable objects as lifecycle changes by the lifecycleOwner(Activity, Fragment). If we want to initialize an object lazily, we should use it with by
keyword and lazy()
method.
val myDialog: Dialog by lifecycleAware { getDarkThemeDialog(baseContext) }
.onCreate { this.show() } // show the dialog when the lifecycle's state is onCreate.
.onDestroy { this.dismiss() } // dismiss the dialog when the lifecycle's state is onDestroy.
.lazy() // initlize the dialog lazily.
In the onCreate
and onDestroy
lambda function, we can omit the this
keyword. In the below example, the MediaPlayer
will be initialized and the start()
will be invoked on the onCreate
state of the lifecycle. And the pause()
, stop()
, or release()
will be invoked based on the state of the lifecycle.
private val mediaPlayer: MediaPlayer by lifecycleAware {
MediaPlayer.create(this, R.raw.bgm3)
}.onCreate {
isLooping = true
start()
}.onStop {
pause()
}.onResume {
start()
}.onDestroy {
stop()
release()
}.lazy()
The above code works the same as the below codes.
private val mediaPlayer: MediaPlayer by lazy { MediaPlayer.create(this, R.raw.bgm3) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mediaPlayer.isLooping = true
mediaPlayer.start()
}
override fun onPause() {
super.onPause()
mediaPlayer.pause()
}
override fun onStop() {
super.onStop()
mediaPlayer.pause()
}
override fun onResume() {
super.onResume()
mediaPlayer.start()
}
override fun onDestroy() {
super.onDestroy()
mediaPlayer.stop()
mediaPlayer.release()
}
CompositeDisposable in RxJava2
Here is an example of CompositeDisposable
in RxJava2.
At the same time as initializing lazily the CompositeDisposable, the dispose()
method will be
invoked automatically when onDestroy.
val compositeDisposable by lifecycleAware { CompositeDisposable() }
.onDestroy { dispose() } // call the dispose() method when onDestroy this activity.
.lazy() // initialize a CompositeDisposable lazily.
Lifecycle related methods
We can invoke lambda functions as lifecycle changes and here are eight lifecycle-related methods of lifecycleAware
.
.onCreate { } // the lambda will be invoked when onCreate.
.onStart { } // the lambda will be invoked when onStart.
.onResume { } // the lambda will be invoked when onResume.
.onPause { } // the lambda will be invoked when onPause.
.onStop { } // the lambda will be invoked when onStop.
.onDestroy { } // the lambda will be invoked when onDestroy.
.onAny { } // the lambda will be invoked whenever the lifecycle state is changed.
.on(On.Create) { } // we can set the lifecycle state manually as an attribute.
Usages in the non-lifecycle owner class
The lifecycleAware
is an extension of lifecycleOwner
so it can be used on non- lifecycle-owner classes.
class MainViewModel(lifecycleOwner: LifecycleOwner) : ViewModel() {
private val compositeDisposable by lifecycleOwner.lifecycleAware { CompositeDisposable() }
.onDestroy { it.dispose() }
.lazy()
...
LifecycleAwareProperty
If we don't need to initialize lazily, here is a more simple way. We can declare a LifecycleAwareProperty
using the lifecycleAware
keyword. The attribute value will not be initialized lazily. so we don't need to use it with by
keyword and lazy()
method.
private val lifecycleAwareProperty = lifecycleAware(CompositeDisposable())
// observe lifecycle's state and call the dispose() method when onDestroy
.observeOnDestroy { dispose() }
And we can access the original property via the value
field.
lifecycleAwareProperty.value.add(disposable)
lifecycleAwareProperty.value.dispose()
We can observe the lifecycle changes using observe_
method.
class MainActivity : AppCompatActivity() {
private val lifecycleAwareProperty = lifecycleAware(DialogUtil.getDarkTheme())
.observeOnCreate { show() }
.observeOnDestroy { dismiss() }
.observeOnAny { .. }
.observeOn(On.CREATE) { .. }
...
Here is the kotlin DSL way.
private val lifecycleAwareProperty = lifecycleAware(getDarkThemeDialog())
.observe {
onCreate { show() }
onResume { restart() }
onDestroy { dismiss() }
}
Using in the non-lifecycle owner class
The lifecycleAware
is an extension of lifecycleOwner
so it can be used on non- lifecycle-owner classes.
class MainViewModel(lifecycleOwner: LifecycleOwner) : ViewModel() {
private val TAG = MainViewModel::class.java.simpleName
private val lifecycleAwareProperty = lifecycleOwner.lifecycleAware(Rabbit())
init {
this.lifecycleAwareProperty
.observeOnCreate { Log.d(TAG, "OnCreate: $this") }
.observeOnStart { Log.d(TAG, "OnStart: $this") }
.observeOnResume { Log.d(TAG, "OnResume: $this") }
.observeOnPause { Log.d(TAG, "OnPause: $this") }
.observeOnStop { Log.d(TAG, "OnStop: $this") }
.observeOnDestroy { Log.d(TAG, "OnDestroy: $this") }
.observeOnAny { }
.observeOn(On.CREATE) { }
}
...
Coroutines and Flow
Add a dependency code to your module's build.gradle
file.
dependencies {
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$versions.lifecycle" // over the 2.4.0-alpha01
}
We can apply to the coroutines lifecycleScope
for launching suspend functions. The Lifecycle-runtime-ktx supports launchWhenStarted
, launchWhenCreated
, and launchWhenResumed
for the lifecycleScope
. However those coroutines jobs will not be canceled automatically, so they must be canceled
on a specific lifecycle. And we can declare canceling together with initialization like the below.
private val job: Job by lifecycleAware {
lifecycleScope.launchWhenCreated {
// call suspend
}
}.onDestroy {
cancel() // cancel when the lifecycle is destroyed.
}.lazy()
We can reduce the above codes like the below using the launchOnStarted
, launchOnCreated
, and launchOnResume
.
private val job: Job by launchOnStarted {
// call suspend
}.onDestroy {
cancel()
}.lazy()
If we need to collect one flow on the coroutines lifecycle scope, we can use like the below.
private val job: Job by launchOnStarted(repository.fetchesDataFlow()) {
// collected value from the repository.fetchesDataFlow()
}.onDestroy {
cancel()
}.lazy()
addOnRepeatingJob
The addRepeatingJob extension has been added in the new version of the Lifecycle-runtime-ktx.
Launches and runs the given block in a coroutine when this LifecycleOwner's Lifecycle is at least at state. The launched coroutine will be canceled when the lifecycle state falls below the state.
We can collect a flow on the coroutines lifecycle scope and cancel it automatically if the lifecycle falls below that state, and will restart if it's in that state again.
private val job: Job by addOnRepeatingJob(Lifecycle.State.CREATED, simpleFlow()) {
// collected value from the fetchesDataFlow()
}.lazy()
β€οΈ
Find this library useful? Support it by joining stargazers for this repository.
And follow me for my next creations!
License
Copyright 2020 skydoves (Jaewoong Eum)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the L