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
Related
I have method, which returns response from server. For example:
fun uploadVideo(link: String, completionHandler: (Result<String>) -> Unit) {
// some action
completionHandler(Result.success(""))
}
I want to call this method one by one. Wait for a response from the previous one to call the next one. For example
uploadVideo("https://stackoverflow.com/video1.mp4") {
}
// call this only when i have response from preview request
uploadVideo("https://stackoverflow.com/video2.mp4") {
}
// call this only when i have response from preview request
uploadVideo("https://stackoverflow.com/video3.mp4") {
}
I tried use suspendCancellableCoroutine, like this
suspend fun uploadVideo(link: String?): String? = suspendCancellableCoroutine { cont ->
uri?.let {
uploadVideo(link,
completionHandler = {
it.onSuccess { uri ->
cont.resume(uri.toString())
}.onFailure {
cont.resumeWithException(it)
}
}
)
} ?: kotlin.run {
cont.resume(null)
}
}
and then call like this:
uploadVideo("https://stackoverflow.com/video1.mp4")
uploadVideo("https://stackoverflow.com/video2.mp4")
uploadVideo("https://stackoverflow.com/video3.mp4")
but these methods are not called sequentially, but in parallel
Note, the contents of your example API function don't quite make sense. If the callback were simply called inside the body of the function, then that would mean the function was blocking the whole time, which would mean there would be no reason for it to even have a callback. It could just directly return the value.
The actual contents of the API function might look more like this:
fun uploadVideo(link: String, completionHandler: (Result<String>) -> Unit) {
val callbackHandler = Handler(Looper.myLooper())
someOtherHandlerOrThreadPool.run {
// some action
callbackHandler.post {
completionHandler(Result.success(""))
}
}
}
The reason I bring that up is that the alternative to nesting a bunch of callbacks is use suspend functions and coroutines, but the code to convert the above to a suspend function doesn't make sense if it were a blocking function like in your version of it.
The basic pattern to convert a callback-based function into a suspend function is to use suspendCoroutine or suspendCancellableCoroutine. If uploadVideo was a function in some api class, you can define it as an extension function:
suspend fun SomeApiClass.uploadVideo(link: String): Result<String> = withContext(Dispatchers.Main) {
suspendCoroutine { cont ->
uploadVideo(link) { cont.resume(it) }
}
}
Now you can call this suspend function repeatedly in sequence if you're inside a coroutine or another suspend function:
fun foo() {
viewModelScope.launch {
val result1 = uploadVideo("https://stackoverflow.com/video1.mp4")
val result2 = uploadVideo("https://stackoverflow.com/video2.mp4")
val result3 = uploadVideo("https://stackoverflow.com/video3.mp4")
}
}
You could try this. this waits till the previous method called its callback and then runs the next one. Only if you have many images this is not a really nice way to do this.
fun uploadVideo(link: String, completionHandler: () -> Unit) {
// some action
completionHandler()
}
uploadVideo("https://stackoverflow.com/video1.mp4") {
uploadVideo("https://stackoverflow.com/video2.mp4") {
uploadVideo("https://stackoverflow.com/video3.mp4") {}
}
}
What I've tried so far
fun getCPByID(ids: List<Int>): List<CheckingPointVo> {
var list : List<CheckingPointVo> = emptyList()
coroutineScope.launch {
list = someMethod()
}
return list
}
here I tried to use async and await but that cannot be run from a non suspend function. Is there a way to do this ?
Not really with the current structure, you're basically trying to combine synchronous code with async.
You have 3 possible options though to make it async:
Use a callback:
fun getCPByID(ids: List<Int>, listCallback: (List<CheckingPointVo>) -> Unit) {
coroutineScope.launch {
listCallback(someMethod())
}
}
Note: If you're using it from Java, this should work with either Java lambdas or Function. But you may create an interface for this, like :
Interface ListCallback {
fun onListReceived(list: List<CheckingPointVo>)
}
fun getCPByID(ids: List<Int>, listCallback: ListCallback) {
.... // Same implementation
}
// Call it from Java
getCPByID(ids, new ListCallback() {
void onListReceived(List<CheckingPointVo> list) {
...
}
});
Use either an observable pattern, use a Flow or LiveData. A possible example:
fun getCPByID(ids: List<Int>) = coroutineScope.launch {
flow {
emit(someMethod())
}
}
}
Make your function a suspend function and use coroutineScope.launch from the caller
I have function which returns some value. In the function parameter I pass the same value:
fun getValue (value:String):String {
var message = value
value = "Hello"
return message
}
How can I call getValue function in another function? For example:
fun getResult (){
var a = getValue (what here?)
}
You can pass a function as a prameter for another function in kotlin.
Kotlin functions can take other functions in arguments, or even return them.
fun getResult (func:(String) -> String) {
//some code
var a = func("some string")
}
fun getValue (value:String):String {
var message = value
return message
}
and call getResult and pass function to it:
getResult({ getValue("Hello") })
Is there any way in kotlin using labels or any kind of feature so that we can jump out of the main function from the calling function.
What i want is when i return from jumperFunction i want to jump out of both the functions.
fun mainFunction(){
// some code and calling this function
jumperFunction()
}
fun jumperFunction{
// some code
return#mainFunction
}
Have jumperFunction() return a Boolean, and use that result:
fun mainFunction() {
//...
if (!jumperFunction()) return
//...
}
//returns false if the calling function should return
fun jumperFunction(): Boolean {
//...
}
Not sure I understood your use case, but you can have non-local returns with inlined lambdas.
Example:
fun main() {
baz {
println("Inside lambda")
return
}
}
inline fun baz(block: () -> Unit) {
println("[Baz] Before block")
block()
println("[Baz] After block")
}
Will print:
[Baz] Before block
Inside lambda
Note that it skips [Baz] After block
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.