How to get exception in init block kotlin - android

following the code, in init function I create a Person object,and have an exception,now I want to stop the progress in catch like java return. How can I do it?
class Person {
val age: String = "10"
private lateinit var person: Person
init {
try {
person = get(2)
} catch (exception: Throwable) {
}
println("----------------do it $person.age")
}
fun get(i: Int): Person {
when (i) {
1 -> {
return Person()
}
else -> {
throw MyException("aaaaaaaaa")
}
}
}
}

If an instance cannot be created due to errors in init, this error shouldn't be suppressed but delegated to the caller. So just do not catch the exception and the init "stopps" automatically.

Related

How do I check the value changed in unit test (Android)

`I am having a hard time getting the middle? value of a stateFlow and checking using assertTrue
Here is my code for testing
fun initData() = viewModelScope.launch {
modifiableUiState.emit(UiState.Loading)
try {
testSomeThing()
modifiableUiState.emit(UiState.Loaded)
} catch (e: Exception) {
modifiableUiState.emit(UiState.Error(e))
} finally {
modifiableUiState.emit(UiState.Idle)
}
}
and my test code
#Test
fun load_store_data_succeed() = runTest {
// given
var uiState: PaySettingBalanceNotificationViewModel.UiState? = null
val collectJob = launch {
viewModel.uiState.collect { uiState = it }
}
// when
viewModel.initMandatoryData()
runCurrent()
// then
assertTrue(uiState is PaySettingBalanceNotificationViewModel.UiState.InitialDataLoaded)
collectJob.cancel()
}
since in finally block it will emit Idle, I can't check if the value has changed to InitialDataLoaded. Is there a way I can check the history? of a value for testing?`

Mockito Test: verify static method was called inside object class

// writing junit ExerciseMainLogger class
so how to verify AnalyticsLog.insertEventLog(builder) using Mockito
I have mocked AnalyticsLog class but getting error Actually, there were zero interactions with this mock.
Wanted but not invoked
object ExerciseMainLogger {
fun setLog(eventName: String, screenId: String = "", dimension: Map<String, String> = mapOf()) {
LOG.d(TAG, "setLog - $eventName, $screenId, $dimension")
val builder = LogBuilders.EventBuilder()
.setEventName(eventName)
.setEventType(LogBuilders.EventType.NORMAL)
if (screenId.isNotEmpty()) {
builder.setScreenView(screenId)
}
if (dimension.isNotEmpty()) {
builder.setDimension(dimension)
}
AnalyticsLog.insertEventLog(builder)
}
}
AnalyticsLog Class
object AnalyticsLog {
#JvmStatic
fun insertEventLog(eventBuilder: EventBuilder) {
if (TestConfig.isTestMode()) {
LOG.d(TAG, "[SA] test mode")
return
}
try {
val eventLogs = eventBuilder.build()
val eventId = eventLogs[EVENT_ID_PROPERTY]
val result = insertLog(eventLogs)
if (FeatureManager.getInstance().getBooleanValue(FeatureList.Key.COMMON_DEVELOPER_MODE)) {
LOG.d(TAG, "[SA-DEV] insertEventLog: EventId: $eventId, logs: $eventLogs, send result: $result")
} else {
LOG.d(TAG, "[SA] insertEventLog: EventId: $eventId, send result: $result")
}
} catch (e: Exception) {
LOG.w(TAG, "[SA] insertEventLog exception, " + e.message)
e.printStackTrace()
}
}
}
My Test method looks like below. I trying to run the test case with the JUnit but getting error
mockkStatic(SamsungAnalyticsLog::class)
every { SamsungAnalyticsLog.insertEventLog(builder) } just runs
ExerciseMainLogger.setLog(
ExerciseMainLogger.EX2012,
screenId = screenId,
dimension = dimension
)
verify(exactly = 1) { AnalyticsLog.insertEventLog(builder) }
Verification failed: call 1 of 1: class com.samsung.android.wear.shealth.base.log.SamsungAnalyticsLog.insertEventLog(eq(com.samsung.context.sdk.samsunganalytics.LogBuilders$EventBuilder#71a04ac6))). Only one matching call to SamsungAnalyticsLog(static SamsungAnalyticsLog)/insertEventLog(EventBuilder) happened, but arguments are not matching:
[0]: argument: com.samsung.context.sdk.samsunganalytics.LogBuilders$EventBuilder#7b05129b, matcher: eq(com.samsung.context.sdk.samsunganalytics.LogBuilders$EventBuilder#71a04ac6), result: -
Stack trace:

How to launch a coroutine from within a class without using init?

I've got the following class:
class SomeApiRemoteSource (private var someApi: SomeApi)
{
private val someData = MutableLiveData<Map<String, String>>()
init
{
GlobalScope.launch(Dispatchers.IO)
{
try
{
val response = someApi.getSomeData(SomeApi.API_KEY).awaitResponse()
if(response.isSuccessful)
someData.postValue(response.body()?.data)
else
someData.postValue(emptyMap())
}
catch (e: Exception)
{
Log.e("ds error", e.message!!)
someData.postValue(emptyMap())
}
}
}
fun getSomeData(): MutableLiveData<Map<String, String>>
{
return someData
}
}
Everything works fine, but is there a way to get the same result without using the init? What if I need to run another API getter call? Should I just run another coroutine right below that 1st one? That seems inefficient.
You can create a function containing your coroutine and you can call it any number of times.
class SomeApiRemoteSource (private var someApi: SomeApi)
{
private val someData = MutableLiveData<Map<String, String>>()
private var apiJob: Job? = null
init
{
getDataFromAPI()
}
fun getDataFromAPI(){
// Cancel previous job if already running
apiJob?.cancel()
apiJob = GlobalScope.launch(Dispatchers.IO)
{
try
{
val response = someApi.getSomeData(SomeApi.API_KEY).awaitResponse()
if(response.isSuccessful)
someData.postValue(response.body()?.data)
else
someData.postValue(emptyMap())
}
catch (e: Exception)
{
Log.e("ds error", e.message!!)
someData.postValue(emptyMap())
}
}
}
fun getSomeData(): MutableLiveData<Map<String, String>>
{
return someData
}
}
I got it. Took some inspiration from previous answers.
class FixerRemoteSource (private var fixerApi: FixerApi)
{
suspend fun getAllSupportedSymbols(): Map<String, String>
{
val response = fixerApi.getAllSupportedSymbols(FixerApi.API_KEY)
return if(response.isSuccessful) response.body()?.symbols!! else emptyMap()
}
}
The problem that was blocking me was that the app kept crashing while I debugged the coroutine stuff. Removed the breaking points out of the coroutines and everything was fine.

How to cancel api request in view model in Mvvm?

I am try to cancel to api request if user calls api to fast then only the latest api should return the result all previous requests should be discarded but this isn't working anyone knows the solution please help thanks
class CartViewModel(val store: Account) : BaseViewModel() {
private var requestCalculation: Job? = null
fun recalculate() {
requestCalculation.let {
if (it != null) {
if (it.isActive) {
requestCalculation!!.cancel()
}
}
}
requestCalculation = viewModelScope.launch(Dispatchers.IO) {
isLoading.postValue(true)
try {
val order = CCOrderManager.shared.calculateTaxesAndApplyRewards(store.id)
refreshOrder()
} catch (e: Exception) {
exception.postValue(e.localizedMessage ?: e.toString())
}
}
}
}
The order of cancellation and execution is wrong. When the function starts, requestCalculation is null, so it cannot be canceled. Make sure you start first the coroutine and cancel it later. For example:
private var requestCalculation: Job? = null
fun recalculate() {
requestCalculation = viewModelScope.launch(Dispatchers.IO) {
delay(10_000)
// do your work...
}
// now the job can be canceled
requestCalculation?.cancel()
}
Adding a check after api call this.isActive {return#launch} finally worked for me...
fun recalculate() {
calculationRequest?.cancel()
isLoading.postValue(true)
calculationRequest = viewModelScope.launch(Dispatchers.IO) {
try {
val order =
CCOrderManager.shared.calculateTaxesAndApplyRewards(store.id)
// this check is the solution *******
if (!this.isActive) {return#launch}
val catalog = CatalogManager.shared().catalog
} catch (e: Exception) {
}
}
}

Kotlin equivalent of Swift's defer keyword

Is there anything similar in Kotlin that provides same ability as the Swift keyword 'defer' ?
What the defer key word does is, it ensure that the code inside a defer block get executed before returning from a function.
Below is an example imagining that defer keyword existed in Kotlin.
class MyClass {
var timeStamp = 0L
fun isEdible(fruit: Fruit): Boolean {
defer {
timeStamp = System.currentTimeMillis()
}
if (fruit.isExpired) {
return false
}
if (fruit.isRipe) {
return true
}
return false
}
}
In the case above, regardless of at what point the function returns, the block inside defer will get executed and timestamp's value will get updated, just before the function ends.
I know Java there is the finally {} keyword used along with try{} catch{}, but it's is not exactly what defer offers.
There's no such keyword in Kotlin, but you can make a construct yourself that will work quite similarly. Something like this (note that this does not handle exceptions in the deferred blocks):
class Deferrable {
private val actions: MutableList<() -> Unit> = mutableListOf()
fun defer(f: () -> Unit) {
actions.add(f)
}
fun execute() {
actions.forEach { it() }
}
}
fun <T> defer(f: (Deferrable) -> T): T {
val deferrable = Deferrable()
try {
return f(deferrable)
} finally {
deferrable.execute()
}
}
Then you can use it like this:
class MyClass {
var timeStamp = 0L
fun isEdible(fruit: Fruit): Boolean = defer { d ->
d.defer {
timeStamp = System.currentTimeMillis()
}
if (fruit.isExpired) {
return false
}
if (fruit.isRipe) {
return true
}
return false
}
}
The closest equivalent is try/finally. catch is not necessary if there's no exceptions thrown.
try {
println("do something")
// ... the rest of your method body here
}
finally {
println("Don't forget about me!");
}
In Swift, defer is usually used to ensure you don't forget to clean up some kind of resource or another (file handle, database connection, shared memory map, etc.). For this purpose, Kotlin use with, which takes a closure, to which the resource is passed as an argument. The resource is valid for the lifetime of the closure, and is automatically closed at the end.
FileWriter("test.txt")
.use { it.write("something") }
// File is closed by now
Solution with exception handling:
class DeferContext {
private val list = mutableListOf<() -> Unit>()
fun defer(payload: () -> Unit) {
list += payload
}
/** lombok `#Cleanup` analog */
fun AutoCloseable.deferClose() = apply {
defer { close() }
}
fun executeDeferred(blockError: Throwable?) {
var error: Throwable? = blockError
for (element in list.reversed()) {
try {
element()
} catch (e: Throwable) {
if (error == null) {
error = e
} else {
error.addSuppressed(e)
}
}
}
error?.let { throw it }
}
}
inline fun <T> deferBlock(payload: DeferContext.() -> T): T {
val context = DeferContext()
var error: Throwable? = null
var result: T? = null
try {
result = context.payload()
} catch (e: Throwable) {
error = e
} finally {
context.executeDeferred(error)
}
return result as T
}
IMHO, main point of defer functionality is execution of deferred actions regardless of previously thrown exceptions.
usage:
deferBlock {
defer { println("block exited") }
val stream = FileInputStream("/tmp/a").deferClose()
}
I came across the same question today.
While I think the answer provided by marstran is good, I decided to refactor it a little bit.
fun <T> deferred(f: ((() -> Unit) -> Unit) -> T): T {
val actions: MutableList<() -> Unit> = mutableListOf()
try {
return f(actions::add)
} finally {
actions.asReversed().forEach { it() }
}
}
I got rid of the Deferrable class by using the list directly in the deffered function. This also solves the fact that the whole Deferrable object was passed to the calling code needing to call it.defer/d.defer. In this version the add method of the mutable list is directly passed into the lambda allowing to have a code that is closer to its go/swift version.
To address the suggestion given by mvndaai to use Stack I decided to call .asReversed() on the list. Maybe there is a LI-FO type in kotlin that is also available in non JVM variants, but if not I think this is a good solution.
the given sample would look like:
class MyClass {
var timeStamp = 0L
fun isEdible(fruit: Fruit): Boolean = deferred { defer ->
defer {
timeStamp = System.currentTimeMillis()
}
if (fruit.isExpired) {
return false
}
if (fruit.isRipe) {
return true
}
return false
}
}
If the class is Closeable you can use use block:
class MyClass : Closeable {
var timeStamp = 0L
override fun close() {
timeStamp = System.currentTimeMillis()
}
fun test(): Boolean {
this.use {
if (fruit.isExpired) {
return false
}
if (fruit.isRipe) {
return true
}
return false
}
}
}

Categories

Resources