I would like to fetch some data from my web server. The data is a JSON object. I fetch the data with the fuel framework. I want to fill a recyclerview with the data. But I want to show a progress until the data is fetched.
But I have no idea how to solve this with fuel.
I have studied the fuel documentation. But I cannot find a solution
The code fetches some JSON data
fun fetchMyThings(): List<Thing> {
val username = "xxxx"
val password = "xxxxxxxxx"
val things = mutableListOf<Thing>()
Fuel.get("https://www.thingurl.com/things")
.authentication()
.basic(username, password)
.responseJson { request, response, result ->
request.responseProgress()
val arr: JSONArray = result.get().array()
println(arr.length())
for (i in 0 until arr.length()) {
var elem: JSONObject = arr[i] as JSONObject
var obj: JSONObject = elem
var thing = Thing(UUID.fromString(obj.getString("uuid")))
thing.summary = obj.getString("summary")
things += thing
println(thing)
}
}
return things
}
This code fills the recyclerview
private fun fillRecyclerView(things : List<Thing>) {
val recyclerView = findViewById<RecyclerView>(R.id.main_recycler)
val mainActivityRecyclerAdapter = MainActivityAdapter(this, things)
recyclerView.adapter = mainActivityRecyclerAdapter
recyclerView.layoutManager = LinearLayoutManager(this)
}
Expectation: Progresshandling with Fuel
Many thanks in advance for any help
Next try...
fun getThings(): Single<Result<String, FuelError>> {
val username = "********"
val password = "********"
val http = "https://www.nowhere.com/thing/"
.httpGet()
.authentication()
.basic(username, password)
.rxString(Charsets.UTF_8)
return http
}
class MainActivity : AppCompatActivity() {
var things: List<Thing> = emptyList()
protected lateinit var adapter: MainActivityAdapter
override fun onCreate(savedInstanceState: Bundle?) {
//....
fillRecyclerView(things)
Server.getThings().subscribe { p ->
println(p)
val arr: JSONArray = JSONArray(p.component1())
for (i in 0 until arr.length()) {
var elem: JSONObject = arr[i] as JSONObject
var obj: JSONObject = elem
var thing = Thing(UUID.fromString(obj.getString("uuid")))
thing.summary = obj.getString("summary")
things += thing
println(thing)
}
adapter?.notifyDataSetChanged()
findViewById<ProgressBar>(R.id.ac_main_progress).visibility = View.GONE
}
private fun fillRecyclerView(things : List<Thing>) {
val recyclerView = findViewById<RecyclerView>(R.id.main_recycler)
val mainActivityRecyclerAdapter = MainActivityAdapter(this, things)
adapter = mainActivityRecyclerAdapter
recyclerView.adapter = mainActivityRecyclerAdapter
recyclerView.layoutManager = LinearLayoutManager(this)
}
}
```
you can use fuel-rxjava (https://github.com/kittinunf/fuel/tree/master/fuel-rxjava) for showing progress. Register an observer to notify the progress bar using rx-java
Related
I want to save data acquired from Volley, But lambda used in VolleyRequest function(which gets json data from server) blocks it.
How should I change local variable that is in outside of lambda?
Thanks in advance.
class ConDataforReturn( val title:String , val imgDataList: ArrayList<ConImgData>)
fun getConData(context: Context, idx : String):ConDataforReturn{
val params = HashMap<String,String>()
var cd = arrayListOf<ConImgData>()
var title =""
params.put("package_idx",idx)
Log.e("idx size",idx.length.toString())
VolleyRequest(context,params,"https://dccon.dcinside.com/index/package_detail") { response ->
val answer = JSONObject(response)
var json = answer.getJSONArray("detail")
title = answer.getJSONObject("info").getString("title")
Log.d("title",title)//Prints right data
for (i in 0..(json.length() - 1)) {
val v = json.getJSONObject(i)
cd.add(ConImgData(v.getString("title"), v.getString("ext"), v.getString("path")))
}
}
return ConDataforReturn(title,cd)//returns ConDataforReturn("",arrayListOf<ConImgData>())
}
Here the the code from were you are calling this method
getConData(this, "id") { condata ->
}
Now, your method look like this,
fun getConData(context: Context, idx : String, returnConData : (condata : ConDataforReturn) -> Unit){
val params = HashMap<String,String>()
var cd = arrayListOf<ConImgData>()
var title =""
params.put("package_idx",idx)
Log.e("idx size",idx.length.toString())
VolleyRequest(context,params,"https://dccon.dcinside.com/index/package_detail") { response ->
val answer = JSONObject(response)
var json = answer.getJSONArray("detail")
title = answer.getJSONObject("info").getString("title")
Log.d("title",title)//Prints right data
for (i in 0..(json.length() - 1)) {
val v = json.getJSONObject(i)
cd.add(ConImgData(v.getString("title"), v.getString("ext"), v.getString("path")))
}
returnConData(ConDataforReturn(title,cd)) //returns ConDataforReturn("",arrayListOf<ConImgData>())
}
}
This is my string:
{"array":[{"message":"test1","name":"test2","creation":"test3"},{"message":"test1","name":"test2","creation":"test3"}]}
And I want it get that array into a list of object in Kotlin app for Android.
I tried to do it using two examples from this site... So here is my code (res = that string):
val gson = Gson()
val obj = gson.fromJson(res, JsonObject::class.java)
val arr = obj.getAsJsonArray("array")
println(arr.toString())
val list1 : List<JThread> = gson.fromJson(arr, object : TypeToken<List<JThread>>() {}.type)
val list2 = gson.fromJson(arr, Array<JThread>::class.java).asList()
for (x in list1){
println(x.message)
}
for (x in list2){
println(x.message)
}
However I'm only getting null in x.message. I don't know what can go wrong.
I also tried changing arr to arr.toString() everywhere and that didn't work either.
Also JThread is:
object JThread {
var message: String? = null
var name: String? = null
var creation: String? = null }
This can be done without GSON or any other third party library:
#Throws(JSONException::class)
fun JSONObject.toMap(): Map<String, Any> {
val map = mutableMapOf<String, Any>()
val keysItr: Iterator<String> = this.keys()
while (keysItr.hasNext()) {
val key = keysItr.next()
var value: Any = this.get(key)
when (value) {
is JSONArray -> value = value.toList()
is JSONObject -> value = value.toMap()
}
map[key] = value
}
return map
}
#Throws(JSONException::class)
fun JSONArray.toList(): List<Any> {
val list = mutableListOf<Any>()
for (i in 0 until this.length()) {
var value: Any = this[i]
when (value) {
is JSONArray -> value = value.toList()
is JSONObject -> value = value.toMap()
}
list.add(value)
}
return list
}
Usage to convert JSONArray to List:
val jsonArray = JSONArray(jsonArrStr)
val list = jsonArray.toList()
Usage to convert JSONObject to Map:
val jsonObject = JSONObject(jsonObjStr)
val map = jsonObject.toMap()
More info is here
Use this code:
import com.google.gson.annotations.SerializedName
import com.google.gson.Gson
data class Array(
#SerializedName("message")
var message: String,
#SerializedName("name")
var name: String,
#SerializedName("creation")
var creation: String
)
data class Example(
#SerializedName("array")
var array: List<Array>? = null
)
private fun fromJson(json:String):Example{
return Gson().fromJson<Example>(json, Example::class.java)
}
PS: I made it with this site:http://www.jsonschema2pojo.org/
So I get this error even when Debugging on a Connected Device.
My App reads a JSON File via an HTTP connection stores it and notifies all Observers to get their new data. One of the Observers is a Plotter using the grapView Lib.
As I searched for some Solutions I ended up to implement the JSON Parser as an AsyncTask.
But the Same error still occurs.
My Question now is: What else can I do so Main Thread blocked?
Main:
class MainScreen : AppCompatActivity() {
//define variables
private lateinit var start: Button
private lateinit var graphView: GraphView
private lateinit var engineTempText: TextView
private lateinit var speedText: TextView
private val timer = Timer("schedule", true)
private var started = false
//find GUI fields/buttons
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_screen)
start = findViewById(R.id.startButton)
graphView = findViewById(R.id.graphView)
engineTempText = findViewById(R.id.engineTempText)
speedText = findViewById(R.id.speedText)
//Register Observers at DataWarehouse
val engineTempObserver = EngineTempObserver(engineTempText)
val speedObserver = SpeedObserver(speedText)
val plotter = Plotter(graphView)
//Wait till Button is pressed to start
start.setOnClickListener(View.OnClickListener {
//end
if (started) {
timer.cancel()
timer.purge()
started = false
}
//start
else {
started = true
val timer = Timer("schedule", true)
timer.scheduleAtFixedRate(500,500) {
Handler(Looper.getMainLooper()).post {
//Start
start()
}
}
}
})
}
//start monitoring
private fun start() {
val jsonParser = JsonParser()
jsonParser.execute()
}
}
JSON Parser:
class JsonParser : AsyncTask<Void, Void, JsonObject>() {
//connect to URL get Json File Data
override fun doInBackground(vararg params: Void): JsonObject {
val response = URL("https://api.myjson.com/bins/14oooa").readText()
//http://10.0.2.2:80/test.json
//create Parser
val parser = Parser()
val stringBuilder = StringBuilder(response)
//Read Json File + get Data
val jsonObject: JsonObject = parser.parse(stringBuilder) as JsonObject
return jsonObject
}
//Parse Json File extract Data and store them in DataWarehouse
override fun onPostExecute(jsonObject: JsonObject) {
//Arrays to Store Coordinates saved in Cone class
val blueConeArray = arrayListOf<Cone>()
val yellowConeArray = arrayListOf<Cone>()
//Get Cones from JsonObject
val blueCoordinates: JsonArray<JsonObject>? = jsonObject.array("BlueCoordinates")
val yellowCoordinates: JsonArray<JsonObject>? = jsonObject.array("YellowCoordinates")
//Store Blue Cones
for (coordinate in blueCoordinates!!) {
val x_blue = coordinate.double("x")
val y_blue = coordinate.double("y")
val blueCone = BlueCone(x_blue!!, y_blue!!)
blueConeArray.add(blueCone)
}
//Store Yellow Cones
for (coordinate in yellowCoordinates!!) {
val x_yellow = coordinate.double("x")
val y_yellow = coordinate.double("y")
val yellowCone = YellowCone(x_yellow!!, y_yellow!!)
yellowConeArray.add(yellowCone)
}
//Store everything in Data Warehouse
DataWarehouse.setValues(
newEngineTemp = jsonObject.string("engineTemp"),
newSpeed = jsonObject.string("speed"),
newBlueCones = blueConeArray,
newYellowCones = yellowConeArray
)
blueConeArray.clear()
yellowConeArray.clear()
}
}
Plotter:
class Plotter(graphView: GraphView) : Observer {
private var graphView = graphView
private lateinit var sortedBlueCones: MutableList<Cone>
private lateinit var sortedYellowCones: MutableList<Cone>
private var blueConeArrayList = ArrayList<DataPoint>()
private var yellowConeArrayList = ArrayList<DataPoint>()
private var blueLines = LineGraphSeries<DataPoint>()
private var yellowLines = LineGraphSeries<DataPoint>()
private var blueIterator = 0
private var yellowIterator = 0
init {
//First Values so resetData works
yellowLines.appendData((DataPoint(0.toDouble(), 0.toDouble())), true, 1000)
blueLines.appendData((DataPoint(0.toDouble(), 0.toDouble())), true, 1000)
register()
}
//Register at Data Warehouse
override fun register() {
DataWarehouse.registerObserver(this)
}
//Get new Cones from Data Warehouse and sort them by X Values
override fun update() {
sortedBlueCones = DataWarehouse.getBlueCones().sortedWith(compareBy({ it.xCoordinate })) as MutableList<Cone>
sortedYellowCones = DataWarehouse.getYellowCones().sortedWith(compareBy({ it.xCoordinate })) as MutableList<Cone>
draw()
}
//Draw Line Graph and Point Graph
private fun draw() {
//Blue Cones
for (i in sortedBlueCones) {
var x: Double = sortedBlueCones.get(blueIterator).xCoordinate
var y: Double = sortedBlueCones.get(blueIterator).yCoordinate
var dataPoint = DataPoint(x, y)
blueConeArrayList.add(dataPoint)
val blueConeArray = arrayOfNulls<DataPoint>(blueConeArrayList.size)
blueConeArrayList.toArray(blueConeArray)
blueLines.resetData(blueConeArray)
blueIterator++
}
//Yellow Cones
for (i in sortedYellowCones) {
var x: Double = sortedYellowCones.get(yellowIterator).xCoordinate
var y: Double = sortedYellowCones.get(yellowIterator).yCoordinate
var dataPoint = DataPoint(x, y)
yellowConeArrayList.add(dataPoint)
val yellowConeArray = arrayOfNulls<DataPoint>(yellowConeArrayList.size)
yellowConeArrayList.toArray(yellowConeArray)
yellowLines.resetData(yellowConeArray)
yellowIterator++
}
//Set Values of Lines
blueLines.setColor(Color.BLUE)
blueLines.setDrawDataPoints(true)
blueLines.setDataPointsRadius(10.toFloat())
yellowLines.setColor(Color.YELLOW)
yellowLines.setDrawDataPoints(true)
yellowLines.setDataPointsRadius(10.toFloat())
//Draw
graphView.addSeries(blueLines)
graphView.addSeries(yellowLines)
blueIterator = 0
yellowIterator = 0
blueConeArrayList.clear()
yellowConeArrayList.clear()
}
}
onPostExecute(jsonObject: JsonObject) is called on the main thread, it looks like you're doing a lot of work there (for loops and db saving). Try to do that in the background and then return only what needs to be done in the main thread.
Other than that those errors (which are actually warnings) can mostly be ignored, especially if working on an older phone or an emulator. If it doesn't actually cause any visible lag you should be fine as long as the lag time isn't major and constantly happening.
I'm using Google Books API for searching books. But problem is that when I want my ListView to be populated with books, I get an error. This error is pointed onPostExecute, but I can't figured out what's the problem.
kotlin.KotlinNullPointerException
at com.example.claudiu.reader.Fragments.ISBNFragment$FetchBookTask.onPostExecute(ISBNFragment.kt:137)
at com.example.claudiu.reader.Fragments.ISBNFragment$FetchBookTask.onPostExecute(ISBNFragment.kt:56)
Here is where I set my adapter :
override fun onPostExecute(books: List<Book>?) {
if (books != null) {
adapter!!.clear()
for (book in books) {
adapter!!.add(book)
}
}
}
And here is all code where I'm parsing the JSON:
#Throws(JSONException::class)
private fun getBookDataFromJson(booksJsonStr: String?): List<Book> {
val books = ArrayList<Book>()
val API_RESULT_ITEMS_ARRAY = "items"
val API_VOLUME_INFO = "volumeInfo"
val API_BOOK_TITLE = "title"
val API_BOOK_IMAGE_LINKS = "imageLinks"
val API_BOOK_SMALL_THUMBNAIL = "smallThumbnail"
val API_BOOK_AUTHORS_ARRAY = "authors"
val booksJson = JSONObject(booksJsonStr)
val itemsArray = booksJson.getJSONArray(API_RESULT_ITEMS_ARRAY)
for (i in 0 until itemsArray.length()) {
val item = itemsArray.getJSONObject(i)
val volumeInfo = item.getJSONObject(API_VOLUME_INFO)
val bookTitle = volumeInfo.getString(API_BOOK_TITLE)
val imageLinksSB = StringBuilder()
if (!volumeInfo.isNull(API_BOOK_IMAGE_LINKS)) {
val imageLinks = volumeInfo.getJSONObject(API_BOOK_IMAGE_LINKS)
imageLinksSB.append(imageLinks.getString(API_BOOK_SMALL_THUMBNAIL))
} else {
imageLinksSB.append("-1")
}
val bookImageLink = imageLinksSB.toString()
val authorsSB = StringBuilder()
if (!volumeInfo.isNull(API_BOOK_AUTHORS_ARRAY)) {
val authorsArray = volumeInfo.getJSONArray(API_BOOK_AUTHORS_ARRAY)
for (k in 0 until authorsArray.length()) {
authorsSB.append(authorsArray.getString(k))
authorsSB.append(getString(R.string.comma))
}
} else {
authorsSB.append(getString(R.string.unknown_error))
}
val bookAuthors = authorsSB.toString()
books.add(Book(bookTitle, bookAuthors, bookImageLink))
}
Log.d(LOG_TAG, "BOOKS : $books")
return books
}
I couldn't find any thing to help me and I have no idea what should I do.
Your problem is the place where you declare your adapter, I had this problem because declaration of my adapter was in the wrong place.
You should move adapter declaration from onCreatView into onViewCreated and everything will work fine.
Hope it helps !
I have the following methods on my code:
fun saveArticles(data: JSONArray) {
var active = String()
for (i in 0..data.length().minus(1)) // create string
val articles = Select.from(Article::class.java).list()
val iterator = articles.iterator()
while (iterator.hasNext()) {
val article = iterator.next() as Article
if (!active.contains(Regex(article.id.toString()))) article.delete()
}
}
fun saveDossiers(data: JSONArray) {
var active = String()
for (i in 0..data.length().minus(1)) // create string
val dossiers = Select.from(Dossier::class.java).list()
val iterator = dossiers.iterator()
while (iterator.hasNext()) {
val dossier = iterator.next() as Dossier
if (!active.contains(Regex(dossier.id.toString()))) dossier.delete()
}
}
fun saveVideos(data: JSONArray) {
var active = String()
for (i in 0..data.length().minus(1)) // create string
val videos = Select.from(Video::class.java).list()
val iterator = videos.iterator()
while (iterator.hasNext()) {
val video = iterator.next() as Video
if (!active.contains(Regex(video.id.toString()))) video.delete()
}
}
As you can see, all the methods do exactly the same thing. The only difference is the Class type of the object I'm working at the moment. Can I somehow create a single method with a parameter of Class type, and depending of the type change the class I need to work? Something like this:
fun saveVideos(data: JSONArray, type: Class) {
var active = String()
for (i in 0..data.length().minus(1)) // create string
val list = Select.from(type).list()
val iterator = list.iterator()
while (iterator.hasNext()) {
val item = iterator.next() as type
if (!active.contains(Regex((item as type).id.toString()))) item.delete()
}
}
You need to extract an interface and use a reified generic.
interface Blabla {
fun delete()
val id: Int
}
inline fun <reified T : Blabla>saveVideos(data: JSONArray) {
var active = String()
for (i in 0..data.length().minus(1)) // create string
val list = Select.from(T::class.java).list()
val iterator = list.iterator()
while (iterator.hasNext()) {
val item = iterator.next() as T
if (Regex(item.id.toString()) !in active) item.delete()
}
}
This should work.
Also, I highly recommend you to use the Kotlin collection library, like this.
inline fun <reified T : Blabla>saveVideos(data: JSONArray) {
val active = ""
for (i in 0 until data.length()) {} // create string
val list = Select.from(T::class.java).list()
list
.map { it as T }
.filter { Regex(it.id.toString()) !in active }
.forEach { it.delete() }
}
And you can even replace forEach { it.delete() } with forEach(T::delete)