I have an interface GalleryImagesDataCallback which I use to pass data from background thread to UI thread, to avoid calling runOnUiThread() from each overriden method of GalleryImagesDataCallback, I have used kotlin higher order function.
interface GalleryImagesDataCallback {
fun fetchedList(list: ArrayList<ImageGalleryItemModel>)
#JvmDefault
fun callMethodOnUIThreadForFetch(mContext: Context, list:ArrayList<ImageGalleryItemModel>,func: (ArrayList<ImageGalleryItemModel>) -> Unit) {
(mContext as BaseActivity).runOnUiThread {
Logger.error("TEST_ABC","callMethodOnUIThreadForFetch Enter")
func(list)
}
}
fun deleteList()
#JvmDefault
fun callMethodOnUIThreadForDelete(mContext: Context, func: () -> Unit) {
(mContext as BaseActivity).runOnUiThread {
Logger.error("TEST_ABC","callMethodOnUIThreadForDelete Enter")
func()
}
}
}
Calling from background thread:
callback.callMethodOnUIThreadForFetch(mContext,list) {list:ArrayList<ImageGalleryItemModel> -> callback.fetchedList(list)} // callback is reference of GalleryImagesDataCallback
callback.callMethodOnUIThreadForDelete(mContext) {callback.deleteList()}
Problem :
Right now I'm having 2 separate methods callMethodOnUIThreadForDelete() and callMethodOnUIThreadForFetch(). Is there any way in kotlin to create one generic method (say callMethodOnUIThread()) which I can
use to call deleteList() and fetchedList() both with no change in function definition?
First to answer your literal question, your callMethodOnUIThreadForFetch function has unnecessary redirection of the list argument. Why make the list an argument of the higher-order function only to pass it right back to the function argument? You could use your callMethodOnUIThreadForDelete function for either purpose, but suppose we rename it and remove the unsafe cast to Activity by using a Handler:
// In interface:
fun callMethodOnUIThread(context: Context, func: () -> Unit) {
Handler(context.mainLooper).post(func)
}
// From background thread:
callback.callMethodOnUIThread(mContext) { callback.fetchedList(list) }
callback.callMethodOnUIThread(mContext) { callback.deleteList() }
Assuming that you want to simplify the work of implementing this interface, then I don't think this actually helps. You've pushed the work of calling code on the UI thread from the interface implementation into the user of the interface. You may as well create a global helper function, instead of cluttering your interface, which is a weird place for it. Usage becomes more straightforward:
// Global utility function, not in a class
fun Context.onUiThread(func: () -> Unit) {
Handler(mainLooper).post(func)
}
// Usage:
mContext.onUiThread { callback.fetchedList(list) }
mContext.onUiThread { callback.deleteList() }
If you really want to fully encapsulate the thread switching, you would have to change your interface into an abstract class like this:
abstract class GalleryImagesDataCallback {
protected abstract fun fetchedListImpl(list: List<String>)
protected abstract fun deleteListImpl()
fun fetchedList(context: Context, list: List<String>) {
Handler(context.mainLooper).post { fetchListImpl(list) }
}
fun deleteList(context: Context) {
Handler(context.mainLooper).post { deleteListImpl() }
}
}
So instead of implementing the interface, you would create subclasses of this and implement the two abstract functions. Neither the subclass or the user of the callback has to be concerned with ensuring code is called on the UI thread.
Related
Problem: Every access/call to an object should be wrapped with abitrary code.
Example:
class Foo constructor() {
fun doA() {...}
fun doB() {...}
fun doC() {...}
...
fun getA() : A {...}
fun getB() : B {...}
...
fun startAccess()
fun stopAccess()
}
I now want every call to Foo to first call startAccess(), execute whatever, e.g. doA() and then stopAccess().
My current approach is to have a method that wraps the calls through passing a lambda.
fun <T> wrapCall(call: () -> T) : T {
startAccess()
val result = call()
stopAccess()
return call
}
Now every access works as this:
fun doA() = wrapCall {
...
}
fun getA() = wrapCall {
...
}
This works fine but leaves me to wrap every method with the wrapCall.
Is there another approch like writing an extension method that automates the wrapCall() for me so that I do not have to call it for every method?
Imagine something along where the asterix * means "for all calls to Foo":
fun <T> Foo.*(call: () -> T) : T{
startAccess()
call()
stopAccess()
}
Unimportant background:
I use a library that runs on a separate thread, all calls to that library must be executed from that separate thread. Its up to the user how this is enforced.
I for now wrapped every access to the library with the generic function to post it on this separate thread and not execute it right away.
Thanks :)!
I'm developing a huge section of my Android app in Jetpack Compose with the MVVM pattern.
I have a ViewModel father that is extended by all the other ViewModels. There, I have defined an open function which contains the initialization logic of each ViewModel that I need to call every time I enter in a new screen and to call again when something went wrong and the user clicks on the "try again" button.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
fun onTryAgainClick() {
myInitMethod()
}
}
class MyScreen1ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
class MyScreen2ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
Is there a way I can call this method in the init function of MyFatherViewModel instead of doing it in all the children ViewModels? If I try to do that, it gives me the "Calling non-final function in constructor" warning and, of course, it doesn't work.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
init {
myInitMethod()
}
fun onTryAgainClick() {
myInitMethod()
}
}
Is it possible to call a non-final function in constructor?
Technically yes, but you shouldn't. Kotlin is trying to protect you from problems here. If you call an open function from a constructor, it means you are running code from the child class before the parent class is completely initialized, and before the child class even started initializing. If the child implementation of the open function tries to access properties from the child class, unexpected things may happen. For instance, non-nullable properties could yield null (because not initialized yet), or primitive values could yield their type's default instead of the default value from their initializer:
fun main() {
Child()
}
open class Parent {
init {
initialize()
}
val id = 42
open fun initialize() = println("parent init")
}
class Child : Parent() {
val name = "Bob"
override fun initialize() = println("initializing $name, parent id=$id")
}
This prints the following output:
initializing null, parent id=0
I guess you can see why this is dangerous.
Maybe you should reconsider what you're trying to do with this try-again feature. Maybe a new view model should be instantiated instead (if try-again is to handle crashes, the state of the current view model may actually be bad enough to want to re-create it from scratch anyway).
I'm working on a pet project where I'm trying to create a hybrid app using a WebView. The web platform that I run in the WebView sends events to the WebView/App through a #JavascriptInterface object. I can also command the web navigation by running a set of javascript functions against the web platform via the WebView using the evaluateJavascript(String, (String) -> Unit) function.
What I'm trying to achieve right now is that these commands that I execute through the evaluateJavascript(String, (String) -> Unit) function run sequentially. I might execute these commands from many different places at the same time, so I want them to run, wait for the callback from the evaluateJavascript() function to get called, and then execute the next command in the queue.
This is what I have in my custom WebView class:
val scriptQueue = mutableListOf<String>()
fun queueEvaluateJavascript(script: String) {
if (webViewIsLoading) {
scriptQueue.add(script)
} else {
scriptQueue.add(script)
runScriptQueue()
}
}
fun runScriptQueue() {
for (script in scriptQueue) {
evaluateJavascript(script, { })
}
scriptQueue.clear()
}
As you can see this is a super basic approach, and I don't really account for the evaluateJavascript() callback. Ideally, I'd like to find a way to flat map each of this evaluateJavascript() calls so we execute one after another, but waiting for the callback to go through.
With RxJava I think I'd create an Observable and then have the evaluateJavascript() callback trigger the subscriber's onNext(). Since, I'm using Kotlin Coroutines I wanted to do something with Coroutines, so I can queue these evaulateJavascript() calls. But I'm not 100% sure what would be the equivalent here.
That would be a nice problem to approach with coroutines.
The usual way to convert callback based APIs to suspend functions is the following:
suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
You can then use that in combination maybe with a Channel (to serve as a queue) and a coroutine that processes this channel:
class MyWebView(context: Context) : WebView(context) {
private val jsQueue = Channel<String>(BUFFERED)
fun startJsProcessingLoopIn(scope: CoroutineScope) {
scope.launch {
for (script in jsQueue) {
evaluateJs(script)
}
}
}
// you could also make this function non-suspend if necessary by calling
// sendBlocking (or trySend depending on coroutines version)
suspend fun queueEvaluateJavascript(script: String) {
jsQueue.send(script)
}
private suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
}
Alternatively you can create your own coroutine scope and make sure to tie it with some sort of lifecycle of your webview (I'm not familiar with WebView so I'll let you judge which kind of method is correct):
class MyWebView2(context: Context) : WebView(context) {
// you can even further customize the exact thread pool used here
// by providing a particular dispatcher
private val jsProcessingScope = CoroutineScope(CoroutineName("js-processing"))
private val jsQueue = Channel<String>(BUFFERED)
// this starts the loop right away but you can also put this in a method
// to start it at a more appropriate moment
init {
jsProcessingScope.launch {
for (script in jsQueue) {
evaluateJs(script)
}
}
}
// you could also make this function non-suspend if necessary by calling
// sendBlocking (or trySend depending on coroutines version)
suspend fun queueEvaluateJavascript(script: String) {
jsQueue.send(script)
}
private suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
fun someCloseOrDisposeCallback() {
jsProcessingScope.cancel()
}
}
I'm trying to deliver realtime updates to my view with Kotlin Flows and Firebase.
This is how I collect my realtime data from my ViewModel:
class MainViewModel(repo: IRepo): ViewModel() {
val fetchVersionCode = liveData(Dispatchers.IO) {
emit(Resource.Loading())
try {
repo.getVersionCode().collect {
emit(it)
}
} catch (e: Exception){
emit(Resource.Failure(e))
Log.e("ERROR:", e.message)
}
}
}
And this is how I emit each flow of data from my repo whenever a value changes in Firebase:
class RepoImpl: IRepo {
override suspend fun getVersionCodeRepo(): Flow<Resource<Int>> = flow {
FirebaseFirestore.getInstance()
.collection("params").document("app").addSnapshotListener { documentSnapshot, firebaseFirestoreException ->
val versionCode = documentSnapshot!!.getLong("version")
emit(Resource.Success(versionCode!!.toInt()))
}
}
The problem is that when I use:
emit(Resource.Success(versionCode!!.toInt()))
Android Studio highlights the emit invocation with:
Suspend function 'emit' should be called only from a coroutine or another suspend function
But I'm calling this code from a CoroutineScope in my ViewModel.
What's the problem here?
thanks
A Firestore snapshot listener is effectively an asynchronous callback that runs on another thread that has nothing to do with the coroutine threads managed by Kotlin. That's why you can't call emit() inside an asynchronous callback - the callback is simply not in a coroutine context, so it can't suspend like a coroutine.
What you're trying to do requires that you put your call to emit back into a coroutine context using whatever method you see fit (e.g. launch), or perhaps start a callbackFlow that lets you offer objects from other threads.
The suspend keyword on getVersionCodeRepo() does not apply to emit(Resource.Success(versionCode!!.toInt())) because it being called from within a lambda. Since you can't change addSnapshotListener you'll need to use a coroutine builder such as launch to invoke a suspend function.
When a lambda is passed to a function, the declaration of its corresponding function parameter governs whether it can call a suspend function without a coroutine builder. For example, here is a function that takes a no-arg function parameter:
fun f(g: () -> Unit)
If this function is called like so:
f {
// do something
}
everything within the curly braces is executed as though it is within a function that is declared as:
fun g() {
// do something
}
Since g is not declared with the suspend keyword, it cannot call a suspend function without using a coroutine builder.
However, if f() is declared thus:
fun f(g: suspend () -> Unit)
and is called like so:
f {
// do something
}
everything within the curly braces is executed as though it is within a function that is declared as:
suspend fun g() {
// do something
}
Since g is declared with the suspend keyword, it can call a suspend function without using a coroutine builder.
In the case of addEventListener the lambda is being called as though it is called within a function that is declared as:
public abstract void onEvent (T value, FirebaseFirestoreException error)
Since this function declaration does not have the suspend keyword (it can't, it is written in Java) then any lambda passed to it must use a coroutine builder to call a function declared with the suspend keyword.
I'm trying to create a Flow that needs to emit values from a callback but I can't call the emit function since the SAM is a normal function
Here's the class with the SAM from a library that I can't really modify it the way I need it to be.
class ValueClass {
fun registerListener(listener: Listener) {
...
}
interface Listener {
fun onNewValue(): String
}
}
And here's my take on creating the Flow object
class MyClass(private val valueClass: ValueClass) {
fun listenToValue = flow<String> {
valueClass.registerListener { value ->
emit(value) // Suspension functions can only be called on coroutine body
}
}
}
I guess it would've been simple if I could change the ValueClass but in this case, I can't. I've been wrapping my head around this and trying to look for implementations.
At least from what I know so far, one solution would be to use GlobalScope like this
class MyClass(private val valueClass: ValueClass) {
fun listenToValue = flow<String> {
valueClass.registerListener { value ->
GlobalScope.launch {
emit(value)
}
}
}
}
Now, this works but I don't want to use GlobalScope since I'll be using viewModelScope to tie it to my app's lifecycle.
Is there any way to work around this?
Thanks in advance. Any help would be greatly appreciated!
You can use callbackFlow to create a Flow from the callback. It will look something like:
fun listenToValue(): Flow<String> = callbackFlow {
valueClass.registerListener { value ->
trySend(value)
channel.close() // close channel if no more values are expected
}
awaitClose { /*unregister listener*/ }
}
Or if only one value is expected from the callback, you can use suspendCoroutine or suspendCancellableCoroutine. It this case listenToValue() function must be suspend and later called from a coroutine(e.g. someScope.launch):
suspend fun listenToValue(): String = suspendCoroutine { continuation ->
valueClass.registerListener { value ->
continuation.resumeWith(value)
}
}