Why do I still get a NetworkOnMainThreadException despite using async? - android

Here is my program to display results from a web app (data product) I made on an Android device.
package studio.nyaa.crimeprediction
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import java.net.URL
import android.util.Log
import android.widget.TextView
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Parser
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.async
suspend fun obtainJson():JsonObject {
val sourceURL = "http://yingzhou474.pythonanywhere.com/api"
val jsonRes: String = URL(sourceURL).readText()
val parser: Parser = Parser.default()
val json = parser.parse(jsonRes) as JsonObject
Log.d("oj", "oj finished")
return json
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking {
val json = async{obtainJson()}.await()
Log.d("postcoroutine", json.toString())
val crime1: Double? = json.obj("data")?.double("AGGRAVATED ASSAULT")
val crime2: Double? = json.obj("data")?.double("AUTO THEFT")
val crime3: Double? = json.obj("data")?.double("COMMERCIAL BURGLARY")
val crime4: Double? = json.obj("data")?.double("HOMICIDE")
val crime5: Double? = json.obj("data")?.double("LARCENY")
val crime6: Double? = json.obj("data")?.double("OTHER BURGLARY")
val crime7: Double? = json.obj("data")?.double("RESIDENTIAL BURGLARY")
val crime8: Double? = json.obj("data")?.double("ROBBERY")
val year: Int? = json.int("year")
val month: Int? = json.int("month")
val day: Int? = json.int("day")
val textView1: TextView = findViewById(R.id.editText1B)
textView1.text = crime1?.toString() ?: "N/A"
val textView2: TextView = findViewById(R.id.editText2B)
textView2.text = crime2?.toString() ?: "N/A"
val textView3: TextView = findViewById(R.id.editText3B)
textView3.text = crime3?.toString() ?: "N/A"
val textView4: TextView = findViewById(R.id.editText4B)
textView4.text = crime4?.toString() ?: "N/A"
val textView5: TextView = findViewById(R.id.editText5B)
textView5.text = crime5?.toString() ?: "N/A"
val textView6: TextView = findViewById(R.id.editText6B)
textView6.text = crime6?.toString() ?: "N/A"
val textView7: TextView = findViewById(R.id.editText7B)
textView7.text = crime7?.toString() ?: "N/A"
val textView8: TextView = findViewById(R.id.editText8B)
textView8.text = crime8?.toString() ?: "N/A"
val textViewDate: TextView = findViewById(R.id.editTextDate)
textViewDate.text = "Date: $month/$day/$year"
}
}
}
Here the data source is this site which returns a JsonObject of this kind:
{"data": {"AGGRAVATED ASSAULT": 6.87, "AUTO THEFT": 4.4, "COMMERCIAL BURGLARY": 1.3, "HOMICIDE": 0.62, "LARCENY": 30.72, "OTHER BURGLARY": 0.59, "RESIDENTIAL BURGLARY": 4.21, "ROBBERY": 3.16}, "year": 2019, "month": 8, "day": 20}
Edited2: I fixed the Klaxon error on my own.
Edited: After fixing this now I'm getting something else:
08-19 23:13:32.385 2175-2175/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: studio.nyaa.crimeprediction, PID: 2175
java.lang.RuntimeException: Unable to start activity ComponentInfo{studio.nyaa.crimeprediction/studio.nyaa.crimeprediction.MainActivity}: java.io.FileNotFoundException: {"data": {"AGGRAVATED ASSAULT": 6.87, "AUTO THEFT": 4.4, "COMMERCIAL BURGLARY": 1.3, "HOMICIDE": 0.62, "LARCENY": 30.72, "OTHER BURGLARY": 0.59, "RESIDENTIAL BURGLARY": 4.21, "ROBBERY": 3.16}, "year": 2019, "month": 8, "day": 20}: open failed: ENOENT (No such file or directory)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2404)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2466)
at android.app.ActivityThread.access$900(ActivityThread.java:175)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5418)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1037)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
Caused by: java.io.FileNotFoundException: {"data": {"AGGRAVATED ASSAULT": 6.87, "AUTO THEFT": 4.4, "COMMERCIAL BURGLARY": 1.3, "HOMICIDE": 0.62, "LARCENY": 30.72, "OTHER BURGLARY": 0.59, "RESIDENTIAL BURGLARY": 4.21, "ROBBERY": 3.16}, "year": 2019, "month": 8, "day": 20}: open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:456)
at java.io.FileInputStream.<init>(FileInputStream.java:76)
at com.beust.klaxon.Parser$DefaultImpls.parse(Parser.kt:21)
at com.beust.klaxon.KlaxonParser.parse(KlaxonParser.kt:9)
at studio.nyaa.crimeprediction.MainActivityKt.obtainJson(MainActivity.kt:16)
at studio.nyaa.crimeprediction.MainActivity$onCreate$1$json$1.invokeSuspend(MainActivity.kt:25)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:740)
Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
at libcore.io.Posix.open(Native Method)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
at libcore.io.IoBridge.open(IoBridge.java:442)
at java.io.FileInputStream.<init>(FileInputStream.java:76) 
at com.beust.klaxon.Parser$DefaultImpls.parse(Parser.kt:21) 
at com.beust.klaxon.KlaxonParser.parse(KlaxonParser.kt:9) 
at studio.nyaa.crimeprediction.MainActivityKt.obtainJson(MainActivity.kt:16) 
at studio.nyaa.crimeprediction.MainActivity$onCreate$1$json$1.invokeSuspend(MainActivity.kt:25) 
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241) 
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594) 
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60) 
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:740) 
08-19 23:13:32.555 2175-2175/? I/Process: Sending signal. PID: 2175 SIG: 9
Old stuff:
Despite using async and await I still got the android.os.NetworkOnMainThreadException. Here is my stack trace:
08-19 22:28:06.675 28015-28015/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: studio.nyaa.crimeprediction, PID: 28015
java.lang.RuntimeException: Unable to start activity ComponentInfo{studio.nyaa.crimeprediction/studio.nyaa.crimeprediction.MainActivity}: android.os.NetworkOnMainThreadException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2404)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2466)
at android.app.ActivityThread.access$900(ActivityThread.java:175)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5418)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1037)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
Caused by: android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
at java.net.InetAddress.lookupHostByName(InetAddress.java:418)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
at java.net.InetAddress.getAllByName(InetAddress.java:215)
at com.android.okhttp.HostResolver$1.getAllByName(HostResolver.java:29)
at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:232)
at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:124)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:272)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:211)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:382)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:332)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:199)
at java.net.URL.openStream(URL.java:470)
at kotlin.io.TextStreamsKt.readBytes(ReadWrite.kt:150)
at studio.nyaa.crimeprediction.MainActivityKt.obtainJson(MainActivity.kt:17)
at studio.nyaa.crimeprediction.MainActivity$onCreate$1$json$1.invokeSuspend(MainActivity.kt:28)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:270)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at studio.nyaa.crimeprediction.MainActivity.onCreate(MainActivity.kt:27)
at android.app.Activity.performCreate(Activity.java:6083)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1115)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2357)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2466) 
at android.app.ActivityThread.access$900(ActivityThread.java:175) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:135) 
at android.app.ActivityThread.main(ActivityThread.java:5418) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1037) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832) 
This question is a variant of my earlier question.

It's because async block is running with Dispatchers.Main try using it with Dispatchers.IO
val json = async(Dispatchers.IO){obtainJson()}.await()
Also a better syntax would be to use withContext
val json = withContext(Dispatchers.IO){obtainJson()}

You shouldn't call network in the main thread. You can fix it by using IO dispatcher:
async(Dispatchers.IO){ obtainJson() }.await()

You should not use runBlocking {} in production code! It'll block your main thread.
Since you're calling this within the main thread, every following call to async or launch will be run in the same scope and thus on the main thread.
Instead you should launch a coroutine preferable with a viewModelScope (GlobalScope would be an acceptable start as well) and run the view updates on the main thread:
GlobalScope.launch(IO) {
val json = obtainJson()
Log.d("postcoroutine", json.toString())
val crime1: Double? = json.obj("data")?.double("AGGRAVATED ASSAULT")
...
val year: Int? = json.int("year")
val month: Int? = json.int("month")
val day: Int? = json.int("day")
launch(Main) {
val textView1: TextView = findViewById(R.id.editText1B)
textView1.text = crime1?.toString() ?: "N/A"
...
val textViewDate: TextView = findViewById(R.id.editTextDate)
textViewDate.text = "Date: $month/$day/$year"
}
}
Using async is not necessary here. Instead of launch(Main) you could use withContext(Main) though.

Related

Android&Firebase | Row too big to fit into CursorWindow while adding data to Firestore

I am using Firebase's Firestore to save my data class for the Android project.
I followed the official tutorial's add Data section provided by Firebase
but now get the error as followed:
SQLiteQuery Exception
E/SQLiteQuery: exception: Row too big to fit into CursorWindow requiredPos=0, totalRows=9; query: SELECT overlay_mutation, largest_batch_id FROM document_overlays WHERE uid = ? AND collection_path = ? AND largest_batch_id > ?
Below are my Data Class to save, and the function to make it as HashMap.
Diary.kt
data class Diary(
val id: String = "",
val account: String = "",
val title: String = "",
val content: String = "",
val imageUri: String? = null,
val likesCount: Int = 0,
val isLiked: Boolean = false,
val comments: List<String> = listOf(),
val createdTime: Long = 0L,
val updatedTime: Long? = null,
val isThumbnailExist: Boolean = false
) {
fun mappingForFireStore(): HashMap<String, Any?> {
return hashMapOf(
"id" to id,
"account" to account,
"title" to title,
"content" to content,
"imageUri" to imageUri,
"likes_count" to likesCount,
"is_liked" to isLiked,
"comments" to comments,
"created_time" to createdTime,
"updated_time" to updatedTime,
"is_thumbnail_exist" to isThumbnailExist
)
}
}
Here is a function that I called to save it to FireStore.
Save-To-FireStore func.kt
private fun asyncTestFirebase() {
val data1 = Diary(
id = UUID.randomUUID().toString(),
account = idInput.value!!.trim(),
title = "My second data",
content = "test data.",
createdTime = Calendar.getInstance(Locale.getDefault()).time.time
).mappingForFireStore()
db.collection("diaries")
.add(data1)
.addOnSuccessListener { documentReference ->
Log.d("Haha", "DocumentSnapshot added with ID: ${documentReference.id}")
}
.addOnFailureListener { error ->
Log.d("OMG", error.stackTraceToString())
}
}
I have searched for similar questions, but could not find answers fit as I am not trying to store any images directly, or using Cursor or Field. I don't think the data weighs more than 1mb so its size would not be a matter.
How can I successfully save data to Firestore in this case?
Below is AndroidRuntimeException Log, just in case.
Thank you.
AndroidRuntime Error Log
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.movingroot.simpleproject, PID: 1616
java.lang.RuntimeException: Internal error in Cloud Firestore (24.2.2).
at com.google.firebase.firestore.util.AsyncQueue.lambda$panic$3(AsyncQueue.java:539)
at com.google.firebase.firestore.util.AsyncQueue$$ExternalSyntheticLambda3.run(Unknown Source:2)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.RuntimeException: android.database.sqlite.SQLiteBlobTooBigException: Row too big to fit into CursorWindow requiredPos=0, totalRows=9
at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor.lambda$executeAndReportResult$1(AsyncQueue.java:330)
at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$$ExternalSyntheticLambda0.run(Unknown Source:4)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$DelayedStartFactory.run(AsyncQueue.java:234)
at java.lang.Thread.run(Thread.java:920)
Caused by: android.database.sqlite.SQLiteBlobTooBigException: Row too big to fit into CursorWindow requiredPos=0, totalRows=9
at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method)
at android.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:1003)
at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:838)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:145)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:132)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:238)
at android.database.AbstractCursor.moveToNext(AbstractCursor.java:287)
at com.google.firebase.firestore.local.SQLitePersistence$Query.forEach(SQLitePersistence.java:493)
at com.google.firebase.firestore.local.SQLiteDocumentOverlayCache.getOverlays(SQLiteDocumentOverlayCache.java:149)
at com.google.firebase.firestore.local.LocalDocumentsView.getDocumentsMatchingCollectionQuery(LocalDocumentsView.java:365)
at com.google.firebase.firestore.local.LocalDocumentsView.getDocumentsMatchingQuery(LocalDocumentsView.java:266)
at com.google.firebase.firestore.local.QueryEngine.appendRemainingResults(QueryEngine.java:259)
at com.google.firebase.firestore.local.QueryEngine.performQueryUsingRemoteKeys(QueryEngine.java:175)
at com.google.firebase.firestore.local.QueryEngine.getDocumentsMatchingQuery(QueryEngine.java:85)
at com.google.firebase.firestore.local.LocalStore.executeQuery(LocalStore.java:844)
at com.google.firebase.firestore.core.SyncEngine.initializeViewAndComputeSnapshot(SyncEngine.java:214)
at com.google.firebase.firestore.core.SyncEngine.listen(SyncEngine.java:207)
at com.google.firebase.firestore.core.EventManager.addQueryListener(EventManager.java:104)
at com.google.firebase.firestore.core.FirestoreClient.lambda$listen$7$com-google-firebase-firestore-core-FirestoreClient(FirestoreClient.java:177)
at com.google.firebase.firestore.core.FirestoreClient$$ExternalSyntheticLambda3.run(Unknown Source:4)

How to extract data from an JsonObject in an android studio project

I'm working on a REST API application in android studio. I want to extract the data from a JsonObject which I saved using the model class when retrieving data using retrofit library. But It shows the below error.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.covid_19updates, PID: 15236
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
Caused by: org.json.JSONException: No value for flag
at org.json.JSONObject.get(JSONObject.java:400)
at org.json.JSONObject.getJSONObject(JSONObject.java:620)
at com.example.covid_19updates.MainActivity$onCreate$1.onResponse(MainActivity.kt:52)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1.lambda$onResponse$0$DefaultCallAdapterFactory$ExecutorCallbackCall$1(DefaultCallAdapterFactory.java:89)
at retrofit2.-$$Lambda$DefaultCallAdapterFactory$ExecutorCallbackCall$1$hVGjmafRi6VitDIrPNdoFizVAdk.run(Unknown Source:6)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
The single data object I'm extracting is below:
{"updated":1606563124609,"country":"Afghanistan","countryInfo":{"_id":4,"iso2":"AF","iso3":"AFG","lat":33,"long":65,"flag":"https://disease.sh/assets/img/flags/af.png"},"cases":45966,"todayCases":127,"deaths":1752,"todayDeaths":12,"recovered":36709,"todayRecovered":414,"active":7505,"critical":93,"casesPerOneMillion":1170,"deathsPerOneMillion":45,"tests":146413,"testsPerOneMillion":3727,"population":39280785,"continent":"Asia","oneCasePerPeople":855,"oneDeathPerPeople":22421,"oneTestPerPeople":268,"activePerOneMillion":191.06,"recoveredPerOneMillion":934.53,"criticalPerOneMillion":2.37}
I want to retrieve data inside the countryInfo object. Below is my modal class
data class Covid(
var updated: Long,
var country:String,
var countryInfo:JSONObject,
var cases:Int,
var todayCases:Int,
var deaths:Int,
var todayDeaths:Int,
var recovered:Int,
var todayRecovered:Int,
var active:Int,
var critical:Int,
var casesPerOneMillion:Double,
var deathsPerOneMillion:Double,
var test:Long,
var testPerOneMillion:Double,
var population:Long,
var continent:String,
var oneCasePerPeople:Double,
var oneDeathPerPeople:Double,
var oneTestPerPeople:Double,
var activePerOneMillion:Double,
var recoveredPerOneMilion:Double,
var criticalPerOneMillion:Double
)
And the code piece I wrote to get the data
override fun onResponse(call: Call<List<Covid>>, response: Response<List<Covid>>) {
var list = response.body()
Log.d("Response: ",response.toString())
var countryList = ArrayList<String>()
var countList = ArrayList<String>()
var activeList = ArrayList<String>()
var deathList = ArrayList<String>()
var recoveredList = ArrayList<String>()
var imageList = ArrayList<String>()
list?.map { item ->
countryList.add(item.country.toString())
var totalCases = "Total Cases: "+item.cases.toString()
var activeCases = "Active Cases: "+item.active.toString()
var deaths = "Total Deaths: "+item.deaths.toString()
var recovered = "Total Recovered: "+item.recovered.toString()
var image = item.countryInfo.getJSONObject("flag").toString()
countList.add(totalCases)
activeList.add(activeCases)
deathList.add(deaths)
recoveredList.add(recovered)
}
Try and make a separate data class for CountryInfo.
In covid class
val countryInfo: CountryInfo,
Then
data class CountryInfo(
val _id: Int,
val iso2: String,
val iso3: String,
val lat: Double, //trust me you should use double on latLng values.
val long: Double,
val flag: String
)
Sidenote, you can use this site to automate making kotlin objects from JSONs.

http request in android kotlin

I try to get JSON or something like this, using URL.readText() from java.net.url in android studio, but my app crashes.
fun ButtonClick(view:View) {
textView.text = URL("https://www.google.com/robots.txt").readText()
}
I also added this into manifest.xml
<uses-permission android:name="android.permission.INTERNET" />
but it crashes anyway
please, help me! what am i doing wrong?. I think it's ok to get text from url, using url.readText, because some guy in this post HTTP Request in Kotlin wrote that it's simpler
crash log:
2020-08-17 23:16:04.117 2251-2251/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 2251
java.lang.IllegalStateException: Could not execute method for android:onClick
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:414)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
at android.view.View.performClick(View.java:5637) 
at android.view.View$PerformClick.run(View.java:22429) 
at android.os.Handler.handleCallback(Handler.java:751) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:154) 
at android.app.ActivityThread.main(ActivityThread.java:6119) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
Caused by: android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:86)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:74)
at java.net.InetAddress.getAllByName(InetAddress.java:752)
at com.android.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:187)
at com.android.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:156)
at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:98)
at com.android.okhttp.internal.http.HttpEngine.createNextConnection(HttpEngine.java:345)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:328)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:246)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:457)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:405)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:243)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java)
at java.net.URL.openStream(URL.java:1057)
at kotlin.io.TextStreamsKt.readBytes(ReadWrite.kt:150)
at com.example.myapplication.MainActivity.ButtonClick(MainActivity.kt:25)
at java.lang.reflect.Method.invoke(Native Method) 
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409) 
at android.view.View.performClick(View.java:5637) 
at android.view.View$PerformClick.run(View.java:22429) 
at android.os.Handler.handleCallback(Handler.java:751) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:154) 
at android.app.ActivityThread.main(ActivityThread.java:6119) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
Based on what limited info you have provided, you might be having a NetworkOnMainThreadException wrap your code in a thread for it to work
fun ButtonClick(view:View){
thread {
val response = URL("https://www.google.com/robots.txt").readText()
activity?.runOnUiThread{
textView.text = response
}
}
}
I was have this problem and I wasn't fix it
but now I using Volley in android and it is really powerful and easy
I suggest you to work with Volley
you should use from Retrofit in your codes
1- Add the following lines in the app level build.gradle file to apply Google Services to your project
dependencies {
...
Implementation 'org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version'
implementation 'com.squareup.retrofit2:retrofit:2.0.0'
implementation 'com.squareup.retrofit2:converter-gson:2.0.0'
}
2-Then, click “Sync Now” to set up your project.
3- Don't forget to add the following permissions in your manifest file
<uses-permission android:name="android.permission.INTERNET"/>
4- Create an interface file named as “WeatherService.kt” and add the following lines in it
interface WeatherService {
#GET("data/2.5/weather?")
fun getCurrentWeatherData(#Query("lat") lat: String, #Query("lon") lon: String,
#Query("APPID") app_id: String): Call<WeatherResponse>
}
5-Create a class file named as “WeatherResponse.kt” and add the following lines. Here, we used a Gson Converter and so the JSON response is automatically converted to the respective and the converter will compare the response tree with the serialized name.
class WeatherResponse {
#SerializedName("coord")
var coord: Coord? = null
#SerializedName("sys")
var sys: Sys? = null
#SerializedName("weather")
var weather = ArrayList<Weather>()
#SerializedName("main")
var main: Main? = null
#SerializedName("wind")
var wind: Wind? = null
#SerializedName("rain")
var rain: Rain? = null
#SerializedName("clouds")
var clouds: Clouds? = null
#SerializedName("dt")
var dt: Float = 0.toFloat()
#SerializedName("id")
var id: Int = 0
#SerializedName("name")
var name: String? = null
#SerializedName("cod")
var cod: Float = 0.toFloat()
}
class Weather {
#SerializedName("id")
var id: Int = 0
#SerializedName("main")
var main: String? = null
#SerializedName("description")
var description: String? = null
#SerializedName("icon")
var icon: String? = null
}
class Clouds {
#SerializedName("all")
var all: Float = 0.toFloat()
}
class Rain {
#SerializedName("3h")
var h3: Float = 0.toFloat()
}
class Wind {
#SerializedName("speed")
var speed: Float = 0.toFloat()
#SerializedName("deg")
var deg: Float = 0.toFloat()
}
class Main {
#SerializedName("temp")
var temp: Float = 0.toFloat()
#SerializedName("humidity")
var humidity: Float = 0.toFloat()
#SerializedName("pressure")
var pressure: Float = 0.toFloat()
#SerializedName("temp_min")
var temp_min: Float = 0.toFloat()
#SerializedName("temp_max")
var temp_max: Float = 0.toFloat()
}
class Sys {
#SerializedName("country")
var country: String? = null
#SerializedName("sunrise")
var sunrise: Long = 0
#SerializedName("sunset")
var sunset: Long = 0
}
class Coord {
#SerializedName("lon")
var lon: Float = 0.toFloat()
#SerializedName("lat")
var lat: Float = 0.toFloat()
}
6- Then, open your Activity file and in my case, I have opened my MainActivity.kt file. Then, create a Retrofit Builder with Base URL and GsonConverterFactory.
val retrofit = Retrofit.Builder()
.baseUrl(BaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
7-Then, create a service for Retrofit with your service interface, as shown below
val service = retrofit.create(WeatherService::class.java)
val call = service.getCurrentWeatherData(lat, lon, AppId)
Here, “getCurrentWeatherData” is an interface function created in the “WeatherService” interface.
8-Then, create a queue with Weather Response which is used to de-serialize the JSON output of the Open Weather API. The following will show the created queue.
call.enqueue(object : Callback<WeatherResponse> {
override fun onResponse(call: Call<WeatherResponse>, response:
Response<WeatherResponse>) {
if (response.code() == 200) {
…
}
}
override fun onFailure(call: Call<WeatherResponse>, t: Throwable) {
…
}
})
variables defined :
companion object {
var BaseUrl = "http://api.openweathermap.org/"
var AppId = "2e65127e909e178d0af311a81f39948c"
var lat = "35"
var lon = "139"
}

How to use generic types with retrofit and moshi in kotlin?

Suppose we have the following model
open class Orden(#field:Json(name = "vehiculo_id") var vehiculo_id: IdNameItem = IdNameItem()) :
RealmObject()
open class IdNameItem(
#field:Json(name = "id") var id: Long = 0,
#field:Json(name = "name") var name: String = ""
) : RealmObject()
the, we want to fetch all Orden from WS, something like this
#GET("orden")
fun getOrden(#Body body: Any = Object()): Call<SyncResponse>
We have configured Retrofit like this
val retrofit = Retrofit.Builder()
.baseUrl(environment.url)
.addConverterFactory(MoshiConverterFactory.create())
.client(httpClient.build())
.build()
return retrofit.create(WebServices::class.java)
And SyncReponse is configured like this
class SyncResponse : BaseResponse<List<Orden>>()
open class BaseResponse<T> {
#field:Json(name = "result")
lateinit var result: ResultResponse<T>
#field:Json(name = "error")
val error: ErrorResponse = ErrorResponse()
and ResultResponse looking like..
open class ResultResponse<T>(
#field:Json(name = "status") val status: Int = 0,
#field:Json(name = "msj_error") val msj_error: String = "",
#field:Json(name = "data") var data: T
This is how I pretend to parse this response
{
"result": {
"status": 201,
"msj_error": "",
"data": [
{"user_id": 1,
"vehiculo": "New Holland/7630/1235888",
** others fields **
}]
}
}
And when I run it, it throws this error
06-27 20:12:27.895 1036-1036/com.azanini.ordenes E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.azanini.ordenes, PID: 1036
java.lang.IllegalArgumentException: Unable to create converter for class com.azanini.ordenes.ws.NoResponse
for method WebServices.login
at retrofit2.Utils.methodError(Utils.java:53)
at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:115)
at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:82)
at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:37)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:192)
at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
at java.lang.reflect.Proxy.invoke(Proxy.java:393)
at $Proxy0.login(Unknown Source)
at com.azanini.ordenes.activities.LoginActivity.login(LoginActivity.kt:83)
at com.azanini.ordenes.activities.LoginActivity.onClick(LoginActivity.kt:76)
at android.view.View.performClick(View.java:5716)
at android.widget.TextView.performClick(TextView.java:10926)
at android.view.View$PerformClick.run(View.java:22596)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:7325)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Caused by: java.lang.IllegalArgumentException: No JsonAdapter for ? extends com.azanini.ordenes.model.Orden (with no annotations)
for ? extends com.azanini.ordenes.model.Orden
for java.util.List<? extends com.azanini.ordenes.model.Orden> data
for class com.azanini.ordenes.ws.ResultResponse result
for class com.azanini.ordenes.ws.NoResponse
at com.squareup.moshi.Moshi$LookupChain.exceptionWithLookupStack(Moshi.java:348)
at com.squareup.moshi.Moshi.adapter(Moshi.java:149)
at com.squareup.moshi.Moshi.adapter(Moshi.java:97)
at com.squareup.moshi.Moshi.adapter(Moshi.java:67)
at com.squareup.moshi.CollectionJsonAdapter.newArrayListAdapter(CollectionJsonAdapter.java:52)
at com.squareup.moshi.CollectionJsonAdapter$1.create(CollectionJsonAdapter.java:36)
at com.squareup.moshi.Moshi.adapter(Moshi.java:137)
at com.squareup.moshi.ClassJsonAdapter$1.createFieldBindings(ClassJsonAdapter.java:99)
at com.squareup.moshi.ClassJsonAdapter$1.create(ClassJsonAdapter.java:82)
at com.squareup.moshi.Moshi.adapter(Moshi.java:137)
at com.squareup.moshi.ClassJsonAdapter$1.createFieldBindings(ClassJsonAdapter.java:99)
at com.squareup.moshi.ClassJsonAdapter$1.create(ClassJsonAdapter.java:82)
at com.squareup.moshi.Moshi.adapter(Moshi.java:137)
at com.squareup.moshi.Moshi.adapter(Moshi.java:97)
at retrofit2.converter.moshi.MoshiConverterFactory.responseBodyConverter(MoshiConverterFactory.java:91)
at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:352)
at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:335)
at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:113)
... 18 more
Caused by: java.lang.IllegalArgumentException: No JsonAdapter for ? extends com.azanini.ordenes.model.Orden (with no annotations)
at com.squareup.moshi.Moshi.adapter(Moshi.java:147)
... 34 more
I imagine you can figure out that any type of data can be set in the "data" field, so I wwant to parametrize it, it is possible?
Allow generic types in responses when using Moshi as JSON parser #3285
Seems like it is impossible.

How correctly to serialize such JSON by means of GSON Android?

How correctly to serialize such JSON by means of GSON Android?
[
{
"id": "bitcoin",
"name": "Bitcoin",
"symbol": "BTC",
"rank": "1",
"price_usd": "15022.7",
"price_btc": "1.0",
"24h_volume_usd": "13711300000.0",
"market_cap_usd": "251264422255",
"available_supply": "16725650.0",
"total_supply": "16725650.0",
"max_supply": "21000000.0",
"percent_change_1h": "0.94",
"percent_change_24h": "17.4",
"percent_change_7d": "47.71",
"last_updated": "1512638053"
},
{
"id": "ethereum",
"name": "Ethereum",
"symbol": "ETH",
"rank": "2",
"price_usd": "427.071",
"price_btc": "0.0291936",
"24h_volume_usd": "2007350000.0",
"market_cap_usd": "41078436955.0",
"available_supply": "96186435.0",
"total_supply": "96186435.0",
"max_supply": null,
"percent_change_1h": "-1.59",
"percent_change_24h": "-5.64",
"percent_change_7d": "-3.22",
"last_updated": "1512638054"
}
]
Data class:
class AllCurrenсyData {
#SerializedName("")
#Expose
var currency: List<Currency>? = null
}
class Currency {
#SerializedName("price_usd")
#Expose
val priceUsd: String? = null
#SerializedName("symbol")
#Expose
val symbol: String? = null
#SerializedName("last_updated")
#Expose
val lastUpdated: String? = null
#SerializedName("total_supply")
#Expose
val totalSupply: String? = null
#SerializedName("24h_volume_usd")
#Expose
val jsonMember24hVolumeUsd: String? = null
#SerializedName("price_btc")
#Expose
val priceBtc: String? = null
#SerializedName("available_supply")
#Expose
val availableSupply: String? = null
#SerializedName("market_cap_usd")
#Expose
val marketCapUsd: String? = null
#SerializedName("percent_change_1h")
#Expose
val percentChange1h: String? = null
#SerializedName("percent_change_24h")
#Expose
val percentChange24h: String? = null
#SerializedName("name")
#Expose
val name: String? = null
#SerializedName("max_supply")
#Expose
val maxSupply: String? = null
#SerializedName("rank")
#Expose
val rank: String? = null
#SerializedName("id")
#Expose
val id: String? = null
#SerializedName("percent_change_7d")
#Expose
val percentChange7d: String? = null
}
To implement this I created such classes. But I'm listening to such a stack of errors:
FATAL EXCEPTION: main
Process: it.incode.cryptocoins, PID: 2620
io.reactivex.exceptions.OnErrorNotImplementedException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704)
at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701)
at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:74)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.checkTerminated(ObservableObserveOn.java:276)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:172)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6164)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:118)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:212)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
at com.jakewharton.retrofit2.adapter.rxjava2.CallObservable.subscribeActual(CallObservable.java:41)
at io.reactivex.Observable.subscribe(Observable.java:10842)
at com.jakewharton.retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:10842)
at io.reactivex.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:33)
at io.reactivex.Observable.subscribe(Observable.java:10842)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:213)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37) 
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25) 
at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:118) 
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:212) 
at retrofit2.OkHttpCall.execute(OkHttpCall.java:174) 
at com.jakewharton.retrofit2.adapter.rxjava2.CallObservable.subscribeActual(CallObservable.java:41) 
at io.reactivex.Observable.subscribe(Observable.java:10842) 
at com.jakewharton.retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34) 
at io.reactivex.Observable.subscribe(Observable.java:10842) 
at io.reactivex.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:33) 
at io.reactivex.Observable.subscribe(Observable.java:10842) 
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) 
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452) 
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61) 
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52) 
at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
at java.lang.Thread.run(Thread.java:761) 
Most likely I'm not correctly processing an array of data that does not have a name for serialization. I hope someone will tell me where my mistake is.
You dont need object AllCurrenсyData, you have to change only your call to return list of elements
fun getData() : Call<List<Currency>>

Categories

Resources