Kotlin syntax for LiveData observer? - android

I have the following bit of code in my HomeActivity to use LiveData.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Show the launch splash screen.
//
this.setContentView(R.layout.activity_home)
this.viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
this.viewModel.getUser().observe(this, Observer { user: User? ->
});
}
While this seems to work, what does the following part mean?
Observer { user: User? ->
}
This must result in an object that conforms to the Observer interface which has
void onChanged (T t)
https://developer.android.com/reference/android/arch/lifecycle/Observer.html
How does
Observer { user: User? ->
}
result in an object with an onChanged method?
I don't know what putting the name of an interface in front of a lambda expression means.
Thanks!

This is called SAM Conversion, a concept that helps interacting with Java Single Abstract Method Interfaces like in your example.
The following creates an implementation of Runnable, where the single abstract method is run():
val runnable = Runnable { println("This runs in a runnable") }
It’s described in the docs: https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
Alternatively, but more verbose, would be to use an object:
val runnable2 = object : Runnable {
override fun run() {
println("This runs in a runnable")
}
}
Both are examples of anonymous implementations of that interface. It's of course also possible to create a concrete subclass and instantiate it then.
class MyRunnable : Runnable {
override fun run() {
println("This runs in a runnable")
}
}
val runnable3 = MyRunnable()

in Kotlin the Observer { } lambda gives you param it, you can rename it as you want and use. by default data will be available with it.something() etc...
JAVA:
... new Observer {
void onChanged(User user){
user.something()
}
}
KOTLIN
... object : Observer<User> {
fun onChanged(user: User){
user.something()
}
}
OR
... Observer {
it.something()
}
you can rename it to whatever you want like
... Observer { myUser ->
myUser.something()
}

To omit the Observer { ... } part just add import androidx.lifecycle.observe and use it like this:
this.viewModel.user.observe(this) { user: User? ->
// ...
}

Related

How to convert a object expression to lambda in Kotlin? [duplicate]

This question already has answers here:
Passing lambda instead of interface
(5 answers)
Closed 1 year ago.
I am new to Android development. Recently I am learning Kotlin and I am trying to figure out setOnClickListener. However, I encountered a problem in the process of converting object expression to lambda using Kotlin.
step 1. setOnClickListener in Java:
buttonLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// my code
}
});
step 2. then I convert Java code to Kotlin code using object expression:
buttonLogin.setOnClickListener(object : View.OnClickListener {
override fun onClick(p0: View?) {
// my code
}
})
step 3. then IntelliJ prompts me to convert object expression to lambda:
buttonLogin.setOnClickListener {
// my code
}
It looks more concise, however, I can't understand the logic behind step 3.
So I checked some information online, it says
Any function that receives an interface with a single function can be
substituted by a lambda
It does on setOnClickListener.
But I still can't fully understand, so I defined an interface and class to verify it.
Here is my code:
interface MyInterface {
fun method1()
}
class MyClass {
fun method2(myInterface: MyInterface) {
myInterface.method1()
}
}
fun main() {
val myClass = MyClass()
myClass.method2(object : MyInterface {
override fun method1() {
println("Hello, world.")
}
})
// So how to write the lambda to replace object expression?
}
The code in step 3 is called trailing lambdas
According to Kotlin convention, if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses,
If the lambda is the only argument in that call, the parentheses can be omitted entirely:
For example:
fun function(f : (String) -> Unit) {
}
fun main() {
function {
}
}
Check the Kotlin Documentation for this feature
https://kotlinlang.org/docs/lambdas.html#passing-trailing-lambdas
You can convert your code to use this feature
class MyClass {
fun method2(function: () -> Unit) {
function()
}
}
fun main() {
val myClass = MyClass()
myClass.method2 {
println("Hello, world.")
}
// Or you can store the lambda in variable and use it like tihs
val myClass2 = MyClass()
val function = {
println("Hello, world.")
}
myClass2.method2(function)
}
Or just add convert your interface to a functional interface
fun interface MyInterface {
fun method1()
}
class MyClass {
fun method2(myInterface: MyInterface) {
myInterface.method1()
}
}
fun main() {
val myClass = MyClass()
myClass.method2 {
println("Hello, world.")
}
}

Kotlin Flow: callbackFlow with lazy initializer of callback object

I want to use reactive paradigm using Kotlin Flow in my Android project. I have an external callback-based API so my choice is using callbackFlow in my Repository class.
I've already read insightfully some proper docs with no help:
callbackFlow documentation
Callbacks and Kotlin Flows by Roman Elizarov
What I want to achieve:
Currently my Repository class looks like this (simplified code):
lateinit var callback: ApiCallback
fun someFlow() = callbackFlow<SomeModel> {
callback = object : ApiCallback {
override fun someApiMethod() {
offer(SomeModel())
}
}
awaitClose { Log.d("Suspending flow until methods aren't invoked") }
}
suspend fun someUnfortunateCallbackDependentCall() {
externalApiClient.externalMethod(callback)
}
Problem occurs when someUnfortunateCallbackDependentCall is invoked faster than collecting someFlow().
For now to avoid UninitializedPropertyAccessException I added some delays in my coroutines before invoking someUnfortunateCallbackDependentCall but it is kind of hack/code smell for me.
My first idea was to use by lazy instead of lateinit var as this is what I want - lazy initialization of callback object. However, I couldn't manage to code it altogether. I want to emit/offer/send some data from someApiMethod to make a data flow but going outside of callbackFlow would require ProducerScope that is in it. And on the other hand, someUnfortunateCallbackDependentCall is not Kotlin Flow-based at all (could be suspended using Coroutines API at best).
Is it possible to do? Maybe using some others Kotlin delegates? Any help would be appreciated.
To answer your question technically, you can of course intialise a callback lazyily or with lateinit, but you can't do this AND share the coroutine scope (one for the Flow and one for the suspend function) at the same time - you need to build some kind of synchronisation yourself.
Below I've made some assumptions about what you are trying to achieve, perhaps they are not perfect for you, but hopefully give some incite into how to improve.
Since it is a Repository that you are creating, I will first assume that you are looking to store SomeModel and allow the rest of your app to observe changes to it. If so, the easiest way to do this is with a MutableStateFlow property instead of a callbackFlow:
interface Repository {
val state: Flow<SomeModel>
suspend fun reload()
}
class RepositoryImpl(private val service: ApiService) : Repository {
override val state = MutableStateFlow(SomeModel())
override suspend fun reload() {
return suspendCoroutine { continuation ->
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
state.value = data
if (continuation.context.isActive)
continuation.resume(Unit)
}
})
}
}
}
interface ApiCallback {
fun someApiMethod(data: SomeModel)
}
data class SomeModel(val data: String = "")
interface ApiService {
fun callBackend(callback: ApiCallback)
}
The downside to this solution is that you have to call reload() in order to actually make a call to your backend, collecting the Flow alone is not enough.
myrepository.state.collect {}
myrepository.reload()
Another solution, again depending on what exactly you are trying to achieve, is to provide two ways to call your backend:
interface Repository {
fun someFlow(): Flow<SomeModel>
suspend fun reload(): SomeModel
}
class RepositoryImpl(private val service: ApiService) : Repository {
override fun someFlow() = callbackFlow<SomeModel> {
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
offer(data)
}
})
awaitClose {
Log.d("TAG", "Callback Flow is closed")
}
}
override suspend fun reload(): SomeModel {
return suspendCoroutine<SomeModel> { continuation ->
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
if (continuation.context.isActive)
continuation.resume(data)
}
})
}
}
}
interface ApiCallback {
fun someApiMethod(data: SomeModel)
}
data class SomeModel(val data: String = "")
interface ApiService {
fun callBackend(callback: ApiCallback)
}
Now you can either call reload() or someFlow() to retrieve SomeModel() and the Repository holds no "state".
Note that the reload() function is simply a 'coroutine' version of the callbackFlow idea.

How to mock android room withTransaction method with Mockk

I'm trying to make some unit tests for my business logic.
I have repository in which I save to room database (2.1.0-rc01) some data from response.
Data saving into different tables with different dao in single transaction.
Code is simplified:
ItemRepository
suspend fun saveItems(response: Response) {
val items = response.items.map { it.toLocalItem() }
val subItems = response.items.flatMap { item ->
item.subItems.map { it.toLocal(item.id) }
}
db.withTransaction {
db.itemDao().deleteAll()
db.itemDao().insertAll(items)
db.subItemDao().insertAll(subItems)
}
}
For unit test I'm using Mockk library. How can I mock room withTransaction method?. withTransaction is declared as
suspend fun <R> RoomDatabase.withTransaction(block: suspend () -> R): R
I'm trying to writing test
#MockK
private lateinit var database: AppDatabase
#MockK
private lateinit var itemDao: ItemDao
#MockK
private lateinit var subItemDao: SubItemDao
#Test
fun checkSaveItems() = runBlocking {
repository = ItemRepository(database)
coEvery { database.itemDao() } returns itemDao
coEvery { database.subItemDao() } returns subItemDao
//TODO: execute database.withTransaction(block: suspend () -> R)
coEvery { itemDao.deleteAll() } just Runs
coEvery { itemDao.insertAll(any()) } just Runs
coEvery { subItemDao.insertAll(any()) } just Runs
repository.saveItems(testResponse)
coVerifySequence {
itemDao.deleteAll()
itemDao.insertAll(testItems)
subItemDao.insertAll(testSubItems)
}
}
You first have to enable static mocks for the Android Room KTX method withTransaction {}. You also need to capture the suspend lambda function passed to it. This captured function can just be invoked so the code inside it runs. Since you're mocking all the database calls, you don't need a real transaction here.
#Before
fun initMocks() {
MockKAnnotations.init(this)
mockkStatic(
"androidx.room.RoomDatabaseKt"
)
val transactionLambda = slot<suspend () -> R>()
coEvery { db.withTransaction(capture(transactionLambda)) } coAnswers {
transactionLambda.captured.invoke()
}
}
You should then be able to run your code as written.
To expand on Andrew's answer, the mockk documentation for extension functions shows that if you are mocking an object wide or class wide extension function, you can just use regular mockk to achieve that. However, if you are using a module wide extension function, like withTransaction, you also need to perform mockkStatic on the module's class name.

How to observe ViewModel that makes a request for a call back

I am trying to make a request to a library that gives me a call back.
Manager.getInstance().request(new CallBack())
I want to put this in a ViewModel so that I can observe it from the Activity.
class RequestViewModel : ViewModel, CallBack {
fun request() {
Manager.getInstance().request(this)
}
override fun onFinished(result : List<String>?) {
}
override fun onFailed() {
}
}
How can I make it so that I can observe when this has finished? I know I could make my Activity implement this CallBack, but I don't want to couple Activity to this.
Ideally this would be a LiveData or Observable.
If I understand the question correctly, you can submit the data acquired in onFinished method to the LiveData instance that should be observed by a view component, e.g.
class RequestViewModel : ViewModel, CallBack {
private val _liveData = MutableLiveData<SomeResult<List<String>>>
val liveData: LiveData<SomeResult<List<String>>> get() = _liveData
fun request() {
Manager.getInstance().request(this)
}
override fun onFinished(result : List<String>?) {
if (result != null) {
_liveData.postValue(SomeResult.success(result))
} else {
_liveData.postValue(SomeResult.failure())
}
}
override fun onFailed() {
_liveData.postValue(SomeResult.failure())
}
}
And somewhere in your object that corresponds to a view component:
viewModel.liveData.observe(lifecycleOwner, Observer<List<String>> {
handleResponse(it)
})
whereas lifecycleOwner typically is your AppCompatActivity or android.support.v4.Fragment inheritor.
I would advise you to decouple requesting from ViewModel and create a class called Repository to handle all the requests. In this class you could have a MutableLiveData object which can be observed and whenever new requested data is retrieved, use mutableLiveData.postValue(retrievedData) for MutableLiveData which notifies the observes about the new changes.
To read more about repository, you can follow these links:
Google's Guide to App Architecture
Codelab tutorial with Repository pattern

Pass interface as parameter in Kotlin

I want to pass an interface as parameter like this:
class Test {
fun main() {
test({})
// how can I pass here?
}
fun test(handler: Handler) {
// do something
}
interface Handler {
fun onCompleted()
}
}
In Java, I can use anonymous function like test(new Handler() { .......... }), but I can't do this in Kotlin. Anyone know how to do this?
In Kotlin you can do :
test(object: Handler {
override fun onComplete() {
}
})
Or make a property the same way:
val handler = object: Handler {
override fun onComplete() {
}
}
And, somewhere in code:
test(handler)
since your interface has only one function. you can convert it to SAM like this
fun interface Handler {
fun onCompleted()
}
then you can just implement this interface using lambda instead and so reduce the overall written code. this is only possible in v1.4
Attached is an example of how to pass an object by parameter that represents the value of the data type and invoke behaviors using interface inheritance.
fun main() {
val customClass = CustomClass(
object : First {
override fun first() {
super.first()
println("first new impl")
}
override fun second() {
super.second()
println("second new impl")
}
}
)
customClass.first.first()
customClass.first.second()
}
data class CustomClass(val first: First)
interface First: Second {
fun first() {
println("first default impl")
}
}
interface Second {
fun second() {
println("second default impl")
}
}
It is worth mentioning that with super.first() or super.second() the default behavior of the interface is being invoked.
It doesn't make much sense to pass a lamda with an anonymous object as a parameter, lambda: () -> Unit , if what we need is to invoke the functions.
GL
Playground

Categories

Resources