Kotlin Coroutines, retrofit-2.6.1, Network response null

1.5k Views Asked by At

I am going through reso-coder Weather app tutorial. Most of the things changed with the passage of time, so is apixu weather website.
Now it's the time of Retrofit 2.6.1 which means kotlin coroutines
The problem is that i am getting everything Null in network Response
I have go through all data classes with SerializedName, everything seems pretty fine, still can't get the problem..
BTW i'm not using ViewModel right now just directly hoping into the fragment textView

interface ApixuWeatherApiService {


 @GET("current")
    suspend fun getCurrentWeather(
        @Query("query") location: String,
        @Query("lang") languageCode: String = "en"
    ): CurrentWeatherResponse

    //to handle this above interface we need companion object

    companion object WeatherAPis {
        private val requestInterceptor = Interceptor { chain ->
            val url = chain.request()
                .url().newBuilder().addQueryParameter("access_key", API_KEY)
                .build()

            val request = chain.request().newBuilder().url(url).build()
            chain.proceed(request)
        }
        private val okHTTPClient = OkHttpClient.Builder().addInterceptor(requestInterceptor).build()
        private fun retroFit(): Retrofit = Retrofit
            .Builder()
            .client(okHTTPClient)
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        val weatherApi: ApixuWeatherApiService =
            retroFit().create(ApixuWeatherApiService::class.java)
    }
}

Fragment Class


class CurrentWeatherFragment : Fragment() {


    private lateinit var viewModel: CurrentWeatherViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.current_weather_fragment, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(CurrentWeatherViewModel::class.java)
        CoroutineScope(IO).launch {
            widCOntext()
        }

    }


    private suspend fun widCOntext() {
        val apiService = ApixuWeatherApiService.weatherApi
            withContext(Main) {
                val currentWeatherResponse =
                    withContext(IO) {
                        apiService.getCurrentWeather(
                            "London"
                        )
                    }
                txtView.text = currentWeatherResponse.toString()
            }

    }
}

I have used plugin to convert JSON to kotlin file to get these
Four data classes
Current

data class Current(
    @SerializedName("observation_time")
    val observationTime: String,
    val temperature: Int,
    @SerializedName("weather_code")
    val weatherCode: Int,
    @SerializedName("weather_icons")
    val weatherIcons: List<String>,
    @SerializedName("weather_descriptions")
    val weatherDescriptions: List<String>,
    @SerializedName("wind_speed")
    val windSpeed: Int,
    @SerializedName("wind_degree")
    val windDegree: Int,
    @SerializedName("wind_dir")
    val windDir: String,
    val pressure: Int,
    val precip: Int,
    val humidity: Int,
    val cloudcover: Int,
    val feelslike: Int,
    @SerializedName("uv_index")
    val uvIndex: Int,
    val visibility: Int,
    @SerializedName("is_day")
    val isDay: String
)

CurrentWeatherResponse

data class CurrentWeatherResponse(
    val request: Request,
    val location: Location,
    val current: Current
)

Location

data class Location(
    val name: String,
    val country: String,
    val region: String,
    val lat: String,
    val lon: String,
    @SerializedName("timezone_id")
    val timezoneId: String,
    val localtime: String,
    @SerializedName("localtime_epoch")
    val localtimeEpoch: Int,
    @SerializedName("utc_offset")
    val utcOffset: String
)

Request

data class Request(
    val type: String,
    val query: String,
    val language: String,
    val unit: String
)
1

There are 1 best solutions below

0
On

Issue Solved : I hope this gonna help someone
Here what i did
Whenever i stuck somewhere, i make new project and try to work on that specific thing.
Now what i did over here which i think resolved my problem.
I have used singleton pattern, I have made Singleton instance of ApiService Interface, before singleton it was showing response 200 but output was null as well and now just by making singleton it resolved my Problem and now i am getting desired output
Here is code:

object RetrofitRequest {

    private val interceptor = Interceptor {chain ->
        val url = chain.request()
            .url()
            .newBuilder()
            .addQueryParameter("access_key/key", yourApiKey)
            .build()

        val request = chain.request().newBuilder().url(url).build()

        return@Interceptor chain.proceed(request)
    }

    private val okHttpClient = OkHttpClient
        .Builder()
        .addInterceptor(interceptor)
        .build()

    @Volatile
    private var instance : ApiService? = null
    fun getInstance() : ApiService = instance ?: synchronized(this) {
        instance ?: fullResponse().also {
            instance = it
        }
    }

    private fun retrofitBuild() : Retrofit =
        Retrofit.Builder()
           .client(okHttpClient)
           .baseUrl(BASE_URL)
           .addConverterFactory(GsonConverterFactory.create())
            .build()


    private fun fullResponse(): ApiService {
        return retrofitBuild().create(ApiService::class.java)
    }

}

Specifically this is the part i am talking about

@Volatile
    private var instance : ApiService? = null
    fun getInstance() : ApiService = instance ?: synchronized(this) {
        instance ?: fullResponse().also {
            instance = it
        }
    }

Now what i think what is happening over here:
Before Singleton the response was successful but instance was not visible for other threads. That is why it was null. As i am using coroutines it might be running from different thread, after making the singleton @volatile which makes the singleton visible for all threads. When the program try to run from different thread and @Volatile has capability to access to all threads,which made program execute successfully without null