Je me demande s'il existe un moyen pour Dagger de savoir qu'il devrait recréer un objet lorsque de nouvelles données sont disponibles.
Le cas dont je parle concerne les en-têtes de demande de modification. À un moment donné (lorsque l'utilisateur se connecte), j'obtiens un jeton que je dois ajouter aux en-têtes de modification pour effectuer des demandes authentifiées. Le problème est que je me retrouve avec la même version non authentifiée de mise à niveau. Voici mon code d'injection:
@Provides
@Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.cache(cache).build();
client
.newBuilder()
.addInterceptor(
chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Accept", "Application/JSON");
Request request = requestBuilder.build();
return chain.proceed(request);
}).build();
return client;
}
@Provides
@Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
return retrofit;
}
@Provides
@Singleton
public NetworkService providesNetworkService(Retrofit retrofit) {
return retrofit.create(NetworkService.class);
}
Des idées sur la façon de faire fonctionner cela?
Veuillez envisager d'utiliser l'approche mentionnée par @ oldgod car c'est le "officiel" et bien mieux, alors que les approches mentionnées ci-dessous ne sont pas déconseillées , elles peuvent être considérées comme des solutions de contournement.
Vous avez plusieurs options.
Retrofit
, créer un nouveau composant et demander une nouvelle instance Retrofit
, qui sera instanciée avec le nécessaire okhttp
instance.SharedPreferences
, créez l'en-tête okHttp
, qui appliquera la lecture du jeton à partir de SharedPreferences
. S'il n'y en a pas - n'envoyez aucun en-tête de jeton.static volatile String
champ, et faites la même chose qu'à l'étape 2.Pourquoi la deuxième option est mauvaise? Parce qu'à chaque demande, vous seriez en train d'interroger la carte SD et de récupérer des données à partir de là.
J'ai personnellement créé un okhttp3.Interceptor
qui fait ça pour moi, que je mets à jour une fois que j'ai le jeton requis. Cela ressemble à quelque chose comme:
@Singleton
public class MyServiceInterceptor implements Interceptor {
private String sessionToken;
@Inject public MyServiceInterceptor() {
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder requestBuilder = request.newBuilder();
if (request.header(NO_AUTH_HEADER_KEY) == null) {
// needs credentials
if (sessionToken == null) {
throw new RuntimeException("Session token should be defined for auth apis");
} else {
requestBuilder.addHeader("Cookie", sessionToken);
}
}
return chain.proceed(requestBuilder.build());
}
}
Dans le composant poignard correspondant, j'expose cet intercepteur afin que je puisse définir le sessionToken
quand j'en ai besoin.
C'est quelque chose dont Jake en a parlé dans son discours Faire fonctionner la rénovation pour vous .
Basé sur la version @oldergod de la solution kotlin avec différentes classes et structure
Faire une instance de Retrofit comme celle-ci
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private val BASE_URL = "http://yoururl"
val retrofitInstance: Retrofit?
get() {
if (retrofit == null) {
var client = OkHttpClient.Builder()
.addInterceptor(ServiceInterceptor())
//.readTimeout(45,TimeUnit.SECONDS)
//.writeTimeout(45,TimeUnit.SECONDS)
.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
Ajoutez la classe ServiceInterceptor
comme ci-dessous
class ServiceInterceptor : Interceptor{
var token : String = "";
fun Token(token: String ) {
this.token = token;
}
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if(request.header("No-Authentication")==null){
//val token = getTokenFromSharedPreference();
//or use Token Function
if(!token.isNullOrEmpty())
{
val finalToken = "Bearer "+token
request = request.newBuilder()
.addHeader("Authorization",finalToken)
.build()
}
}
return chain.proceed(request)
}
}
Interface de connexion et implémentation de la classe de données
interface Login {
@POST("Login")
@Headers("No-Authentication: true")
fun login(@Body value: LoginModel): Call<LoginResponseModel>
@POST("refreshToken")
fun refreshToken(refreshToken: String):
Call<APIResponse<LoginResponseModel>>
}
data class LoginModel(val Email:String,val Password:String)
data class LoginResponseModel (val token:String,val
refreshToken:String)
appeler cela dans une activité comme celle-ci
val service = RetrofitClientInstance.retrofitInstance?.create(Login::class.Java)
val refreshToken = "yourRefreshToken"
val call = service?.refreshToken(refreshToken)
call?.enqueue(object: Callback<LoginResponseModel>{
override fun onFailure(call: Call<LoginResponseModel>, t: Throwable) {
print("throw Message"+t.message)
Toast.makeText(applicationContext,"Error reading JSON",Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<LoginResponseModel>, response: Response<LoginResponseModel>) {
val body = response?.body()
if(body!=null){
//do your work
}
}
})