I am trying to create an Extension function in android with Handlers but facing issue:
Extension fun Code:
fun delayTask(millis: Long, myFunction: (data:String) -> Unit) {
Handler().postDelayed({
myFunction(data) //why error is of data here
}, millis)
}
Calling like this:
delayTask(500, ::function)
Getting error Unresolved reference: data
data is not a parameter of your higher order function. It is a parameter of your function parameter. So it doesn't exist for you to pass to the passed function.
To be able to pass this data to your lambda, you will need to add it as another parameter:
fun delayTask(millis: Long, data: String, myFunction: (String) -> Unit) {
Handler().postDelayed({
myFunction(data)
}, millis)
}
And when you call it, you would have to also pass the data:
delayTask(500, someDataString, ::function)
Your function could be more versatile by removing the parameter from the function parameter. Then you could call any function with any amount of parameters needed just by wrapping it in a lambda:
fun delayTask(millis: Long, myFunction: () -> Unit) {
Handler().postDelayed({
myFunction()
}, millis)
}
delayTask(500) { myFunction(someData) }
For performance reasons, it would be better to make it inline. But the passed function has to be crossinline since it's wrapped in another object and called later:
inline fun delayTask(millis: Long, crossinline myFunction: () -> Unit) {
Handler().postDelayed({
myFunction()
}, millis)
}
Note this functionality is already available with the postDelayed function in Android Ktx core:
Handler().postDelayed(500L) { someFunction() }
Related
I have these two overloads for a higher order function:
// first function
fun<T> Response<T>.onSuccess(predicate: (T) -> Unit) {
val body = body()
if (isSuccessful && body != null) {
predicate(body)
}
}
// second function
fun<T> Response<T>.onSuccess(predicate: () -> Unit) {
if (isSuccessful)
predicate()
}
The first one has a function-type which takes a generic type argument and returns Unit. While the second function has a function-type that doesn't take any arguments.
The Response is the retrofit2.Response object which takes a generic type.
I can call the first function by explicitly naming the lamda parameter as follows:
response.onSuccess { result ->
// not implemented
}
But the compiler gives a overload resolution ambiguity error when I try to call the second function:
response.onSuccess { // error
// not implemented
}
Overload resolution ambiguity. All these functions match.
public fun Response<TypeVariable(T)>.onSuccess(predicate: () → Unit): Unit defined in com.vs.data.network in file NetworkHelper.kt
public fun Response<TypeVariable(T)>.onSuccess(predicate: (TypeVariable(T)) →
Unit): Unit defined in com.vs.data.network in file NetworkHelper.kt
How to pass a function in android using Kotlin . I can able to pass if i know the function like :
fun a(b :() -> Unit){
}
fun b(){
}
I want to pass any function like ->
fun passAnyFunc(fun : (?) ->Unit){}
You can use anonymous function or a lambda as follows
fun main(args: Array<String>) {
fun something(exec: Boolean, func: () -> Unit) {
if(exec) {
func()
}
}
//Anonymous function
something(true, fun() {
println("bleh")
})
//Lambda
something(true) {
println("bleh")
}
}
Method as parameter Example:
fun main(args: Array<String>) {
// Here passing 2 value of first parameter but second parameter
// We are not passing any value here , just body is here
calculation("value of two number is : ", { a, b -> a * b} );
}
// In the implementation we will received two parameter
// 1. message - message
// 2. lamda method which holding two parameter a and b
fun calculation(message: String, method_as_param: (a:Int, b:Int) -> Int) {
// Here we get method as parameter and require 2 params and add value
// to this two parameter which calculate and return expected value
val result = method_as_param(10, 10);
// print and see the result.
println(message + result)
}
Use an interface:
interface YourInterface {
fun functionToCall(param: String)
}
fun yourFunction(delegate: YourInterface) {
delegate.functionToCall("Hello")
}
yourFunction(object : YourInterface {
override fun functionToCall(param: String) {
// param = hello
}
})
First declare a lambda function(a function without name is known as lamdda function. its comes from kotlin standard lib not kotlin langauage which strat with {} ) in Oncreate like below
var lambda={a:Int,b:Int->a+b}
Now Create a function which accept another function as a parameter like below
fun Addition(c:Int, lambda:(Int, Int)-> Int){
var result = c+lambda(10,25)
println(result)
}
Now Call Addition function in onCreate by passing lambda as parameter like below
Addition(10,lambda)// output 45
Is it possible to call a function with multiple lambda functions?
If so, how can I invoke the following function?
fun post(path: String,
params: JSONObject,
completionHandler: (response: JSONObject?) -> Unit,
errorCompletionHandler: (error: VolleyError?) -> Unit
)
Yes, you can have as many lambdas as you like. The shown post can be invoked as follows:
post("/a", "json", {response-> println(response) }, { error-> println(error)})
It's also possible to lift the last lambda out of the parentheses as described in the documentation:
In Kotlin, there is a convention that if the last parameter to a function is a function, and you're passing a lambda expression as the corresponding argument, you can specify it outside of parentheses.
Applied to your code, this means:
post("/a", "json", { response -> println(response) }) { error ->
println(error)
}
You would define the lambdas as shown below. You can assign them to variables to make the code more readable. This becomes especially handy if you lambda become bigger.
val completionHandler: (JSONObject?) -> Unit = { response ->
// ...
}
val errorCompletionHandler: (VolleyError?) -> Unit = { error ->
// ...
}
post("/path", jsonObject, completionHandler, errorCompletionHandler)
Or you can define functions which you pass using a reference:
fun errorCompletionHandler(error: VolleyError?) {}
fun completionHandler(response: JSONObject?) {}
post("/path", jsonObject, ::completionHandler, ::errorCompletionHandler)
Notice that Unit can be ommitted here because it is the implicit return type if nothing else was specified.
I'm converting my function having lambda as parameter into inline function for performance improvement.
I have list of lambda of type MutableList<(Authenticate) -> Unit> variable as data member in class. When I try to adding lambda parameter into the list.
Kotlin compiler says:
Illegal usage of inline parameter callback
Here is the code
// Some code skipped
object Odoo {
val pendingAuthenticateCallbacks = mutableListOf<(Authenticate) -> Unit>()
inline fun authenticate(
login: String, password: String, database: String,
quick: Boolean = false, crossinline callback: Authenticate.() -> Unit
) {
// Following statement has error saying
// Illegal usage of inline parameter callback. add 'noinline' modifier to parameter declaration.
pendingAuthenticateCallbacks += callback
// Error in above statement
if (pendingAuthenticateCallbacks.size == 1) {
// Retrofit2 Object boxing code skipped
val call = request.authenticate(requestBody)
call.enqueue(object : Callback<Authenticate> {
override fun onFailure(call: Call<Authenticate>, t: Throwable) {
(pendingAuthenticateCallbacks.size - 1 downTo 0)
.map { pendingAuthenticateCallbacks.removeAt(it) }
.forEach {
it(Authenticate(httpError = HttpError(
Int.MAX_VALUE,
t.message!!
)))
}
}
override fun onResponse(call: Call<Authenticate>, response: Response<Authenticate>) {
(pendingAuthenticateCallbacks.size - 1 downTo 0)
.map { pendingAuthenticateCallbacks.removeAt(it) }
.forEach {
it(Authenticate(httpError = HttpError(
response.code(),
response.errorBody()!!.string()
)))
}
}
})
}
}
}
Inlining inserts the code in the lambda directly into the call site, which removes the overhead of having a function object.
For example, this roughly results in main here:
fun withLambda(lambda: () -> Unit) {
lambda()
}
inline fun inlinedLambda(lambda: () -> Unit) {
lambda()
}
fun main(args: Array<String>) {
withLambda { println("Hello, world") }
inlinedLambda { println("Hello, world") }
}
being converted to this:
fun main(args: Array<String>) {
withLambda { println("Hello, world") }
println("Hello, world") // <- Directly inserted!
}
If you have
pendingAuthenticateCallbacks += callback
This is impossible because callback must be an object in order for it to be added to the list.
You need to add the noinline modifier.
A rough approximation would be to say that an inlined lambda cannot be treated as an object, as it doesn't really exist as an object. It is used directly instead of being created as an object.
Of course, you could create a containing lambda:
pendingAuthenticateCallbacks += { callback() } // Not a good idea
but this would entirely defeat the point of inlining (don't do this!).
However, making the parameter noinline would mean your method now has zero lambda parameters that can be inlined, so you might as well just remove the inline modifier as performance benefit would be minimal.
The compiler should recognize this:
Note that if an inline function has no inlinable function parameters and no reified type parameters, the compiler will issue a warning, since inlining such functions is very unlikely to be beneficial.
The main reason for inlining methods is for performance when using lambdas and for reified generic type parameters. As of Kotlin 1.1, it is also possible to have an inline property accessor for properties without a backing field.
In short, if you have no lambda parameters (or no reified type parameters, in which case you must), it is usually pointless to mark a function as inline.
I'm trying to figure out how to use an extension function to run any method with a delay, but can't seem to figure it out.
I am trying something like below where I have a function and I want a handler to delay the execution by a certain timeInterval:
functionX().withDelay(500)
functionY().withDelay(500)
private fun Unit.withDelay(delay: Int) {
Handler().postDelayed( {this} , delay)}
private fun Handler.postDelayed(function: () -> Any, delay: Int) {
this.postDelayed(function, delay)}
Anyone?
Another approach would be to declare a top-level (i.e. global) function like this:
fun withDelay(delay : Long, block : () -> Unit) {
Handler().postDelayed(Runnable(block), delay)
}
Then you can call it, from anywhere, like this:
withDelay(500) { functionX() }
You should put the extension on the function type, not Unit:
fun functionX() {}
fun functionY() {}
private fun (() -> Any).withDelay(delay: Int) {
Handler().postDelayed(this , delay)
}
Usage:
::functionX.withDelay(500)
::functionY.withDelay(500)
Here, ::functionX is a reference to the global function called functionX.
Or I like this version too:
Wrap whatever code block you want to be executed within { ... }
{ invokeSomeMethodHere() }.withDelay()
And have an extension function that invokes the Runnable after a certain delay:
fun <R> (() -> R).withDelay(delay: Long = 250L) {
Handler().postDelayed({ this.invoke() }, delay)
}
For example, you can declare your global variables like:
private var handler: Handler = Handler()
private lateinit var runnableNewUrlBackground: Runnable
And also declare the function as global
init {
runnableNewUrlBackground = Runnable {
// Your function code here
myCustomFun();
}
}
Then, when you want to call this, just use:
handler.postDelayed(runnableNewUrlBackground, YOUR_DESIRED_TIME)