Which way is better between companion object and fun without a class in Kotlin? - kotlin

I know there isn't a static function in Kotlin, so I write two code in OkHttpService.kt and my.kt
I don't know which is better, could you tell me? Thanks!
OkHttpService.kt
class OkHttpService {
companion object {
fun httpGet(username: String, callback: Callback) {
val fetchRepoUrl = "https://api.github.com/users/$username/repos?page=1&per_page=20"
val client = OkHttpClient()
val request = Request.Builder()
.url(fetchRepoUrl)
.build()
client.newCall(request).enqueue(callback)
}
}
}
my.kt
fun OkHttpService_httpGet(username: String, callback: Callback) {
val fetchRepoUrl = "https://api.github.com/users/$username/repos?page=1&per_page=20"
val client = OkHttpClient()
val request = Request.Builder()
.url(fetchRepoUrl)
.build()
client.newCall(request).enqueue(callback)

For scoping use a regular object, not companion:
object OkHttpService{
fun httpGet(username: String, callback: Callback) {
val fetchRepoUrl = "https://api.github.com/users/$username/repos?page=1&per_page=20"
val client = OkHttpClient()
val request = Request.Builder()
.url(fetchRepoUrl)
.build()
client.newCall(request).enqueue(callback)
}
}

Related

Create bearer authorization header in Kotlin Using OkHttp

I use OkHttp3 in Kotlin as a HTTP client and send Authorization Token Bearer header in request and return the result.
this is my code, but when run it the app closed :
RetrofitInstance.kt
class OAuthInterceptor(
private val tokenType: String,
private val acceessToken: String,
private val branchid : Int,
private val currency : Int,):Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response
{
var request = chain.request()
request = request.newBuilder()
.addHeader("Authorization", "$tokenType $acceessToken")
.addHeader("branchId", "$branchid")
.addHeader("currencyId", "$currency")
.build()
return chain.proceed(request)
}
}
class RetrofitInstance {
companion object {
private val client = OkHttpClient.Builder()
.addInterceptor(OAuthInterceptor(
"Bearer",
"YourToken",
123,
1)
)
.build()
val BASE_URL = "https://url"
fun getRetrofitInstance(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client).addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
}
}
}
AlbumService.Kt
interface AlbumService {
#GET("users")
suspend fun getAlbums() : Response<Albums>
}
Album.kt
class Albums : ArrayList<dataapi>()
AlbumItem.kt
data class dataapi(
#SerializedName("address")
val address: String,
)

take access token from Shared Preferences and pass in base url next time

I want to know how can I access my token stored in Shared Preferences and pass it in the url when required.
RetrofitClient.kt file :
class OAuthInterceptor(private val tokenType: String, private val access_token: String):
Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
request = request.newBuilder().header("Authorization", "$tokenType $access_token").build()
return chain.proceed(request)
}
}
val client = OkHttpClient.Builder()
.addInterceptor(OAuthInterceptor("Bearer", access_token))
.build()
val retrofit = Retrofit.Builder()
.baseUrl("___________________")
.client(client)
.build()
object RetrofitClient {
private const val BASE_URL = "_______"
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.method(original.method, original.body)
val request = requestBuilder.build()
chain.proceed(request)
}.build()
val instance: MyApi by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
retrofit.create(MyApi::class.java)
}
}
Below is my SharedPrefManager file :
class SharedPrefManager private constructor(private val mCtx: Context) {
val user: User
get() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
return User(
sharedPreferences.getString(user.access_token, ""),
sharedPreferences.getString(user.user_id.toString(), "" ),
sharedPreferences.getString(user.user_name, ""),
sharedPreferences.getString(user.status, ""),
sharedPreferences.getString(user.role_id,""),
sharedPreferences.getString(user.phone, "")
)
}
fun saveUser(user: User) {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putString("access_token", user.access_token)
editor.putString("id", user.user_id)
editor.putString("username", user.user_name)
editor.putString("status", user.status)
editor.putString("role_id", user.role_id)
editor.putString("phone", user.phone)
editor.apply()
}
fun clear() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.clear()
editor.apply()
}
companion object {
private const val SHARED_PREF = "my_shared_pref"
private var mInstance: SharedPrefManager? = null
#Synchronized
fun getInstance(mCtx: Context): SharedPrefManager {
if (mInstance == null) {
mInstance = SharedPrefManager(mCtx)
}
return mInstance as SharedPrefManager
}
}
}
You should use the name of the save preference when retrieving them.
Try to change your SharedPrefManager get method like this:
get() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
return User(
sharedPreferences.getString("access_token", ""),
sharedPreferences.getString("id", "" ),
sharedPreferences.getString("username", ""),
sharedPreferences.getString("status", ""),
sharedPreferences.getString("role_id",""),
sharedPreferences.getString("phone", "")
)
}

How to create an HttpResponse object with dummy values in ktor Kotlin?

I am using ktor for developing a microservice in Kotlin. For testing a method, I need to create a dummy HttpResponse (io.ktor.client.statement.HttpResponse to be specific) object with status = 200 and body = some json data.
Any idea how I can create it?
You can use mockk or a similar kind of library to mock an HttpResponse. Unfortunately, this is complicated because HttpRequest, HttpResponse, and HttpClient objects are tightly coupled with the HttpClientCall. Here is an example of how you can do that:
val call = mockk<HttpClientCall> {
every { client } returns mockk {}
coEvery { receive(io.ktor.util.reflect.typeInfo<String>()) } returns "body"
every { coroutineContext } returns EmptyCoroutineContext
every { attributes } returns Attributes()
every { request } returns object : HttpRequest {
override val call: HttpClientCall = this#mockk
override val attributes: Attributes = Attributes()
override val content: OutgoingContent = object : OutgoingContent.NoContent() {}
override val headers: Headers = Headers.Empty
override val method: HttpMethod = HttpMethod.Get
override val url: Url = Url("/")
}
every { response } returns object : HttpResponse() {
override val call: HttpClientCall = this#mockk
override val content: ByteReadChannel = ByteReadChannel("body")
override val coroutineContext: CoroutineContext = EmptyCoroutineContext
override val headers: Headers = Headers.Empty
override val requestTime: GMTDate = GMTDate.START
override val responseTime: GMTDate = GMTDate.START
override val status: HttpStatusCode = HttpStatusCode.OK
override val version: HttpProtocolVersion = HttpProtocolVersion.HTTP_1_1
}
}
val response = call.response
I did this with following. I only needed to pass a status code and description, so I didn't bother about other fields.
class CustomHttpResponse(
private val statusCode: Int,
private val description: String
) :
HttpResponse() {
#InternalAPI
override val content: ByteReadChannel
get() = ByteReadChannel("")
override val call: HttpClientCall
get() = HttpClientCall(HttpClient())
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
override val headers: Headers
get() = Headers.Empty
override val requestTime: GMTDate
get() = GMTDate()
override val responseTime: GMTDate
get() = GMTDate()
override val status: HttpStatusCode
get() = HttpStatusCode(statusCode, description)
override val version: HttpProtocolVersion
get() = HttpProtocolVersion(name = "HTTP", major = 1, minor = 1)}
With Ktor 2, it's best to use externalServices block instead of attempting to mock HttpResponse. That way you don't need to attempt and mock the internals of Ktor, and it's not complicated at all.
externalServices {
hosts("https://your-fake-host") {
routing {
get("/api/v1/something/{id}/") {
call.respondText(
"{}",
contentType = ContentType.Application.Json,
status = HttpStatusCode.OK
)
}
}
}
}
This need to be wrapped with testApplication

Android Kotlin - retrofit2 set POST parameters

interface ApiInterface {
#Headers("Content-Type: application/json")
#POST("testgetmemes/")
fun getMemes(): Call<List<Memes>>
}
object ApiClient {
var BASE_URL:String="https://www.androidisapos.com/"
val getClient: ApiInterface
get() {
val gson = GsonBuilder()
.setLenient()
.create()
val client = OkHttpClient.Builder().build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
return retrofit.create(ApiInterface::class.java)
}
}
and inside a function:
val call: Call<List<Memes>> = ApiClient.getClient.getMemes()
call.enqueue(object : Callback<List<Memes>> {
override fun onResponse(call: Call<List<Memes>>?, response: Response<List<Memes>>) {
setMemes(JSONArray(Gson().toJson(response.body())), gal)
}
override fun onFailure(call: Call<List<Memes>>?, t: Throwable?) {
Log.d(tagg, t!!.toString())
}
})
How can I add POST Parameters (and the values ofc)? I've seen countless examples but they all construct the code of this absolutely awful library differently what makes it impossible to understand when you don't know Kotlin/Java 100%
EDIT:
I tried:
fun getMemes(#Query("test") test: String?): Call<List<Memes>>
and
val call: Call<List<Memes>> = ApiClient.getClient.getMemes("bla")
It doesn't send POST key test with value bla

How to emit from a LiveData builder from a non-suspending callback function

I'm new to LiveData and Kotlin Coroutines. I'm trying to use the Chromium Cronet library to make a request from my repository class to return a LiveData object. To return the liveData, I'm using the new LiveData builder (coroutines with LiveData). How would I emit the result from a successful Cronet request?
class CustomRepository #Inject constructor(private val context: Context, private val gson: Gson) : Repository {
private val coroutineDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
override suspend fun getLiveData(): LiveData<List<MyItem>> = liveData(coroutineDispatcher) {
val executor = Executors.newSingleThreadExecutor()
val cronetEngineBuilder = CronetEngine.Builder(context)
val cronetEngine = cronetEngineBuilder.build()
val requestBuilder = cronetEngine.newUrlRequestBuilder(
"http://www.exampleApi.com/example",
CustomRequestCallback(gson),
executor
)
val request: UrlRequest = requestBuilder.build()
request.start()
}
class CustomRequestCallback(private val gson: Gson) : UrlRequest.Callback() {
override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {
byteBuffer?.flip()
byteBuffer?.let {
val byteArray = ByteArray(it.remaining())
it.get(byteArray)
String(byteArray, Charset.forName("UTF-8"))
}.apply {
val myItems = gson.fromJson(this, MyItem::class.java)
// THIS IS WHAT I WANT TO EMIT
// emit(myItems) doesn't work since I'm not in a suspending function
}
byteBuffer?.clear()
request?.read(byteBuffer)
}
// other callbacks not shown
}
}
The solution involves wrapping the UrlRequest.Callback traditional callback structure in a suspendCoroutine builder.
I also captured my learning in a Medium article which discusses Cronet integration with LiveData and Kotlin Coroutines.
override suspend fun getLiveData(): LiveData<List<MyItem>> = liveData(coroutineDispatcher) {
lateinit var result: List<MyItem>
suspendCoroutine<List<MyItem>> { continuation ->
val requestBuilder = cronetEngine.newUrlRequestBuilder(
"http://www.exampleApi.com/example",
object : UrlRequest.Callback() {
// other callbacks not shown
override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {
byteBuffer?.flip()
byteBuffer?.let {
val byteArray = ByteArray(it.remaining())
it.get(byteArray)
String(byteArray, Charset.forName("UTF-8"))
}.apply {
val myItems = gson.fromJson(this, MyItem::class.java)
result = myItems
continuation.resume(result)
}
byteBuffer?.clear()
request?.read(byteBuffer)
},
executor
)
val request: UrlRequest = requestBuilder.build()
request.start()
}
emit(result)
}