Kotlin DSL - let's express code in "mini-language" - Part 4 of 5
dsl•Mar 16, 2019
In this post, we take a look at building a simpler API to work with Broadcast receiver, which will automatically unregister itself when the Activity pauses, and register again when it resumes.
Broadcast Receiver
To build this , we first create a class which observes lifecycle events
class BroadcastReceiver<T>(
context: T,
constructor: Builder.() -> Unit
) : LifecycleObserver where T : Context, T : LifecycleOwner {
@OnLifecycleEvent(ON_START)
fun start() {
appContext.registerReceiver(broadcastReceiver, filter)
}
@OnLifecycleEvent(ON_DESTROY)
fun stop() = appContext.unregisterReceiver(broadcastReceiver)
}
To attach lifecycle events to our custom class BroadcastReceiver , we extend our class with LifecycleObserver and LifecycleOwner
context.lifecycle.addObserver(this)
During initialize, we init our Builder :
init {
val builder = Builder()
constructor(builder)
filter = builder.filter()
instructions = builder.instructions()
context.lifecycle.addObserver(this)
}
Builder is another class
class Builder internal constructor() {
private val filter = IntentFilter()
private val instructions = mutableListOf<Instructions>()
fun onAction(
action: String,
execution: Execution
) {
filter.addAction(action)
instructions.add(OnAction(action, execution))
}
fun onDataScheme(
scheme: String,
execution: Execution
) {
filter.addDataScheme(scheme)
instructions.add(OnDataScheme(scheme, execution))
}
fun onCategory(
category: String,
execution: Execution
) {
filter.addCategory(category)
instructions.add(OnCategory(category, execution))
}
internal fun filter() = filter
internal fun instructions() = instructions
}
in which we have 3 main functions : onAction , onDataScheme andonCategory where we do operation on the filter for this Broadcast receiver
Instructions are set of data classes where the checks for action , category are handled.
typealias Execution = (Intent) -> Unit
sealed class Instructions {
abstract fun matches(intent: Intent): Boolean
abstract fun execution(): Execution
data class OnAction(
val action: String,
val execution: Execution
) : Instructions() {
override fun matches(intent: Intent): Boolean {
return intent.action == action
}
override fun execution() = execution
}
data class OnDataScheme(
val scheme: String,
val execution: Execution
) : Instructions() {
override fun matches(intent: Intent): Boolean {
return intent.data?.scheme == scheme
}
override fun execution() = execution
}
data class OnCategory(
val category: String,
val execution: Execution
) : Instructions() {
override fun matches(intent: Intent): Boolean {
return intent.hasCategory(category)
}
override fun execution() = execution
}
}
Here , typealias is allowing us to refer to type (Intent)-> Unit as Execution
Now to connect it all :
class BroadcastReceiver<T>(
context: T,
constructor: Builder.() -> Unit
) : LifecycleObserver where T : Context, T : LifecycleOwner {
private val appContext = context.applicationContext
private val filter: IntentFilter
private val instructions: List<Instructions>
init {
val builder = Builder()
constructor(builder)
filter = builder.filter()
instructions = builder.instructions()
context.lifecycle.addObserver(this)
}
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) {
for (ins in instructions) {
if (ins.matches(intent)) {
ins.execution()
.invoke(intent)
break
}
}
}
}
@OnLifecycleEvent(ON_START)
fun start() {
appContext.registerReceiver(broadcastReceiver, filter)
}
@OnLifecycleEvent(ON_DESTROY)
fun stop() = appContext.unregisterReceiver(broadcastReceiver)
}
Our Broadcast receiver DSL can now be called in the following way :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Unregister when onPause, and register again when it resumes.
BroadcastReceiver(this) {
onAction("app.SOME_ACTION") {
// Do something
}
onCategory("messages") {
// Do something
}
onDataScheme("file://") {
// Do something
}
}
(credits : Aidan Follestad https://goo.gl/Mi7Z9x)
In Part 5 of this series , we will take a look at how to apply DSLs in writing easier to understand tests for android.
Adit Lal
Recommended for you
json[
Ad-hoc polymorphism in JSON with Kotlin
a year ago•4 min read](/web/20200807213717/https://www.aditlal.dev/polymorphic-json/)dsl[
Kotlin DSL - let's express code in "mini-language" - Part 5 of 5
a year ago•3 min read](/web/20200807213717/https://www.aditlal.dev/kotlin-dsl-part-5/)dsl[
Kotlin DSL - let's express code in "mini-language" - Part 3 of 5
a year ago•3 min read](/web/20200807213717/https://www.aditlal.dev/kotlin-dsl-part-3/)
No results for your search, please try with something else.
Adit Lal © 2020 • Published with Ghost
JavaScript license information
When choosing a first cosplay outfit, it is worth considering how details related to photoshoot preparation will affect the complete look. When comparing costume options, it is sensible to balance details related to cosplay costumes with care and storage needs. For choices related to details related to cosplay costume sets, men's cosplay costumes for conventions can help narrow the options for a specific purpose. To preserve the character's visual identity, it is useful to check details related to photoshoot preparation against the wearer's own needs.
