Retrofit with dynamic URL - android

in my application am using a Retrofit 2.9.0, my issue is the user can change completely the URL from the app menu, in this case is not working when i changed the URL only if i restart the app.
this my instance of Retrofit :
object ApiService {
var token: String = ""
#JvmName("setToken1")
fun setToken(tk: String) {
token = tk
}
private val globalInterceptor = GlobalErrorInterceptor()
private val loginInterceptor = LoginErrorInterceptor()
private val okHttpClient =
OkHttpClient.Builder().addInterceptor(globalInterceptor).build()
private val okHttpClientLogin =
OkHttpClient.Builder().addInterceptor(loginInterceptor).build()
var gson = GsonBuilder()
.setLenient()
.create()
/**This instance for the others requests */
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(LOGIN_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
.build()
}
val API: WebServicesApi by lazy {
retrofit.create(WebServicesApi::class.java)
}
/**This instance for the login to get the Token */
private val retrofitLogin by lazy {
Retrofit.Builder()
.baseUrl(LOGIN_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClientLogin)
.build()
}
val APILogin: WebServicesApi by lazy {
retrofitLogin.create(WebServicesApi::class.java)
}
}

You can dynamically change retrofit URL by doing something like this. First change retrofit from val to var.
private fun changeBaseUrl(url: String) {
// change the base url only if new url is different than old url
if (retrofit.baseUrl().toString() != url) {
retrofit = retrofit.newBuilder().baseUrl(url).build()
}
}
Please note you might have to change this method and call it according to your flow. The main point to note here is the use of .newBuilder().baseUrl(url).build().

Related

Dynamic urls with Koin with Retrofit also call the old url

I'm working on a solution that needs to make recurring calls to an api every 10 seconds. However, I need to dynamically change the URL pointing to another service. That is, the new loop that will start will make the call to this new url base. I am using Koin as a DI. Here is an example of my code:
This is my dataModule koin
single<Retrofit>() {
Retrofit.Builder()
.client(httpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(OLD_BASE)
.build()
}
single<ApiService>() {
get<Retrofit>().create(ApiService::class.java)
}
{ single<OkHttpClient>(named(WITH_AUTH)) {
OkHttpClient.Builder()
.callTimeout(30, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(get<HttpLoggingInterceptor>(named(DATA_INTERCEPTOR)))
.addInterceptor(get<AuthInterceptor>(named(AUTH_INTERCEPTOR)))
.authenticator(get<AccessTokenAuthenticator>(named(AUTH_AUTHENTICATOR)))
.build()
}
single(named(DATA_INTERCEPTOR)) {
HttpLoggingInterceptor().apply {
level =
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.BASIC
}
}
}
single<AuthInterceptor>(named(AUTH_INTERCEPTOR)) {
AuthInterceptor(
get(), get()
)
}
And this is my interceptor :
class AuthInterceptor(
private val tokenRepository: TokenRepository,
private val envRepository: EnvRepository
) : Interceptor {
#Volatile
private var host: HttpUrl? = null
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val token = tokenRepository.getToken(TokenRepository.AUTH_TOKEN).blockingGet()
//val authenticationRequest = request(originalRequest, token)
host = envRepository.getEnvBaseUrl().toHttpUrlOrNull()
host?.let {
val newUrl = chain.request().url.newBuilder()
.scheme(it.scheme)
.host(it.toUrl().toURI().host)
.port(it.port)
.build()
request = chain.request().newBuilder()
.url(newUrl)
.build()
}
val authRequest = request(request, token) ?: request
return chain.proceed(authRequest)
}
private fun request(originalRequest: Request?, token: String?): Request? {
return if (!token.isNullOrEmpty()) {
originalRequest?.newBuilder()?.addHeader("Authorization", "Bearer $token")?.build()
} else {
originalRequest
}
}
}
The problem is that my interceptor works well, but each time before calling the new URL it also calls the old one. And I have no idea how to prevent it from calling the old URL in the loop. SO I have something like this in my debuger htts:
call old url
call olrd url
call new url
call new url
call old url
call old url
call new url
call new url
I hope I have been clear
Thanks,

Multiple API call with Observable.zip

I develop a RSS reader Android app. I want to API call more than one but I can't. My code is here, What am I doing wrong ?
Note;
Response will be XML as String dasd
Request size may increase (maybe 10)
ArticleServisGenerator;
object ArticleServiceGenerator {
private const val FAKE_URL = "https://api.github.com"
private val interceptor: HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BASIC
}
private val client: OkHttpClient = OkHttpClient.Builder().apply {
addInterceptor(interceptor)
}.build()
private val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(FAKE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build()
val service: ArticleService = retrofit.create(ArticleService::class.java)}
ArticleService;
interface ArticleService {
#GET
fun getArticlesFromRss(#Url url: String): Observable<ResponseBody>}
And Repository
class Repository {
fun getArticlesFromRss() {
val request: ArrayList<Observable<*>> = ArrayList()
// The number may increase here.
request.add(ArticleServiceGenerator.service.getArticlesFromRss("https://commonsware.com/blog/feed.atom"))
request.add(ArticleServiceGenerator.service.getArticlesFromRss("https://jfenn.me/blog/feeds/android.xml"))
request.add(ArticleServiceGenerator.service.getArticlesFromRss("https://arunkumar.dev/feed.xml"))
Observable.zip(request) { Any() }
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.subscribe({
print(it.toString())
}) {
print(it.toString())
}
}}
You can try something like below:
fun getArticlesFromRss() {
val request: ArrayList<Observable<ResponseBody>> = ArrayList()
request.add(ArticleServiceGenerator.service.getArticlesFromRss("https://commonsware.com/blog/feed.atom").subscribeOn(Schedulers.io()))
request.add(ArticleServiceGenerator.service.getArticlesFromRss("https://jfenn.me/blog/feeds/android.xml").subscribeOn(Schedulers.io()))
request.add(ArticleServiceGenerator.service.getArticlesFromRss("https://arunkumar.dev/feed.xml").subscribeOn(Schedulers.io()))
Observable.zip(request){ args -> Arrays.asList(args) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
print(it.toString())
}) {
print(it.toString())
}
}}
Each Observable inside zip is executed sequentially. Add subscribeOn(Schedulers.io()) to each getArticles Observable and it will execute them concurrently.
Observable<List<String>> result = Observable.zip(
observable1.subscribeOn(Schedulers.io()),
observable2.subscribeOn(Schedulers.io()),
observable3.subscribeOn(Schedulers.io()),
new Function3<ResponseType1, ResponseType2, ResponseType3, List<String>>() {
#Override
public List<String> apply(ResponseType1 type1, ResponseType2 type2, ResponseType3 type3) {
List<String> list = new ArrayList();
list.add(type1.data);
list.add(type2.data);
list.add(type3.data);
return list;
}
}
);
You just need to specify default scheduler.
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io))

Call Retrofit2 + Decrypt + Json conversor

I am using retrofit2 in kotlin, and I need to get the content that is a json and this encrypted, I know that to convert json just use the JacksonConverterFactory (until this part was working well) but an encryption was added before that and I do not know how To handle this, do I need to create a converter of my own? Does anyone have a read to tell me?
My current call for retrofit
val retrofit = Retrofit.Builder()
.baseUrl("http://100.1.1.100/")
.addConverterFactory(JacksonConverterFactory.create())
.client(httpClient.build())
.build()
And i already have my fucntion (working) to decrypt:
CryptAES.decrypt(value))
This can be done by creating an decrypt interceptor:
class DecryptInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response = chain
.run { proceed(request()) }
.let { response ->
return#let if (response.isSuccessful) {
val body = response.body()!!
val contentType = body.contentType()
val charset = contentType?.charset() ?: Charset.defaultCharset()
val buffer = body.source().apply { request(Long.MAX_VALUE) }.buffer()
val bodyContent = buffer.clone().readString(charset)
response.newBuilder()
.body(ResponseBody.create(contentType, bodyContent.let(::decryptBody)))
.build()
} else response
}
private fun decryptBody(content: String): String {
//decryption
return content
}
}
setup:
val httpClient = OkHttpClient().newBuilder()
httpClient.addInterceptor(DecryptInterceptor())
val retrofit = Retrofit.Builder()
.baseUrl("http://100.1.1.100/")
.addConverterFactory(JacksonConverterFactory.create())
.client(httpClient.build())
.build()

OkHttp token interceptor retries forever

I'm making an OkHttp interceptor so it retry to sign in when any end point returns a 401 error, but the interceptor is looping forever.
I've also tried to add a counter, but the counter resets itself every time.
Here's my code:
object Service {
private fun getOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(getLoggingInterceptor())
.addInterceptor(NetworkInterceptor())
.build()
}
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(getOkHttpClient())
.baseUrl(getBaseUrl())
.build()
}
}
class NetworkInterceptor: Interceptor {
var counter = 0
override fun intercept(chain: Interceptor.Chain): Response? {
val originalResponse = chain.proceed(chain.request())
if (!originalResponse.isSuccessful && originalResponse.code() == 401) {
Log.e("NetworkInterceptor", "Network error 401. Counter = $counter")
counter++
val refreshedToken = refreshToken()
Log.e("NetworkInterceptor", "refreshedToken = $refreshedToken")
}
return originalResponse
}
private fun refreshToken(): String {
val context = MyApp.appContext
val preferencesUtil = SharePreferencesUtils(context)
val username = preferencesUtil.getUsername()
val password = preferencesUtil.getPassword()
val login = AuthService().loginSync(username, password).execute()
return login.body()?.access_token!!
}
}
I have tried this with an Auth call being an RxJava Single, and a regular synchronous Call<>
In each case, the call happens forever, the 401 gets returned forever, and the counter always stays at 0.
Any ideas on what I'm missing or doing wrong?
Thank you very much!
Add as a NetworkInterceptor using
addNetworkInterceptor(NetworkInterceptor)
refer to this link for further clarification: https://github.com/square/okhttp/wiki/Interceptors

Retrofit 2 POST XML and get JSON answer from API. with Kotlin

I need to send XML to server and I want server sends me back JSON.
I'am using Retrofit 2 + Kotlin
Retrofit2 method(in methods interface):
#Headers("Content-Type: application/xml; charset=urf-8",
"Accept: application/json")
#POST(Connectors.SECRET_LINK)
fun sendCustomXml(#Body data: XmlHolder): Observable<String>
Retrofit2 service:
private fun <S> createService(serviceClass: Class<S>): S {
val retrofit =
return
}
init {
initLoggingInterceptor()
val builder = Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(SimpleXmlConverterFactory.create()) //here it is I thought
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
mService = builder.client(getHttpClient()).build().create(ApiMethods::class.java))
}
XML object I need to send like XML(tags and so on):
#Root(name = "root_element")
class XmlHolder {
#Attribute(name = "xmlns")
private val mXmlns = "http://www.anr.ru/types"
#Attribute(name = "type")
private val mType = "request"
#Element(name = "data")
private val mData = Data()
inner class Data {
#Attribute(name = "code")
private val mCode = "P0116"
#Element(name = "list_model")
private val mListModel = "123"
}
}
for now I got 400 error from server. shit.
Jake Wharton, can you suggest any solutions to me? :)
Any help, any examples, please, people...

Categories

Resources