I'm not sure what I'm looking to do is even possible. If it is, it's new to me.
here's a basic outline of what I'm trying to accomplish:
class MyClass : SomeInterface {
fun makeSomethingHappen() {
methodInInterfaceThatReturnsValueBelow()
}
override fun iDidSomething(result: Value) {
//give this value back to the original caller of makeSomethingHappen()
}
override fun iDidSomethingElse(result: Value) {
//give this value back to the original caller of makeSomethingHappen()
}
override fun onFailure(result: Value) {
//give this value back to the original caller of makeSomethingHappen()
}
}
Explanation:
We're using a required SDK that has about 15 overrides. I call into this class to call a function in the SDK. That function is going to call one of the override functions when it's done.
Is there a way (live data, flows, anything) to have whoever called makeSomethingHappen() receive the value from any of the override methods?
This is a basic example of reactive programming. You wait for an event that a producer/observable emits, it's like when you declare a click listener on a button.
You can't return the value in makeSomethingHappen(), but you can create a listener and the observer implements that listener to get the value.
typealias MyListener = (Value) -> Unit
class MyClass : SomeInterface {
private var _listeners: List<MyListener> = mutableListOf()
fun addListener(listener: MyListener) {
_listeners.add(listener)
}
fun makeSomethingHappen() {
methodInInterfaceThatReturnsValueBelow()
}
override fun iDidSomething(result: Value) {
// Send result Value to the listener implementations
_listeners.forEach { it.invoke(result) }
}
override fun iDidSomethingElse(result: Value) {
// Send result Value to the listener implementations
_listeners.forEach { it.invoke(result) }
}
override fun onFailure(result: Value) {
// Send result Value to the listener implementations
_listeners.forEach { it.invoke(result) }
}
}
The you can get the result implementing MyListener
val myClass = MyClass()
myClass.makeSomethingHappen { value ->
// Here you have the value and you can do whatever
print(value)
}
Related
private fun openTrailer() {
val trailerDialog = Dialog(this)
val trailerDialogBinding = DialogTrailerBinding.inflate(layoutInflater)
trailerDialog.setContentView(trailerDialogBinding.root)
youtubePLayerInit = object : YouTubePlayer.OnInitializedListener {
override fun onInitializationSuccess(
p0: YouTubePlayer.Provider?,
youtubePlayer: YouTubePlayer?,
p2: Boolean
) {
fun loadTrailer2(videoId: String) {
youtubePlayer?.loadVideo(videoId)
}
}
override fun onInitializationFailure(
p0: YouTubePlayer.Provider?,
p1: YouTubeInitializationResult?,
) {
toast("la")
}
}
if (!isInitialized) {
trailerDialogBinding.vvMovieTrailer.initialize(youtubeApiKey, youtubePLayerInit)
isInitialized = true
} else {
Log.e("initializerStatus", "Already Initialized")
}
trailerDialog.show()
}
How do I access access the loadTrailer2 function in the main code? Load Trailer is the function inside the object of the YoutubePlayer.OnInitializedListener. I'm trying to accesss it outside the open trailer function, aka in the onCreate method
Well, as it is right now, loadTrailer2 is a local function declared inside onInitializationSuccess - it only exists while that function is running, so you can't access it from outside that scope.
You could move it into the object itself, but since it relies on the YoutubePlayer object being passed in, how would you call it? Do you have access to that player in onCreate?
If you did have access to it (e.g. when the initialisation first succeeds, you store it in a ViewModel or something, and use that next time you create a fragment) you still have an issue: the type of youtubePlayerInit is YouTubePlayer.OnInitializedListener, and you're adding another method to your object, one that isn't part of that YouTubePlayer.OnInitializedListener interface.
What that means, is nothing actually knows that method exists. If you create that anonymous object inside a function, the compiler sees it as a special type that contains that method, so you can access it directly:
interface Thing {
fun onSomeEvent(code: Int)
}
fun main() {
doSetup()
}
fun doSetup() {
val myThing = object : Thing {
override fun onSomeEvent(code: Int) {
println("Event code: $code")
}
fun coolFunction() {
println("Secret function accessed")
}
}
// calling it inside the scope the object was declared in, so the function
// is visible here
myThing.coolFunction()
}
>> Secret function accessed
but outside of that scope, it just looks like the type declared when creating the object (Thing in this example):
interface Thing {
fun onSomeEvent(code: Int)
}
fun main() {
val myThing = doSetup()
// this only knows it's a Thing, which doesn't have this method
myThing.coolFunction()
}
fun doSetup(): Thing {
val myThing = object : Thing {
override fun onSomeEvent(code: Int) {
println("Event code: $code")
}
fun coolFunction() {
println("Secret function accessed")
}
}
return myThing
}
>> Unresolved reference: coolFunction
If you wanted to access that function, you'd have to e.g. declare a CoolFunctionHaver interface with that function, and either declare myThing as that type, or cast it if you really have to
I have a class below that updates a data variable. How can I observe when this variable changes?
object Manager {
private var data: Type = B()
fun doWork{
while(active) {
if(conditionA)
data = A()
else if(conditionB)
data = B()
}
}
fun getData(): Flow<Type>
}
interface Type {
}
Some classes that implements the interface.
class A: Type {}
class B: Type {}
I want to be able to observe these changes without using LiveData or anything that is Experimental. How can I let other areas of my code observe the data variable?
I know there is BroadcastChannel but I cannot use it because it is experimental.
You can use listener and built-in Kotlin delegate:
object Manager {
var dataListeners = ArrayList<(Type) -> Unit>()
// fires off every time value of the property changes
private var data: Type by Delegates.observable(B()) { property, oldValue, newValue ->
dataListeners.forEach {
it(newValue)
}
}
fun doWork{
while(active) {
if(conditionA)
data = A()
else if(conditionB)
data = B()
}
}
}
I have a quick question about Kotlin,
For example I have a class A which have this field:
private val observer: Observer<O> = object : Observer<O> {
override fun onChanged(output: O) {
}
}
Is there any Kotlin way of returning/passing/extending the onChange event (not the value) thru a method?
I don't want to expose the output thru a listener/callback(Java way).
What I'm looking for is to somehow return the onChanged method call, without using a "middle" object/callback
Thanks
when we say return a value, it returns a value back to the callee, in this case, whoever called the onChanged method. This happens in case of synchronous calls.
In this case, onChanged call will be invoked in an asynchronous manner which makes it impossible to simply return a value back to the callee without a callback.
If i correctly understand your question, you can use observer.onChanged as Kotlin Function:
val observerOnChangedFunction = observer.run { ::onChanged }
Than you can invoke this function:
observerOnChangedFunction(instanceOfO)
Usecase: onChanged as var field
class Foo<O> {
var onChanged: (O) -> Unit = { /* default */}
private val observer: Observer<O> = object : Observer<O> {
override fun onChanged(output: O) = onChanged(output)
}
}
fun main() {
val foo = Foo<Int>()
foo.onChanged = { it.toString() }
}
-
Usecase: parameter in constructor as observer
class Foo<O> (
observer: Observer<O>
) {
private val observer: Observer<O> = observer
}
-
Usecase: parameter in constructor as onChanged lambda
class Foo<O> (
onChanged: (O) -> Unit
) {
private val observer: Observer<O> = object : Observer<O> {
override fun onChanged(output: O) = onChanged(output)
}
}
How can I create a class which could be more reusable with enum classes, as I might have few more classes later on? My point is to make it more reusable, flexible and global for other usage.
enum class PaymentMethodType(val type: String) {
PAYPAL("Paypal"),
VISA("Visa"),
MASTERCARD("MasterCard"),
VISA_DEBIT("VISA Debit"),
LPQ_CREDIT("Lpq Credit");
companion object {
private val TAG: String = this::class.java.simpleName
fun fromString(name: String): PaymentMethodType? {
return getEnumFromString(PaymentMethodType::class.java, name)
}
private inline fun <reified T : Enum<T>> getEnumFromString(c: Class<T>?, string: String?): T? {
if (c != null && string != null) {
try {
return enumValueOf<T>(
string.trim()
.toUpperCase(Locale.getDefault()).replace(" ", "_")
)
} catch (e: IllegalArgumentException) {
Log.e(TAG, e.message)
}
}
return null
}
}
}
You can generalize your getEnumFromString function by creating an interface and having your companion object implementing it. An extension on this interface will let you call the function directly on the companion of your enum class.
This will do the trick:
interface EnumWithKey<T : Enum<T>, K> {
val T.key: K
}
/* The reified type parameter lets you call the function without explicitly
* passing the Class-object.
*/
inline fun <reified T : Enum<T>, K> EnumWithKey<T, K>.getByKey(key: K): T? {
return enumValues<T>().find { it.key == key }
}
Now you can create your PaymentMethodType like this:
enum class PaymentMethodType(val type: String) {
PAYPAL("Paypal"),
VISA("Visa"),
MASTERCARD("MasterCard"),
VISA_DEBIT("VISA Debit"),
LPQ_CREDIT("Lpq Credit");
companion object : EnumWithKey<PaymentMethodType, String> {
// Just define what the key is
override val PaymentMethodType.key
get() = type
}
}
And voila, now you can do this:
println(PaymentMethodType.getByKey("Paypal")) // Prints PAYPAL
The EnumWithKey interface can now be reused by just having the companion object of an enum implementing it.
Well? How about this code?
enum class PaymentMethodType(val type: String) {
PAYPAL("Paypal"),
VISA("Visa"),
MASTERCARD("MasterCard"),
VISA_DEBIT("VISA Debit"),
LPQ_CREDIT("Lpq Credit");
companion object {
private val TAG: String = PaymentMethodType::class.simpleName
fun fromString(name: String?): PaymentMethodType? {
val maybeType = PaymentMethodType.values().firstOrNull { it.type == name }
if (maybeType == null) {
Log.e(TAG, "No corresponding PaymentMethodType for $name")
}
return maybeType
}
}
}
Just made getEnumFromString method simpler like this way.
Moreover, if you want to make your PaymentMethodType more "reusable, flexible and global", add some abstract method onto your PaymentMethodType or consider using Sealed class in this case. We can guess that many payment methods require their own protocols, and implementing it by enum requires an externalised when or if-else branch to do so. For example, the code should be looks like this:
fun paymentProcessor(payment: PaymentMethodType): Boolean {
return when (payment) {
PAYPAL -> { processPaypalPayment() }
VISA -> { processVisaPayment() }
// ...
}
}
which is not bad unless numbers of payment methods are limited but not quite desirable. We can remove this insidious if or when keyword like this way(retaining enum class approach):
enum class PaymentMethodType(val type: String) {
PAYPAL("Paypal") {
override fun processPayment(): Boolean {
TODO("Not implemented.")
}
},
VISA("Visa") {
override fun processPayment(): Boolean {
TODO("Not implemented.")
}
},
// ... more types ...
;
abstract fun processPayment(): Boolean
// ...
}
With either approach, we can eliminate when keyword in paymentProcessor method I demonstrated like this:
fun paymentProcessor(payment: PaymentMethodType): Boolean {
return payment.processPayment()
}
I don't explain sealed class approach since the code is not much different compare to enum class approach in this case. The official document may help.
Hope this helps.
Get all enum values with PaymentMethodType.values(), then use find() to get the one you need:
fun fromString(type: String): PaymentMethodType? = PaymentMethodType.values().find { it.type.toLowerCase() == type.toLowerCase() }
So my goal like the the title says is to pass API response results throw an onSuccess.run() method to a fragment. Just to give some context, I start to do the method like this in a Manager class for example:
override fun callUser(onSuccess:Runnable, onFailure:Runnable){
NetworkManager.instance.performCall(NetworkManager.REGISTRATION.verifyUser(id),
object : NetworkManager.OnRequestCallback<UserInfoResponse> {
override fun onSuccess(body: UserInfoResponse?) {
body?.data?.let {
onSuccess.run()
}
}
override fun onError(errorBody: String?) {
onFailure.run()
}
})
}
Then I go to a fragment and call the method above like this:
objectManager.callVerifyAdvisor(
Runnable {[On Success stuff },
Runnable {[On Error]}
}
The problem is that, although I can decide in the fragment the actions I want to do in the onSuccess() and onFailure() methods, I cant get the API Results to that fragment by doing this way.
So my ideia is to do something like [I used comments to specify the sections that matter]:
NetworkManager.instance.performCall(NetworkManager.REGISTRATION.verifyUser(id),
object : NetworkManager.OnRequestCallback<UserInfoResponse> {
override fun onSuccess(body: UserInfoResponse?) {
body?.data?.let {
it.userName // I get this from api response
onSuccess.run()
}
}
override fun onError(errorBody: String?) {
onFailure.run()
}
})
}
Then on my fragment I want something like this:
objectManager.callVerifyAdvisor(
Runnable {[On Success stuff }, //receive here the it.userName
Runnable {[On Error]}
}
Can someone give any ideia how to do this? Side note -> I put kotlin on the tag because this has some kind of functional stuff.
You just need to replace you onSuccess from Runnable to your custom functional type callback:
override fun callUser(onSuccess: (String) -> Unit, onFailure: Runnable) {...
Then pass userName to callback:
NetworkManager.instance.performCall(NetworkManager.REGISTRATION.verifyUser(id),
object : NetworkManager.OnRequestCallback<UserInfoResponse> {
override fun onSuccess(body: UserInfoResponse?) {
body?.data?.let {
onSuccess.invoke(it.userName) // Pass userName
}
}
override fun onError(errorBody: String?) {
onFailure.run()
}
})
}
And then you can get userName from this callback:
objectManager.callVerifyAdvisor(
{userName -> } // Here you get your result
Runnable {[On Error]}
}