why the coroutine exception handler double the original exception? - asynchronous

I implements my own async, I can't process the exception in a right way. why?
val expected = IllegalStateException();
val it = async<Any> {
throw expected;
};
assert.that({ it.get() }, throws(equalTo(expected)));
// ^--- but it throws a IllegalStateException(cause = expected)
Source Code
interface Response<in T> {
suspend fun yield(value: T);
}
interface Request<out T> {
fun get(): T;
fun <R> then(mapping: (T) -> R): Request<R>;
}
private val executor: ExecutorService = ForkJoinPool(20);
fun <T> async(block: suspend Response<T>.() -> Unit): Request<T> {
return object : Request<T>, Response<T> {
#Volatile var value: T? = null;
var request: Continuation<Unit>? = block.createCoroutine(this, delegate {}).let {
var task: Future<*>? = executor.submit { it.resume(Unit); };
return#let delegate {
try {
val current = task!!;
task = null;
current.get();
} catch(e: ExecutionException) {
throw e.cause ?: e;
}
};
};
override fun <R> then(mapping: (T) -> R): Request<R> = async<R> {
yield(mapping(get()));
};
override fun get(): T {
return value ?: wait();
}
private fun wait(): T {
val it = request!!;
request = null;
it.resume(Unit);
return value!!;
}
suspend override fun yield(value: T) {
this.value = value;
}
};
}
inline fun <T> delegate(noinline exceptional: (Throwable) -> Unit = { throw it; }, crossinline resume: (T) -> Unit): Continuation<T> {
return object : Continuation<T> {
override val context: CoroutineContext = EmptyCoroutineContext;
override fun resumeWithException(exception: Throwable) {
exceptional(exception);
}
override fun resume(value: T) {
resume(value);
}
}
}

the strange behavior is comes from java. the ForkJoinTask#getThrowableException will rethrow exception for the given task:
Returns a rethrowable exception for the given task, if
available. To provide accurate stack traces, if the exception
was not thrown by the current thread, we try to create a new
exception of the same type as the one thrown, but with the
recorded exception as its cause. If there is no such
constructor, we instead try to use a no-arg constructor,
followed by initCause, to the same effect. If none of these
apply, or any fail due to other exceptions, we return the
recorded exception, which is still correct, although it may
contain a misleading stack trace.
which implies that if you don't want to rethrow exception for the give task you can make the exception constructor non-publicly, for example:
val exception = object: IllegalStateException(){/**/};
// ^--- its constructor only available in its scope

Related

firestore, coroutine and flow

firebase method is working on worker thread automatically. but I have used coroutine and callbackflow to implement firebase listener code synchronously or get return from the listener.
below is my code that I explained
coroutine await with firebase for one shot
override suspend fun checkNickName(nickName: String): Results<Int> {
lateinit var result : Results<Int>
fireStore.collection("database")
.document("user")
.get()
.addOnCompleteListener { document ->
if (document.isSuccessful) {
val list = document.result.data?.get("nickNameList") as List<String>
if (list.contains(nickName))
result = Results.Exist(1)
else
result = Results.No(0)
//document.getResult().get("nickNameList")
}
else {
}
}.await()
return result
}
callbackflow with firebase listener
override fun getOwnUser(): Flow<UserEntity> = callbackFlow{
val document = fireStore.collection("database/user/userList/")
.document("test!!!!!")
val subscription = document.addSnapshotListener { snapshot,_ ->
if (snapshot!!.exists()) {
val ownUser = snapshot.toObject<UserEntity>()
if (ownUser != null) {
trySend(ownUser)
}
}
}
awaitClose { subscription.remove() }
}
so I really wonder these way is good or bad practice and its reason
Do not combine addOnCompleteListener with coroutines await(). There is no guarantee that the listener gets called before or after await(), so it is possible the code in the listener won't be called until after the whole suspend function returns. Also, one of the major reasons to use coroutines in the first place is to avoid using callbacks. So your first function should look like:
override suspend fun checkNickName(nickName: String): Results<Int> {
try {
val userList = fireStore.collection("database")
.document("user")
.get()
.await()
.get("nickNameList") as List<String>
return if (userList.contains(nickName)) Results.Exist(1) else Results.No(0)
} catch (e: Exception) {
// return a failure result here
}
}
Your use of callbackFlow looks fine, except you should add a buffer() call to the flow you're returning so you can specify how to handle backpressure. However, it's possible you will want to handle that downstream instead.
override fun getOwnUser(): Flow<UserEntity> = callbackFlow {
//...
}.buffer(/* Customize backpressure behavior here */)

Replace deprecated setTargetFragment() in PreferencesFragment

I cannot figure out how to replace setTargetFragment() in the code sample below, which is from my Preferencesfragment obviously:
override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat,
pref: Preference
): Boolean {
// Instantiate the new Fragment
val args = pref.extras
val fragment = supportFragmentManager.fragmentFactory.instantiate(
classLoader,
pref.fragment
).apply {
arguments = args
setTargetFragment(caller, 0) // <-- DEPRICATED CODE
}
// Replace the existing Fragment with the new Fragment
supportFragmentManager.beginTransaction()
.replace(R.id.settings, fragment)
.addToBackStack(null)
.commit()
title = pref.title
return true
}
Too many unknowns for my level of knowledge of Android Studio! This reference helps, but still confused:
How to replace setTargetFragment() now that it is deprecated
Well, apparently this works, but I'm not sure that I really understand what is going on:
override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat,
pref: Preference
): Boolean {
// Instantiate the new Fragment
val args = pref.extras
val fragment: Fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment)
fragment.arguments = args
supportFragmentManager.beginTransaction().replace(R.id.settings, fragment).addToBackStack(null).commit()
supportFragmentManager.setFragmentResultListener("requestKey", fragment) { _, _ -> }
return true
}

Kotlin Flow - Generic function for Retrofit with Result sealed class

I am recently working with Flow in my retrofit's repository.
Sealed class for Result
enum class ApiStatus{
SUCCESS,
ERROR,
LOADING
}
sealed class ApiResult <out T> (val status: ApiStatus, val data: T?, val message:String?) {
data class Success<out R>(val _data: R?): ApiResult<R>(
status = ApiStatus.SUCCESS,
data = _data,
message = null
)
data class Error(val exception: String): ApiResult<Nothing>(
status = ApiStatus.ERROR,
data = null,
message = exception
)
data class Loading<out R>(val _data: R?, val isLoading: Boolean): ApiResult<R>(
status = ApiStatus.LOADING,
data = _data,
message = null
)
}
Example repository call for 3 state - Loading, Error, Success
fun googleDisconnect() = flow {
emit(ApiResult.Loading(null, true))
val call = userDataSource.self("v4").googleDisconnect()
if(call.isSuccessful) {
emit(ApiResult.Success(call.body()))
} else {
emit(ApiResult.Error("Google Disconnect Failed"))
}
}
However, I have multiple network call with different function in my repository. Is there any idea to write a generic function for these flow so that these flow can be emitted to the flow builder?
My attempt but problem is How can I pass suspend function into the function?
Finally I got myself the answer. I wonder if this will helps but I will post out my answer.
fun <T> toResultFlow(call: suspend () -> Response<T>?) : Flow<ApiResult<T>?> {
return flow {
emit(ApiResult.Loading())
val c = call() <-- have to initialize the call method first
c?.let {
try{
if(c.isSuccessful) {
c.body()?.let {
emit(ApiResult.Success(it))
}
} else {
c.errorBody()?.let {
val error = it.string()
it.close()
emit(ApiResult.Error(error))
}
}
}catch (e: Exception) {
emit(ApiResult.Error(e.toString()))
}
}
}.flowOn(Dispatchers.IO)
}
Then, pass in your suspend function as lambda
fun googleDisconnect() = toResultFlow {
userDataSource.self("v4").googleDisconnect()
}
Finally, the toResultFlow will be return Flow<ApiResult> and T is your preferred datatype! Volla!

How can I get the name of a Kotlin property?

I have the following function to access a property's delegate. It uses Kotlin reflection to get a property's name and Java reflection to get the field.
fun Any.getDelegate<T>(prop: KProperty<T>): Any {
return javaClass.getDeclaredField("${prop.name}\$delegate").let {
it.setAccessible(true)
it.get(this)
}
}
The method is used like this:
val delegate = a.getDelegate(A::b)
However, I would prefer to use it like this:
val delegate = a.b.delegate
The problem with the code above is getting the property name of a.b and getting the instance a from a.b. From what I know about Kotlin, this is probably not possible, however I'd like to see if I can clean up my function at all.
To give a bigger picture of what I'm trying do here's my complete code. I want an observable delegate to which I can add and remove observers using the delegate reference and without creating addition variables.
fun Any.addObservable<T>(prop: KProperty<T>, observer: (T) -> Unit) {
getObservableProperty(prop).observers.add(observer)
}
fun Any.getObservableProperty<T>(prop: KProperty<T>): ObservableProperty<T> {
return getDelegate(prop) as ObservableProperty<T>
}
fun Any.getDelegate<T>(prop: KProperty<T>): Any {
return javaClass.getDeclaredField("${prop.name}\$delegate").let {
it.setAccessible(true)
it.get(this)
}
}
class ObservableProperty<T>(
initialValue: T,
initialObservers: Array<(T) -> Unit> = emptyArray()) : ReadWriteProperty<Any?, T> {
private var value = initialValue
public val observers: MutableSet<(T) -> Unit> = initialObservers.toHashSet()
public override fun get(thisRef: Any?, desc: PropertyMetadata): T {
return value
}
public override fun set(thisRef: Any?, desc: PropertyMetadata, value: T) {
this.value = value
observers.forEach { it(value) }
}
}
class A() {
var b by ObservableProperty(0)
}
fun main(args: Array<String>) {
val a = A()
a.addObservable(A::b) {
println("b is now $it")
}
a.b = 1
a.b = 2
a.b = 3
}
Edit:
I just realized that the function also isn't strict because the property delegate field name is referenced by KProperty name, which doesn't require a strong reference to the enclosing class. Here's an example to demonstrate the problem:
class A() {
var foo by ObservableProperty(0)
}
class B() {
var foo by ObservableProperty(0)
}
fun main(args: Array<String>) {
val a = A()
a.addObservable(B::foo) {
println("b is now $it")
}
a.foo = 1
a.foo = 2
a.foo = 3
}
This compiles and runs without error because A::foo and B::foo both result in a field string of "foo$delegate.
Right now reflection is all we can do to get to the delegate object. We are designing a language feature to have direct access to delegate instance, but it's long way to go.
This is how you get the name of a Kotlin Property (although only with an instance of the class). This part will be useful to anyone arriving at this question purely based off its title.
class Stuff(val thing: String)
val stuff = Stuff("cool stuff")
val thingFieldName = "${stuff.thing}\$delegate"
// value of thingFieldName is now "thing"
In terms of getting the delegate itself easier, they say you can now do this:
class Foo {
var bar: String by ReactiveProperty<String>()
}
val foo = Foo()
val bar = foo.bar
val barDelegate = ... // foo.bar$delegate
See ticket.

Dynamically implement interface in Groovy using invokeMethod

Groovy offers some really neat language features for dealing with and implementing Java interfaces, but I seem kind of stuck.
I want to dynamically implement an Interface on a Groovy class and intercept all method calls on that interface using GroovyInterceptable.invokeMethod. Here what I tried so far:
public interface TestInterface
{
public void doBla();
public String hello(String world);
}
import groovy.lang.GroovyInterceptable;
class GormInterfaceDispatcher implements GroovyInterceptable
{
def invokeMethod(String name, args) {
System.out.println ("Beginning $name with $args")
def metaMethod = metaClass.getMetaMethod(name, args)
def result = null
if(!metaMethod)
{
// Do something cool here with the method call
}
else
result = metaMethod.invoke(this, args)
System.out.println ("Completed $name")
return result
}
TestInterface getFromClosure()
{
// This works, but how do I get the method name from here?
// I find that even more elegant than using invokeMethod
return { Object[] args -> System.out.println "An unknown method called with $args" }.asType(TestInterface.class)
}
TestInterface getThisAsInterface()
{
// I'm using asType because I won't know the interfaces
// This returns null
return this.asType(TestInterface.class)
}
public static void main(String[] args)
{
def gid = new GormInterfaceDispatcher()
TestInterface ti = gid.getFromClosure()
assert ti != null
ti.doBla() // Works
TestInterface ti2 = gid.getThisAsInterface()
assert ti2 != null // Assertion failed
ti2.doBla()
}
}
Returning the Closure works fine, but I couldn't figure a way to find out the name of the method being called there.
Trying to make a Proxy to the this reference itself (so that method calls will call invokeMethod) returns null.
You could use the Map coercion feature of Groovy to dynamically generate a Map that represents the given interface:
TestInterface getMapAsInterface() {
def map = [:]
TestInterface.class.methods.each() { method ->
map."$method.name" = { Object[] args->
println "Called method ${method.name} with ${args}"
}
}
return map.asType(TestInterface.class)
}
To complete the response of Christoph, as stated by this page, you can implement an interface with a closure. For example:
def map = [doBla: { println 'Bla!'}, hello: {world -> "Hello $world".toString()}] as TestInterface
map.hello 'Groovy' // returns 'Hello Groovy'

Resources