API call not working inside Anko Async - android

I'm trying to make an http request in Android, using Kotlin, and I've come across two ways of doing so.
One is the traditional way, using AsyncTask (not really pretty) which I got to work with the following code (just the doInBackground, as the rest of the class seemed unnecessary):
override fun doInBackground(vararg params: Void?): String? {
val url = URL("myUrl")
val httpClient = url.openConnection() as HttpURLConnection
if(httpClient.getResponseCode() == HttpURLConnection.HTTP_OK){
try {
val stream = BufferedInputStream(httpClient.getInputStream())
val data: String = readStream(inputStream = stream)
return data;
} catch (e : Exception) {
e.printStackTrace()
} finally {
httpClient.disconnect()
}
}else{
println("ERROR ${httpClient.getResponseCode()}")
}
return null
}
Now, I've come across a library called Anko, which many here know, and I tried to use its DSL for asynchronous tasks. The thing is, I haven't found a lot of info here about Anko for asynchronous tasks, so I thought I would open a new topic to see if someone could walk me through what I'm doing wrong, or what they think I should do to make it work.
The code I wanted to use is the following:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
async() {
val result = URL("myUrl").readText()
textView1.setText(result)
}
}
I tried to keep it as slim as possible so to minimize any potential mistakes, but I must be doing something wrong here because the code inside the async block is not doing anything, yet the app is not crashing and I'm not getting any exceptions. I've tried debugging it using Intellij IDEA, but after the first line inside the async block it stops the debugging while saying "The application is running". My best guess is that it got hung up somewhere in that first line due to the failed connection, but I don't know.
I've also tried to use the regular URL("myUrl").openConnection() inside the async block, but that hasn't worked either.
Anyway, any help would be deeply appreciated.

The problem textView1 contents not getting updated is caused by calling setText outside of main thread.
The documentation shows a nice example how to properly use async. Take a look at the following adapted version of your code:
async() {
val result = URL("http://stackoverflow.com/").readText()
uiThread {
textView1.text = result
}
}
PS. While not directly related to the question consider using a nicer http client i.e. Retrofit, OkHttp

The problem turned out to be a lot more basic than what I was thinking. It was a problem of compatibility apparently from having an older version of Android Studio running with the new version 1.0.2 of the Kotlin plugin and, again, apparently the function readText was not working properly and therefore I wasn't getting anything from it.
Anyway, I updated Android Studio, with the latest version of Kotlin and it started working fine, although I'm going to see if I can find out what it was that was causing the problem inside readText.

Related

Android coroutines "Possibly blocking call in non-blocking context could lead to thread starvation"

I have code like this:
CoroutineScope(Dispatchers.IO).launch {
binding.titleIcon.setImageBitmap(
BitmapFactory.decodeStream(
URL(titleIcon.formatImageUrl())
.openConnection()
.getInputStream()
)
)
}
This code gets me warning from the question title on the both lines:
.openConnection()
.getInputStream()
I don't understand why, My code is executed on background thread, Main thread should be fine, why is this message written. Even though this code works and does not crash, I don't like warnings in code and want to understand what is going on here.
It suggests me wrapping with withContext. So when I change this code to:
CoroutineScope(Dispatchers.IO).launch {
withContext(Dispatchers.IO) {
binding.titleIcon.setImageBitmap(
BitmapFactory.decodeStream(
URL(titleIcon.formatImageUrl())
.openConnection()
.getInputStream()
)
)
}
}
warnings dissapear and everything seems fine. Can anyone give nice explanation about this?

How to restart Android app within Espresso test?

I am using Espresso with Kotlin for the UI test automation. I am trying to find a proper way to restart the app during the test and start it again, so the test scenario is the following:
start the app, go to login page
force close the app and open it again (basically restart it)
check some stuff etc
The way our UI tests are organized:
there is a test class where I have rules
val intent = Intent(ApplicationProvider.getApplicationContext(), MainActivity::class.java)
.putExtra(UI_TEST_INTENT, true)
#get:Rule
val rule = ActivityScenarioRule<MainActivity>(intent)
there Before/After functions and tests functions in this class
What I want is to have generic restartApp function in separated class, let's say TestUtils and to be able to call it at any point of time, when is needed.
So far I didn't find a solution. There are some similar questions on stackoverflow, but I am not sure I understand how to work with the answers I found, like this:
with(activityRule) {
finishActivity()
launchActivity(null)
}
Since ActivityTestRule is deprecated and documentation asking to use ActivityScenarioRule, I tried this:
#get:Rule
val rule = ActivityScenarioRule<MainActivity>(intent)
private fun restart() {
rule.scenario.close()
rule.scenario.recreate()
}
but it gets java.lang.NullPointerException
another option is
private fun restart() {
pressBackUnconditionally()
Intents.release()
ActivityScenario.launch<MainActivity>(intent)
}
it works, app restarts but I can not interact with the app anymore, because for some reason there are two intents running now
Would be great to get an answer I can work with (I am quite new to Espresso)
Cheers
The solution is found:
private fun restart() {
Intents.release()
rule.scenario.close()
Intents.init()
ActivityScenario.launch<MainActivity>(intent)
}
Seems like the author's answer has some excess code. The following is enough
activityScenarioRule.scenario.close()
ActivityScenario.launch(YourActivity::class.java, null)

Xamarin android app throws object not set when calling api

So i have android app that makes several requests to api using following structure. Note that this works in iOS and this appeared to start happening after updating xamarin forms.
public async Task<Rootobject> GetData()
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(url); //throws exception here. See screenshot
}
}
This works fine for all my requests except for one and the only difference is that this one is called from within a task factory like following:
tasks.Add(Task.Factory.StartNew(async()=>
{
var data = await GetData();
}));
And invoked like following:
Task.WhenAll(tasks).ContinueWith(t =>
{
IsBusy = false;
}).GetAwaiter();
The part that really has me confused is that this works as expected in iOS. Does anyone have any idea what i can do to get past this?
Turns out the api was not issue at all even though it seemed like it because that is where debugger would blow up but turned out issue was https://github.com/alexrainman/CarouselView/issues/557 and is still an issue. My work around is to use custom carousel view when android instead of plugin. Hope this helps someone not spend nearly two days on like i did.

What is the proper way to mock a list inside a subscribe in Android? Is it even possible?

I have the following question because I've been fairly stuck for a while.
I have the following array:
private val toyGroupCodes = listOf("A1", "B2", "C3", "D4")
and I'm working with RxJava to obtain a list. I use the list to check if any of the toys inside it match the toyGroupCodes, see code below:
.subscribe ({ toyGroups: List<toyGroup?>? ->
if (!toyGroups.isNullOrEmpty()) {
if (toyGroups.any { it?.toyName in toyGroupCodes }) {
// do stuff
} else {
// do other stuff
}
} else {
// do other stuff
}
}, Timber::e)
The code works. The issue I'm having is that I have to write a proper test case. The struggle I'm facing is that my test specifically fails on this line:
toyGroups.any { it?.toyName in toyGroupCodes }
I've been trying all kinds of mocking to try and make it pass but I'm having no success. Is it because toyGroups is inside the subscribe that you can't mock it? If I comment out the if statement, the test passes. So I'm 100% sure the issue lies there. Is it because there's a collection iterable() happening through any{} that is not stubbed? If so, how can you solve this?
Can anybody help me please.

Make HTTP request on button click in Kotlin

I want to make a request in my android app when the button is clicked. In Python I could do this like that:
import requests
params = {
'param1':some_string,
'param2':some_int,
'param3':another_string
}
requests.post("https://some.api.com/method/some.method", params=params)
I'd like to do the same in Kotlin when I push the button. I tried tp do this with Fuel and khhtp but didn't succeed much -- app crashed as soon as I pushed the button, responsible for sending request.
UPD: What I used:
AndroidManifest.xml
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
...
build.gradle
dependencies {
...
compile 'com.github.jkcclemens:khttp:0.1.0'
...
}
MainActivity.kt
fun request(){
var message = "message"
var uid = "123456" //I wanted to use it as int, but mapOf didn't allow me
var token = "token"
val payload = mapOf("token" to token, "user_id" to uid, "message" to message)
get("https://some.api.com/method/some.method", params=payload)
val popup = Toast.makeText(this,"Message sent!",Toast.LENGTH_LONG)
popup.show()
}
activity_main.xml
<Button
...
android:onClick="request" />
This is the example with khhtp, the one with Fuel is gone.
UPD2. Part of Logcat output:
You just need to look at the stack trace to find the issue. The code is throwing a NetworkOnMainThreadException. This happens when you try to access the network from within Android's main (often called UI) thread. This question have some good answers about this issue, however instead of trying to use AsyncTask make sure to read the documentation of your chosen network library and see how to make the call on a different thread.
I'm not sure if this is the root of your problem but your request method signature should be:
fun request(view: View)
{
}
As other members answered your code is calling network operation on main thread that's why it is crashing . You can avoid this either by using Kotlin Coroutines or by using methods of Anko Library (Which is officially supported by kotlin to simplify things in android). Here i just give a reference for how to do Async call in Anko.
doAsync {
// Call all operation related to network or other ui blocking operations here.
uiThread {
// perform all ui related operation here
}
}
To do it as Kotlin Coroutines, you can refer this answer:-
Kotlin Coroutines the right way in Android
I found the answer, basically what's happening is that you are not able to run internet connections at main thread, To override, add the following to the class where the user is performing network operations:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Reference (https://www.educative.io/edpresso/how-to-fix-androidosnetworkonmainthreadexception-error)

Categories

Resources