I used MockWebServer like below code. But MockWebServer not finish. The test is infinitely progressing. What I missed?
Result:
Jul 02, 2018 7:30:59 PM okhttp3.mockwebserver.MockWebServer$2 execute
정보: MockWebServer[49728] starting to accept connections
interface Webservice {
#GET("users/{user}/missions")
fun getMissions(#Path("user") user: String): Call<Missions>
}
class MockWebTest {
private lateinit var webservice: Webservice
#Before
fun setUp() {
val baseUrl = MyURL.API_DOMAIN
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.SECONDS)
.readTimeout(2, TimeUnit.SECONDS)
.writeTimeout(2, TimeUnit.SECONDS)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
webservice = retrofit.create(Webservice::class.java)
}
#Test
fun test3() {
val server = MockWebServer()
server.start()
val missionsJson = """
{
"message": "walk"
}
""".trimIndent()
server.enqueue(MockResponse().setBody(missionsJson).throttleBody(1024, 1, TimeUnit.SECONDS))
val call = webservice.getMissions("1")
val entity = call.execute().body()!!
assertEquals("walk", entity.message)
val request1 = server.takeRequest()
assertEquals("GET", request.method)
assertEquals("/users/1/missions", request.path)
server.shutdown()
}
}
I checked this repo GithubBrowserSample Google sample. Retrofit baseUrl needs to be set by MockWebServer#url(String)
Like this, not my URL.
val retrofit = Retrofit.Builder()
.baseUrl(server.url("/"))
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
Related
I don't know how to get data from nested Json
{
"results":[
{
"id":1,
"name":"Rick Sanchez",
"status":"Alive",
"species":"Human",
"type":"",
"gender":"Male",
Json looks like above, i want to get access to name variable.
My code:
Data class:
data class Movie(
#Json(name = "results") val results: List<MovieDetail>
)
data class MovieDetail(
#Json(name = "name") val name: String
)
ApiService:
private const val BASE_URL = "https://rickandmortyapi.com/api/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface MovieApiService {
#GET("character")
suspend fun getMovies(): List<Movie>
}
object MovieApi {
val retrofitService : MovieApiService by lazy {
retrofit.create(MovieApiService::class.java)
}
}
And ViewModel:
private val _status = MutableLiveData<String>()
val status: LiveData<String> = _status
init {
getMovies()
}
private fun getMovies() {
viewModelScope.launch {
val listResult = MovieApi.retrofitService.getMovies()
_status.value = "Success: ${listResult.size} names retrieved"
}
}
For plain Json there is no problem but i don't know how to get access to this nested variables, i think that i have to use "results" variable from data class but i don't know where and how.
During running app i've got error: Expected BEGIN_ARRAY but was BEGIN_OBJECT at path $
You should change
#GET("character")
suspend fun getMovies(): List<Movie>
To:
#GET("character")
suspend fun getMovies(): Movie
You are receiving object and not list of objects
By okhttp log I know I get response from remote server. But when I try to parse response I receive null! How to correct parse response from the server to get data?
My Data object:
#Parcelize
data class DataItem(
#field:SerializedName("name")
val name: String? = null,
) : Parcelable
MY Api:
interface Api {
#GET("v1/media/list/image")
suspend fun getImage(): DataItem
}
My Retrofit object:
private val httpClient = OkHttpClient
.Builder()
.protocols(listOf(Protocol.HTTP_1_1))
.addInterceptor(BearerTokenInterceptor(TOKEN))
.addInterceptor(logInterceptor())
//get api by retrofit
private val api: TVApi by lazy {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build()
retrofit.create(TVApi::class.java)
}
private fun logInterceptor() : HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
return interceptor
}
My try to parse the response:
private val scope = CoroutineScope(Dispatchers.IO)
private fun getNetworkResponse() {
scope.launch {
try {
Log.d(MY_TAG, "api: ${api.getVideo()}")
} catch (e: IOException) {
Log.e(MY_TAG, "IOException: $e")
} catch (e: HttpException) {
Log.e(MY_TAG, "HttpException: $e")
}
}
}
OKhttp log:
{"code":200,"message":"Success","data":[{"name"....}
My Log:
api: DataItem(extension=null, size=null, name=null, url=null)
I am new to Kotlin and Android Studio and I am tasked with creating a tab layout with 3 tabs and with view pager and fragments. Each fragment will have a list of songs from different genres: Rock, Pop, and Classic. I am given APIs and I created json files from these APIs. I created the Xmls and setup the layouts, adapters, data classes, and everything. Now I'm trying to get the data from the json file into the fragment using recycler view and card view. Here is the error I am getting:
Type mismatch: inferred type is MainActivity but List was expected and Classifier 'MyAdapter' does not have a companion object, and thus must be initialized here. Here is my code:
//Main Activity
package com.example.itunes_mysia
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException
import java.nio.charset.Charset
var artist_name: ArrayList<String> = ArrayList()
var track_name: ArrayList<String> = ArrayList()
var track_price: ArrayList<String> = ArrayList()
class MainActivity : AppCompatActivity() {
private lateinit var itunesToolbar: androidx.appcompat.widget.Toolbar
private lateinit var itunesTabs: TabLayout
private lateinit var itunesTitleText: TextView
private lateinit var itunesViewPager: ViewPager
private lateinit var itunesPagerAdapters: PagerAdapters
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "KotlinApp"
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
val linearLayoutManager = LinearLayoutManager(applicationContext)
recyclerView.layoutManager = linearLayoutManager
try {
val obj = JSONObject(loadJSONFromAsset())
val rockArray = obj.getJSONArray("Rock")
for (i in 0 until rockArray.length()) {
val userDetail = rockArray.getJSONObject(i)
artist_name.add(userDetail.getString("Artist"))
track_name.add(userDetail.getString("Track Name"))
track_price.add(userDetail.getString("Track Price"))
}
}
catch (e: JSONException) {
e.printStackTrace()
}
val myAdapter = MyAdapter(this#MainActivity)
recyclerView.adapter = MyAdapter
// Set find ID
itunesToolbar = findViewById(R.id.itunesToolbar)
itunesTitleText = findViewById(R.id.itunesTitleText)
itunesTabs = findViewById(R.id.itunesTabs)
itunesViewPager = findViewById(R.id.itunesViewPager)
itunesPagerAdapters = PagerAdapters(supportFragmentManager)
// Set Toolbar
itunesToolbar.setTitle("")
itunesTitleText.setText(getString(R.string.itunes))
setSupportActionBar(findViewById(R.id.itunesToolbar))
// Set Fragment List
itunesPagerAdapters.addfragment(RockFragment(), "Rock")
itunesPagerAdapters.addfragment(ClassicFFragment(), "Classic")
itunesPagerAdapters.addfragment(PopFragment(), "Pop")
// Set View Pager Adapter
itunesViewPager.adapter = itunesPagerAdapters
// Set Tab Layout with View Pager Adapter
itunesTabs.setupWithViewPager(itunesViewPager)
// Set Icons
itunesTabs.getTabAt(0)!!.setIcon(R.mipmap.music1)
itunesTabs.getTabAt(1)!!.setIcon(R.mipmap.music2)
itunesTabs.getTabAt(2)!!.setIcon(R.mipmap.music3)
}
class ViewPagerOnTabSelectedListener(private val viewPager: ViewPager) :
OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
viewPager.currentItem = tab.position
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
// No-op
}
override fun onTabReselected(tab: TabLayout.Tab?) {
// No-op
}
}
private fun loadJSONFromAsset(): String {
val json: String?
try {
val inputStream = assets.open("rock.json")
val size = inputStream.available()
val buffer = ByteArray(size)
val charset: Charset = Charsets.UTF_8
inputStream.read(buffer)
inputStream.close()
json = String(buffer, charset)
}
catch (ex: IOException) {
ex.printStackTrace()
return ""
}
return json
}
}
//MyAdapter.kt (for recycler view)
package com.example.itunes_mysia
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class MyAdapter(private val Rock: List<Rock>) :
RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
MyViewHolder {
val itemView= LayoutInflater.from(parent.context).inflate(R.layout.row, parent,
false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = Rock[position]
holder.artistName.text = currentItem.artistName
holder.trackName.text = currentItem.trackName
holder.trackPrice.text = currentItem.trackPrice
}
override fun getItemCount(): Int {
return Rock.size
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val artistName: TextView = itemView.findViewById(R.id.art_name)
val trackName: TextView = itemView.findViewById(R.id.trackName)
val trackPrice: TextView = itemView.findViewById(R.id.trackPrice)
}
}
Hello i am having big problems here this is my first time using retrofit and i am new to kotlin, i don't know why this piece of code is not working.
This is my retrofit client
private const val BASE_URL = "https://89a6t4gtke.execute-api.eu-west-3.amazonaws.com/Prod/"
val instance : IApi by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(IApi::class.java)
}
This is my response class
data class DefaultResponse(val message: String) {}
This is my response:
{
"message": "GET"
}
Interface
interface IApi {
#GET("hello")
fun returnHello():Call<DefaultResponse>
}
The call
toast_button.setOnClickListener{
RetrofitClient.instance
.returnHello()
.enqueue(object: Callback<DefaultResponse>{
override fun onFailure(call: retrofit2.Call<DefaultResponse>, t: Throwable) {
Toast.makeText(context,t.message + "bla",Toast.LENGTH_SHORT).show()
}
override fun onResponse(call: retrofit2.Call<DefaultResponse>,response: Response<DefaultResponse>) {
Toast.makeText(context, "empty?",Toast.LENGTH_SHORT)
}
})
}
No toast messages show, i had an error show once when i made my api just return a string and not a json string but now there is no error as i fixed it.
You should change to your Retrofit Client as following code;
class RetrofitClient {
companion object {
fun getClient(): Retrofit {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://89a6t4gtke.execute-api.eu-west-3.amazonaws.com/Prod/")
.build()
}
}
}
Then add this in your activity ;
val service = RetrofitClient.getClient().create(IApi::class.java)
val call = service.returnHello()
val resp: DefaultResponse? = call.clone().execute().body()
if (resp != null) {
println("your response is -> $resp")
}
I have api that return json:
{"countries":[{"id":1,"name":"Australia"},{"id":2,"name":"Austria"}, ... ]}
I write model class (Kotlin lang)
data class Country(val id: Int, val name: String)
And I want do request using retorift that returning List < Models.Country >, from "countries" field in json
I write next:
interface DictService {
#GET("/json/countries")
public fun countries(): Observable<List<Models.Country>>
companion object {
fun create() : DictService {
val gsonBuilder = GsonBuilder()
val listType = object : TypeToken<List<Models.Country>>(){}.type
gsonBuilder.registerTypeAdapter(listType, CountriesDeserializer)
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
val service = Retrofit.Builder()
.baseUrl("...")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
.build()
return service.create(DictService::class.java)
}
}
object CountriesDeserializer : JsonDeserializer<List<Models.Country>> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): List<Models.Country>? {
val res = ArrayList<Models.Country>()
if(json!=null) {
val countries = json.asJsonObject.get("countries")
if (countries.isJsonArray()) {
for (elem: JsonElement in countries.asJsonArray) {
res.add(Gson().fromJson(elem, Models.Country::class.java))
}
}
}
return null;
}
}
}
But I get error:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
CountriesDeserializer code dont execute even!
What they want from me?
Maybe I need write my own TypeAdapterFactory?
I dont want use model class like
class Countries {
public List<Country> countries;
}
If your intention is to simplify the interface and hide the intermediate wrapper object I guess the simplest thing to do is to add an extension method to the DictService like so:
interface DictService {
#GET("/json/countries")
fun _countries(): Observable<Countries>
}
fun DictService.countries() = _countries().map { it.countries }
data class Countries(val countries: List<Country> = listOf())
Which can then be used as follows:
val countries:Observable<List<Country>> = dictService.countries()
I found the way:
object CountriesTypeFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson?, type: TypeToken<T>?): TypeAdapter<T>? {
val delegate = gson?.getDelegateAdapter(this, type)
val elementAdapter = gson?.getAdapter(JsonElement::class.java)
return object : TypeAdapter<T>() {
#Throws(IOException::class)
override fun write(outjs: JsonWriter, value: T) {
delegate?.write(outjs, value)
}
#Throws(IOException::class)
override fun read(injs: JsonReader): T {
var jsonElement = elementAdapter!!.read(injs)
if (jsonElement.isJsonObject) {
val jsonObject = jsonElement.asJsonObject
if (jsonObject.has("countries") && jsonObject.get("countries").isJsonArray) {
jsonElement = jsonObject.get("countries")
}
}
return delegate!!.fromJsonTree(jsonElement)
}
}.nullSafe()
}
}
But it is very complex decision, I think, for such problem.
Are there another one simpler way?
Another one:
I found bug in my initial code from start meassage!!!
It works fine if replace List by ArrayList!
I would use Jackson for this task.
Try this https://github.com/FasterXML/jackson-module-kotlin
val mapper = jacksonObjectMapper()
data class Country(val id: Int, val name: String)
// USAGE:
val country = mapper.readValue<Country>(jsonString)