Kotlin Object OnComplete listener - kotlin

I need to know when my object is finally setup (gets data from internet) so I can properly use it without any nullpointerexceptions. Is there a way I can make an object notify when it's all setup, just like onComplete lambdas as function parameters. Simple question but anything helps :)

You can create a function type variable in your class. Set the function in your activity. Call it when your object is ready.
class MyObject()
{
var onComplete : (()-> Unit)? = null
fun setup()
{
//setup your object
onComplete?.invoke()
}
}
In your activity
val myObject = MyObject()
myObject.onComplete = {
//..things to do after setup...
}
myObject.setup()
You can also put it in the constructor if you want.

In android, we can use LiveData if you added support library dependency.
class Result
object MyObject {
val onComplete: MutableLiveData<Result> = MutableLiveData()
fun setUp() {
// do something
onComplete.setValue(Result()) // main thread
// onComplete.postValue(Result()) // other thread
}
}
class SampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MyObject.onComplete.observe(this, Observer { result ->
// do something with result
})
MyObject.setUp()
}
}
If this method is unfamiliar, check out observer pattern or LiveData.

Related

Using Coroutines with Third party library that's using callback handlers

Here is a breakdown of how the current third party SDK implementation works.
class Handler(val context: Context) {
val device = Controller.getInstance(context,Listener())
fun connectBT(BTDevice:BluetoothDevice){
device.connectBT(BTDevice)
}
}
and then the Listener implementation
class Listener: BBDeviceController.BBDeviceControllerListener{
override fun onBTConnected(device: BluetoothDevice?) {
println("Device Connected")
// Send back to function that device is connect
}
}
This is a straightforward example, but the idea is, when you press a button it will call connectBT() and then contain the result like so:
val handler = Handler(this)
val res = handler.connectBT(btDevice)
I know you can use suspendCoroutine on the function handler.connectBT() however the issue is how do I get the listeners result from the SDK to return back to the main function that called it?
When using suspendCoroutine, you need to call resume/resumeWithException/etc on the continuation object. You can store/pass this object anywhere, for example to your listener:
class Handler(val context: Context) {
val listener = Listener()
val device = Controller.getInstance(context, listener)
suspend fun connectBT(BTDevice:BluetoothDevice){
suspendCoroutine<Unit> { continuation ->
listener.continuation = continuation
device.connectBT(BTDevice)
}
}
}
class Listener: BBDeviceController.BBDeviceControllerListener{
var continuation: Continuation<Unit>? = null
override fun onBTConnected(device: BluetoothDevice?) {
println("Device Connected")
if (continuation != null) {
continuation?.resume(Unit)
continuation = null
}
}
}

How to mock a ProducerScope from callbackFlow builder in Kotlin Flow?

I'd like to test a function where I use the scope of a callbackFlow builder. Assuming I have a function inside the flow builder like this:
fun items(): Flow<Items> = callbackFlow {
getItems(this) {
trySend(it)
}
awaitClose()
}
In getItems function, I received data from websockets. The scope of ProducerScope is used to either launch a new coroutine with a delay and do something or to close the scope if an error happens. So it might call scope.launch { } or scope.close().
For example, this could do something as follows:
fun getItems(scope: ProducerScope<Items>, callback: (Items) -> Unit) {
if (something) {
scope.launch { ... }
}
if (somethingElse) {
...
scope.close(error)
}
...
callback(items)
}
The callbackFlow's block uses a ProducerScope, extension of CoroutineScope and SendChannel, I tried to mock it using Mockk:
val scope: ProducerScope<Items> = mockk()
Unfortunately, I end up with:
java.lang.ClassCastException: class kotlin.coroutines.CoroutineContext$Element$Subclass6 cannot be cast to class kotlin.coroutines.ContinuationInterceptor
How can I mock a ProducerScope?
How do I unit test getItems above when scope can be either a CoroutineScope and a SendChannel?
Thanks in advance.
After many tries, I cannot do this easily without expecting strange behaviors. So I refactored my function to use a Channel and a CoroutineScope separately. Thanks to the CoroutineScope plus extension, I can create a new scope from the flow builder. This is now testable!
Therefore, the flow builder became:
fun items(): Flow<Items> = callbackFlow {
val channel = this.channel
val scope = this.plus(this.coroutineContext)
getItems(channel, scope) {
...
}
...
}
My function still uses both but gets them separately:
fun getItems(
channel: SendChannel<Items>,
scope: CoroutineScope,
callback: (Items) -> Unit
) {
if (something) {
scope.launch { ... } // <-- use scope
}
if (somethingElse) {
...
channel.close(error) // <-- use channel
}
...
callback(items)
}
Then, I can now test using a Channel with the same requirements than the one in callbackFlow and the scope from runTest:
#Test
fun `get items and succeed`() = runTest {
val channel = Channel<Any>(Channel.BUFFERED, BufferOverflow.SUSPEND)
...
service.getItems(channel, this#runTest, callback)
...
}

What's callback in Kotlin?

I'm learning kotlin in intelij Idea, and I have to make presentation about interfaces. One subject is callback, where can I find information about it? or can you tell me simply, veery simply, what's call back?
fun main() {
val myphone = Myphone()
myphone.phoneOn()
myphone.onClick()
myphone.onTouch()
myphone.openApp()
myphone.closeApp()
}
interface Application {
var AppName: String
fun openApp()
fun closeApp() {
println("$AppName App is closed!")
}
}
interface Button {
var helloMessage: String
fun phoneOn()
fun onClick()
fun onTouch() {
println("The screen was touched!")
}
}
class Myphone: Button, Application {
override var AppName: String = "Facebook"
override fun openApp() {
println("$AppName Is Open!")
}
override var helloMessage: String = "Hello"
override fun onClick() {
println("The screen was clicked!")
}
override fun phoneOn() {
println("$helloMessage !")
}
}
VERY simply: callback means the function, that is executed on the other function's finish or some specific event happening.
fun execute() {
// Some logic
executeAnotherOnFinish();
}
OR
// filter executes only after array converted to list
myIntArray.toList().filter { it > 0 }
OR
myListener.notify()
// Listener class methid
notify() {
// Do some work
executeCallback()
}
Callback is not just Kotlin related, its very common programming technique which is primarily used with asynchronous programming. The simplest explanation is that it is function that will be called back (hence the name) once some asynchronous event has occurred.
Button's onClick function is quite good example of that, we have some logic that we need to execute but we want it to run only when button is clicked so we provide callback which will be called once that button is clicked.

return value only if Observer method called

I'm trying to achieve to return value of method only if Observer method called. But didn't know the right way. I use let but it's required unit and i have to return MutableList<Pair<String,String>>.
That's my method:
override fun getPlaylistsNameAndId(userCategory: String):MutableList<Pair<String,String>> {
val abc = mutableListOf<Pair<String,String>>()
addPlaylistViewModel.getPlaylistsForChips(userCategory).observe(this, Observer { it ->
it.forEach {
abc.add(Pair(it.playlistName,it.playlistId))
}
//i'm called
})
// return if (observer called) else wait for calling.
}
Remember that observe is an async operation. So your method will return immediately after setting up the observer. The observer will only execute later - asynchronously.
I'm doing this in my application: (Pseudo code)
val myData: List<MyData> = arrayListOf()
val myLiveData: LiveData<List<MyData>> by lazy { MyRoomDatabase.getInstance(this).myDataDao().myDataLive }
override fun onCreate() {
super.onCreate()
myLiveData.observe(this, Observer { data ->
myData = data
}
}
Essentially, whenever the live data updates, it'll also update the myData property.
So any time we access the myData property, it should be up to date.

Hiding base class constructor parameters in Kotlin

I am trying to understand how to hide a base constructor parameter in a subclass in kotlin. How do you put a facade over a base constructor? This doesn't work:
import com.android.volley.Request
import com.android.volley.Response
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private fun hiddenListener() = Response.ErrorListener {
/* super secret listener */
}
...
}
I think I understand the problem:
During construction of a new instance of a derived class, the base
class initialization is done as the first step (preceded only by
evaluation of the arguments for the base class constructor) and thus
happens before the initialization logic of the derived class is run.
I'm trying to solve this problem for Volley, where I need my custom request to be be a Request so that it can be passed into a RequestQueue. It would be easier of RequestQueue took in some kind of interface but since it doesn't I have to subclass. There are other ways I can hide these complexities from the caller, but this limitation has come up for me other times in Kotlin and I'm not sure how to solve it.
I am not familiar with volley but I tried to come up with an example that should give you some insight how to solve your problem. What you can do is use a companion object:
interface MyListener {
fun handleEvent()
}
open class Base<T>(anything: Any, val listener: MyListener) { // this would be your Request class
fun onSomeEvent() {
listener.handleEvent()
}
}
class Derived(anything: Any) : Base<Any>(anything, hiddenListener) { // this would be your MyCustomRequest class
private companion object {
private val hiddenListener = object : MyListener {
override fun handleEvent() {
// do secret stuff here
}
}
}
}
So if you apply this to your problem, the result should look something like this:
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private companion object {
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
}
...
}
A different way would be to use a decorator, create your Request withing that decorator and just delegate the calls to it:
class Decorator(anything: Any) {
private var inner: Base<Any>
private val hiddenListener: MyListener = object : MyListener {
override fun handleEvent() { }
}
init {
inner = Base(anything, hiddenListener)
}
}
And once again for your example that would look like this:
class MyCustomRequest(url: String) {
private var inner: Request<String>
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
init {
inner = Request<String>(Request.Method.POST, url, hiddenListener)
}
...
}