I am using Fuel to send and receive requests from an API.
However, I am not able to show Toast Messages or AlertDialogs, if the request was not successful.
Sample Code:
private fun validatePassword(): Boolean {
var minPasswordLength = 0
val password = findViewById<EditText>(R.id.input_password_register).text.toString()
val password_repeat: String = findViewById<EditText>(R.id.input_password_repeat_register).text.toString()
"/auth/password.json".httpGet().responseString { request, response, result ->
//do something with response
request.header(mapOf("Content-Type" to "application/json"))
Log.println(Log.ASSERT, "password_Curl", request.cUrlString())
when (result) {
is Result.Failure -> {
val data = response.data.toString(Charsets.UTF_8)
Log.println(Log.ASSERT, "Response_Password_Fail", data)
val jelement = JsonParser().parse(data)
val jobject = jelement.asJsonObject
Toast.makeText(this, jobject.get("Error").asString, Toast.LENGTH_SHORT).show()
}
is Result.Success -> {
val data = response.data.toString(Charsets.UTF_8)
Log.println(Log.ASSERT, "Response_Passwd_Succes", data)
val jelement = JsonParser().parse(data)
val jobject = jelement.asJsonObject
minPasswordLength = jobject.get("minimal_length").asInt
}
}
}
return password.length >= minPasswordLength && password.equals(password_repeat)
}
I have tried to run the makeText command on the UIThread using:
runOnUiThread{Toast.makeText(this, jobject.get("Error").asString, Toast.LENGTH_SHORT).show()}
This did not work either.
I do however get to the code while debugging:
Thanks for the help! Cheers.
UPDATE:
While debugging I noticed that minPasswordLength stays 0 even though "minimal_length" from the API has the value 8. So maybe it is a Threading problem?
Use the implementation 'com.github.kittinunf.fuel:fuel-android:1.15.0 instead of implementation 'com.github.kittinunf.fuel:fuel:1.15.0' in the dependencies (build.gradle file).
The JVM implementation of the library doesn't automatically invoke the handlers in the UI thread.
This works for me even in the JVM implementation:
runOnUiThread { Toast.makeText(this, "Some text", Toast.LENGTH_SHORT).show() }
I think that it didn't work for you because the JSON doesn't contain the "Error" but "details" field.
Related
In all cases that I have been using corrutines, so far, it has been executing its "lines" synchronously, so that I have been able to use the result of a variable in the next line of code.
I have the ImageRepository class that calls the server, gets a list of images, and once obtained, creates a json with the images and related information.
class ImageRepository {
val API_IMAGES = "https://api.MY_API_IMAGES"
suspend fun fetch (activity: AppCompatActivity) {
activity.lifecycleScope.launch() {
val imagesResponse = withContext(Dispatchers.IO) {
getRequest(API_IMAGES)
}
if (imagesResponse != null) {
val jsonWithImagesAndInfo = composeJsonWithImagesAndInfo(imagesResponse)
} else {
// TODO Warning to user
Log.e(TAG, "Error: Get request returned no response")
}
...// All the rest of code
}
}
}
Well, the suspend function executes correctly synchronously, it first makes the call to the server in the getRequest and, when there is response, then composes the JSON. So far, so good.
And this is the call to the "ImageRepository" suspension function from my main activity:
lifecycleScope.launch {
val result = withContext(Dispatchers.IO) { neoRepository.fetch(this#MainActivity) }
Log.i(TAG, "After suspend fun")
}
The problem is that, as soon as it is executed, it calls the suspension function and then displays the log, obviously empty. It doesn't wait for the suspension function to finish and then display the log.
Why? What am I doing wrong?
I have tried the different Dispatchers, etc, but without success.
I appreciate any help.
Thanks and best regards.
It’s because you are launching another coroutine in parallel from inside your suspend function. Instead of launching another coroutine there, call the contents of that launch directly in your suspend function.
A suspend function is just like a regular function, it executes one instruction after another. The only difference is that it can be suspended, meaning the runtime environment can decide to halt / suspend execution to do other work and then resume execution later.
This is true unless you start an asynchronous operation which you should not be doing. Your fetch operation should look like:
class ImageRepository {
suspend fun fetch () {
val imagesResponse = getRequest(API_IMAGES)
if (imagesResponse != null) {
val jsonWithImagesAndInfo = composeJsonWithImagesAndInfo(imagesResponse)
} else {
// TODO Warning to user
Log.e(TAG, "Error: Get request returned no response")
}
... // All the rest of code
}
}
-> just like a regular function. Of course you need to all it from a coroutine:
lifecycleScope.launch {
val result = withContext(Dispatchers.IO) { neoRepository.fetch() }
Log.i(TAG, "After suspend fun")
}
Google recommends to inject the dispatcher into the lower level classes (https://developer.android.com/kotlin/coroutines/coroutines-best-practices) so ideally you'd do:
val neoRepository = ImageRepository(Dispatchers.IO)
lifecycleScope.launch {
val result = neoRepository.fetch()
Log.i(TAG, "After suspend fun")
}
class ImageRepository(private val dispatcher: Dispatcher) {
suspend fun fetch () = withContext(dispatcher) {
val imagesResponse = getRequest(API_IMAGES)
if (imagesResponse != null) {
val jsonWithImagesAndInfo = composeJsonWithImagesAndInfo(imagesResponse)
} else {
// TODO Warning to user
Log.e(TAG, "Error: Get request returned no response")
}
... // All the rest of code
}
}
I'm kind of new developing with Android, and have problems understanding how things works. I made an app to scrape content from a page, and download elements, first with AsyncTask and worked, but since AsyncTask doesn't let me communicate with the UI Activity on progress, i decided to change to coroutines, checked an example, and the same code i used doesn't seems to work.
I used a few logs to try to determine the problem, and seems like it doesn't wait for the Jsoup request. The coroutine first calls a method scrapePage() to download the HTML and scrape the links, and then calls downloadImages() to add the links to Android's DownloadManager. In the logs Log.d("action", "Start Scraping") is printed, but Log.d("action", "Page downloaded") doesn't, still we get Log.d("action", "End") from the coroutine, which makes me think that instead of waiting for the Jsoup request to answer, it goes with an empty response, causing the rest of the code to not work correctly.
DownloadService.kt
object DownloadService {
private val parentJob = Job()
...
private val coroutineScope = CoroutineScope(Dispatchers.Main + parentJob +
coroutineExceptionHandler)
fun StartService(URL: String, location:String, contx:Context) {
coroutineScope.launch(Dispatchers.Main) {
Log.d("action", "Start")
val links = scrapePage(URL)
val download = downloadImages(links, location, contx)
Log.d("action", "End")
}
}
private suspend fun scrapePage(url: String): MainActivity.Scraped =
withContext(Dispatchers.IO) {
var URL = url
var scrape = MainActivity.Scraped()
try {
Log.d("action", "Start Scraping")
var response = Jsoup.connect(URL).get()
Log.d("action", "Page downloaded")
response.getElementsByClass("link").forEach {
/*Scrape URLs*/
Log.d("action", "Add "+link)
}
} catch (e: Exception) {
when(e) {
is HttpStatusException -> {
System.out.println(e.getStatusCode())
scrape.error = true
error = true
}
}
}
return#withContext scrape
}
...
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
...
fun makeRequest(URL : String) {
WorkingURL = URL
var uri = ""
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
uri = MediaStore.Downloads.EXTERNAL_CONTENT_URI.toString()
log.text = uri
} else {
uri = getStoragePath()
log.text = uri
}
DownloadService.StartService(URL, uri, this)
Log.d("links", DownloadService.getError().toString())
}
}
I am not sure where the problem is, nor where to start searching. I know the code for the Scraping works, because i used it before with AsyncTask, so the problem seems to be passing it to coroutines.
Here Jsoup.connect(URL).get() is throwing an error. so, Log.d("action", "Page downloaded") is not called.
But since you are handling the exception, the code runs the catch part and completes the suspend function and moves on to downloadImages().
Solution
First, add a log in the catch part of scrapePage() function and find out what is causing the exception. Everything else in your code is good.
I am writing a testing code using Mockito.
I am using RxJava and Retrofit to get the resource from the server.
mockRestService.get(id) method returns Observable.
First, the request call returns an item with the status of "not ready".
So I should use the Rx operator "repeatWhen()".
After some retry, the server sends an item with the status of "complete".
Below is my Presenter code.
val getItem = restService.getItem(id)
.repeatWhen { it.delay(1000, TimeUnit.MILLISECONDS) }
.takeUntil { it.status == "complete" }
And below is my testing code.
To mimic server behaviour, I wrote below the testing code.
#Test
fun printJobTest_one_job_normal_case() {
val notReadyItem = Item(status = "not ready")
val completeItem = Item(status = "complete")
Mockito.`when`(mockRestService.getItem(id))
.thenReturn(Observable.just(notReadyItem)) // First 2 response is "not ready" status
.thenReturn(Observable.just(notReadyItem))
.thenReturn(Observable.just(completeItem)) // Third response is "complete" status
// verify
}
To mimic the server behaviour, I used chained "thenReturn()" method.
But only always the item which's status is "not ready" occurs.
I... found the solution.
It's easy...
Just below code works fine.
#Test
fun printJobTest_one_job_normal_case() {
val notReadyItem = Item(status = "not ready")
val completeItem = Item(status = "complete")
Mockito.`when`(mockRestService.getItem(id))
.thenReturn(Observable.just(notReadyItem, notReadyItem, completeItem))
// verify
}
I just removed the chained method "thenReturn", and moved the variables to the parameter of Observable.
I am using the Play Services Auth api Phone and so far I have the foll
fun startSmsListener() {
val client = SmsRetriever.getClient(applicationContext /* context */);
val task = client.startSmsRetriever();
task.addOnSuccessListener(object : OnSuccessListener<Void> {
override fun onSuccess(p0: Void?) {
//do somethin
}
})
task.addOnFailureListener(object : OnFailureListener {
override fun onFailure(p0: Exception) {
//Handle error
}
})
}
Now I want to put this in an SmsManager class and convert it into an Single/Observable so I can handle it in a reactive way in my viewmodel. How can I do that?
So far I've got this:
var single = Single.create(SingleOnSubscribe<Void> { e ->
val task = client.startSmsRetriever()
task.addOnSuccessListener {
e.onSuccess(it)
}
task.addOnFailureListener {
e.onError(it)
}
})
But I am unsure as to whether this code is correct or not, whether there is something im missing like removing the listeners after disposal.
Any help?
You are interested in a "boolean" value - either connected or not connected, thus instead of Single you should use Completable:
Completable.create { emitter ->
val client = SmsRetriever.getClient(applicationContext)
val task = client.startSmsRetriever()
task.addOnSuccessListener { emitter.onComplete() }
task.addOnFailureListener { emitter.tryOnError(it) }
}
While creating a Completable manually will work, you might also have a look at the RxTask project. It provides "RxJava 2 binding for Google Play Services Task APIs".
If you need it just in one place, an extra library would certainly be an overkill. But if you plan to use more Play Services together with RxJava, it might be worth a look...
It doesn't (yet) provide a wrapper explicitly for SmsRetriever, but the general task helper classes would probably be enough:
val client = SmsRetriever.getClient(applicationContext)
val smsReceiver = CompletableTask.create(client::startSmsRetriever)
I´m sorry if this question has been asked previously, I really couldn´t find anything not even simillar! I´m as well sorry if the question is dumb, I´m an iOS Developer and I´m a bit lost here in Android...
So I´m using Fuel Library(https://github.com/kittinunf/Fuel) to GET JSON data from an API... In Swift there´s something called completion handler that whenever the function finished, it will return it and immediately run the code inside it. This is an example of it in Swift:
func hardProcessingWithString(input: String, completion: (result: String) -> Void) {
...
completion("we finished!")
}
What I need is to do something similar with this following function that I have in Kotlin.
fun recomendationsData() {
Fuel.get("https://rss.itunes.apple.com/api/v1/us/apple-music/hot-tracks/10/explicit.json").response { request, response, result ->
println(request)
println(response)
val (bytes, error) = result
if (bytes != null) {
val str = String(bytes)
val obj = JSONObject(str)
val resultsP = obj.getJSONObject("feed")
val results = resultsP.getJSONArray("results")
for (i in 0..(results.length() - 1)) {
val o = results.getJSONObject(i)
trackName1.add(o.getString("name"))
trackArtist1.add(o.getString("artistName"))
trackImage1.add(o.getString("artworkUrl100"))
}
}
}
}
I´ve read about something called "callback" but I really don´t understand how it works, nor how to implement it(The task must be done Asynchronously).
Thank you very much again!
Regards
In this case the syntax is similar to swift:
fun recommendationsData(callback: (String) -> Unit) {
Then in your function you have a function called callback that you can call with the result (change String to whatever you're returning).
Then change your function invocation from recommendationsData() to either recommendationsData(doSomething) or
recommendationsData {
doSomethingWith(it) // or you can get named argument
// do some more stuff
}