How to change retrofit #GET programatically - retrofit2

I have a app where I use youtube api and make a get request using retrofit, now I want to get a list of videos for a specific keyword, but for that I have to use a different get req everytime so how can I change the get request programatically
Code for calling API
private fun getVideosList() {
val videos = RetrofitInstance.youtubeapi.getYoutubeVideos()
videos.enqueue(object : Callback<YoutubeAPIData?> {
override fun onResponse(call: Call<YoutubeAPIData?>, response: Response<YoutubeAPIData?>) {
val videosList = response.body()?.items
if (videosList != null) {
for(video in videosList) {
Log.d("title", video.snippet.title)
}
}
}
override fun onFailure(call: Call<YoutubeAPIData?>, t: Throwable) {
Toast.makeText(applicationContext, "Unable to fetch results!", Toast.LENGTH_SHORT).show()
Log.d("APIError",t.toString())
}
})
}
Retrofit Instance
object RetrofitInstance {
const val BASE_URL = "https://www.googleapis.com/youtube/v3/"
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val youtubeapi: YoutubeListApi by lazy {
retrofit.create(YoutubeListApi::class.java)
}}
Code for API interface
interface YoutubeListApi {
#GET("search?part=snippet&q=eminem&key=*my_key*")
fun getYoutubeVideos(): Call<YoutubeAPIData>}
Now what I want is to change the #GET("search?part=snippet&q=eminem&key=my_key") in the api interface so that if the keyword is eminem it should be search?part=snippet&q=eminem&key=my_key
and if keyword is dog it should be search?part=snippet&q=dogkey=my_key

Why not use #Query from retrofit?
You could redefine your interface to:
interface YoutubeListApi {
#GET("search")
fun getYoutubeVideos(
#Query("part") part: String,
#Query("q") query: String,
#Query("key") key: String,
): Call<YoutubeAPIData>
}
and then you can call it like getYoutubeVideos("snippets", "eminem", "your key") or getYoutubeVideos("snippets", "dog", "your key").
I think you can even hardcode some values in the URL if you want, but honestly I think you can just use kotlin default values:
interface YoutubeListApi {
#GET("search")
fun getYoutubeVideos(
#Query("q") query: String,
#Query("part") part: String = "snippet",
#Query("key") key: String = "your key",
): Call<YoutubeAPIData>
}
and just pass the query getYoutubeVideos("eminem"). I haven't double checked this, but I think it could work.

Related

How is the best way to map UsbConfiguration object in Kotlin?

Hello actually I'm developing an app with the use of USB, I need return the UsbConfiguration from this map structure but the compiler error is:
Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly
This is the call from the method:
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun getConfiguration(index:Int?):
Map<String, Any> {
val device = usbDevice
val configuration: UsbConfiguration = device!!.getConfiguration(index!!)
val map = configuration.toMap() + ("index" to index)
return map
}
And this is the part with the problem specific in getInterface(it).toMap():
fun UsbConfiguration.toMap() = mapOf(
"id" to id,
"interfaces" to List(interfaceCount) {
getInterface(it).toMap() }
)
The code from library to the method getInterface(int index)
#NonNull
public UsbInterface getInterface(int index) {
throw new RuntimeException("Stub!");
}

Unresolved reference : retrofit while creating retrofit instance

I am working on an android app which request Mars photos ans use it to display it on screen.
To make an request.And trying to use A public Api object that exposes the lazy-initialized Retrofit service.
below is source code with error
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory
import retrofit2.http.GET
class MarsApiService {
public val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(Companion.BASE_URL)
.build()
interface MarsApiService{
#GET("photos")
fun getPhotos(): String
}
object MarsApi {
val retrofitService: MarsApiService by lazy { retrofit.create(MarsApiService::class.java) }
}
companion object {
private const val BASE_URL = "https://android-kotlin-fun-mars-server.appspot.com"
}
}
17th line the code inside object MarsApi pops up errors Unresolved reference : retrofit. The call to create() function on a Retrofit object is expensive and the app needs only one instance of Retrofit API service. So, i exposed the service to the rest of the app using object declaration.
What I have tried:
The code is working if i bring code inside object MarsApi out but doing so may result in multiple instance of retrofit.
code1
interface MarsApiService {
#GET("photos")
suspend fun getPhotos(): String
companion object {
private const val BASE_URL = "https://android-kotlin-fun-mars-server.appspot.com"
val marsApiService: MarsApiService by lazy {
Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(BASE_URL)
.build().create(MarsApiService::class.java)
}
}
}
code2
private const val BASE_URL = "https://android-kotlin-fun-mars-server.appspot.com/"
private val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(BASE_URL)
.build()
interface MarsApiService {
#GET("photos")
suspend fun getPhotos(): String
}
object MarsApi {
val marsApiService: MarsApiService by lazy { retrofit.create(MarsApiService::class.java) }
}
run
fun main() = runBlocking {
val rs = marsApiService.getPhotos()
println(rs)
}
In your code, retrofit by lazy reference not resolved cause of retrofit is property of class, move the retrofit globally like using (Companion) object or like code2 (highest hierarchy?)
Hope this link
make you clear the difference between object and companion object and when to use them. Happy learning
It was the same problem that I had.
I solved it just excluding the class MarsApiService
I didnĀ“t use companion object too.
The right code is below
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory
import retrofit2.http.GET
private const val BASE_URL = "https://android-kotlin-fun-mars-server.appspot.com"
public val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(Companion.BASE_URL)
.build()
interface MarsApiService{
#GET("photos")
fun getPhotos(): String
}
object MarsApi {
val retrofitService: MarsApiService by lazy {
retrofit.create(MarsApiService::class.java)
}
}

I face this error java.lang.IllegalArgumentException: Could not locate call adapter for class java.lang.Object

I am new with MVVM in kotlin, I want to fetch some data using retrofit and show this in textview but I can't fetch this. In this app first time, I am using the android jetpack component. I tried a lot of times but I can't solve this error. My code in below
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val repository = Repository()
val viewModelFactory = MainViewModelFactory(repository)
viewModel = ViewModelProvider(this, viewModelFactory).get(MainViewModel::class.java)
viewModel.getEmployeeData()
viewModel.repoResponse.observe(this, Observer { response ->
if (response.isSuccessful) {
Log.d("Response", response.body()?.employee_id.toString())
Log.d("Response", response.body()?.employee_name.toString())
Log.d("Response", response.body()?.employee_age.toString())
Log.d("Response", response.body()?.employee_salary.toString())
name.text = response.body()?.employee_name!!
} else {
Log.d("Response", response.errorBody().toString())
name.text = response.code().toString()
}
})
}
MainViewModelFactory.kt
class MainViewModelFactory(private val repository: Repository):ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(repository) as T
}
MainViewModel.kt
class MainViewModel(private val repository: Repository):ViewModel() {
val repoResponse:MutableLiveData<Response<Employee>> = MutableLiveData()
fun getEmployeeData(){
viewModelScope.launch {
val response = repository.getEmployeeData()
repoResponse.value = response
}
}
ApiClient.kt
object ApiClient {
private val retrofit by lazy{
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val api:Api by lazy {
retrofit.create(Api::class.java)
}
Api.kt
public interface Api {
#GET("api/v1/employee/1")
suspend fun getEmployeeData():Response<Employee>
}
Repository.kt
class Repository {
suspend fun getEmployeeData():Response<Employee>{
return ApiClient.api.getEmployeeData()
}
Employee.kt
data class Employee(
#SerializedName("id")
var employee_id: Int,
#SerializedName("employee_name")
var employee_name: String,
#SerializedName("employee_age")
var employee_age: Int,
#SerializedName("employee_salary")
var employee_salary: Int
)
Constants.kt
class Constants {
companion object{
const val BASE_URL = "http://dummy.restapiexample.com"
}
Error image
How can i solve this? please help me. Thank you
Retrofit only supports suspend keyword since 2.6.4, so using 2.3.0 won't work.
You should update to a newer version of Retrofit. The current latest version at the time of writing is 2.9.0.

Room cannot verify the data integrity and Crashes on Empty Data

I am working on Android Application in Which I am getting specific Data from Room Database by specific path in the Storage. My App Got Crashes as It does not have Any Data in the Storage and the Logcat gives me this..
java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
at androidx.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:154)
at androidx.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:135)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:195)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:428)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:317)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:145)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:106)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at com.maximus.technologies.views.activities.scanneddatabase.TodoDaoScanned_Impl.getAllScan(TodoDaoScanned_Impl.java:152)
at com.maximus.technologies.views.fragments.scanhistorypackage.QRRetrievingScanClassPresenter$getAllDatFromDatabase$1.invokeSuspend(QRRetrievingScanClassPresenter.kt:29)
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)
The Above Error or crash Only occurs as the app dont have any data in Storage. But as I put a Data the Crash Problem Get Resolved.
I am not able to Understand what the Problem actually is...
Here is My Room Database Class..
#Database(
entities = [TodoEntity::class,TodoEntityScanned::class],
version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun TodoDao(): TodoDao
abstract fun TodoDaoScanned(): TodoDaoScanned
object DatabaseBuilder {
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
if (INSTANCE == null) {
synchronized(AppDatabase::class) {
INSTANCE = buildRoomDB(context)
}
}
return INSTANCE!!
}
private fun buildRoomDB(context: Context) =
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"mindorks-example-coroutines"
).build()
}
}
Room Database Retrieving Interface where app Crashes on getall()
override fun getAllDatFromDatabase(appDatabasescanned: AppDatabase) {
var list = listOf<TodoEntityScanned>()
try {
GlobalScope.launch(Dispatchers.Default) {
list = appDatabasescanned.TodoDaoScanned().getAllScan()
Log.d("hello","hello")
mView.showAllData(list)
}
}
catch (e:Exception){
Log.d("get hello",e.toString())
}
}
The getAll lies in Dao Class
interface TodoDao {
#Query("SELECT * FROM tablefilepaths")
fun getAll(): List<TodoEntity>
#Query("SELECT * FROM tablefilepaths WHERE imagespath LIKE :title")
fun findByTitle(title: String): TodoEntity
#Insert
fun insertpaths(todo: TodoEntity)
#Delete
fun deletepaths(todo: TodoEntity)
#Query("DELETE FROM tablefilepaths WHERE id = :noteId")
fun deleteNoteById(noteId: Int)
#Update
fun updateTodo(vararg todos: TodoEntity)}
Here is My Fragment Class Where I am Setting data in RecyclerView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerviewcreatehistory?.layoutManager = LinearLayoutManager(context)
recyclerviewcreatehistory?.setHasFixedSize(true)
filefetch()
customAdaptercreatehistory = CustomAdapterCreateHistory(this.context ?: return, charItemcreate!!,this)
recyclerviewcreatehistory?.adapter = customAdaptercreatehistory
}
fun filefetch() {
val noteDatabase: AppDatabase = AppDatabase.DatabaseBuilder.getInstance(requireContext())
retrivingpresenter = QRRetrievingClassPresenter(this)
retrivingpresenter!!.getAllDatFromDatabase(noteDatabase)
}
override fun showAllData(note_list: List<TodoEntity>) {
if (note_list is ArrayList<*>) {
val arraylist = note_list as ArrayList<TodoEntity>
charItemcreate=arraylist
}
if (charItemcreate.isEmpty()){
}else{
customAdaptercreatehistory?.updateUsers(note_list as ArrayList<TodoEntity>)
customAdaptercreatehistory?.notifyDataSetChanged()
// Log.d("hello", note_list[0].imagesPathData)
}
}
You have to do some checks in your getAllDatFromDatabase() inside your coroutine. I guess list variable equals null or something like that. You should check if there are any files and if not you need to put there something else.

Kotlin Coroutines, retrofit-2.6.1, Network response null

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
)
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

Resources