En rattrapage pour répondre à la réponse json à pojo, nous le faisons habituellement
@POST
Call<User> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
ApiCalls api = retrofit.create(ApiCalls.class);
Call<User> call = api.getDataFromServer(StringConstants.URL,hashMap);
call.enqueue(new Callback<User>() {
//Response and failure callbacks
}
où l'utilisateur est ma classe Pojo . Mais pour chaque autre requête, j'ai besoin de créer un pojo différent et d'écrire le même code pour un appel ultérieur. comme ça
ApiCalls api = retrofit.create(ApiCalls.class);
Call<*ClassPassed*> call = api.getDataFromServer(StringConstants.URL,hashMap);
call.enqueue(new Callback<*ClassPassed*>() {
//Response and failure callbacks
}
alors maintenant, je peux n'importe quelle classe de pojo à la méthode simple et obtenir la réponse. Ceci est juste pour éviter de réécrire le même code encore et encore. est-ce possible
Mise à jour Pour en savoir plus:
Supposons que je doive faire deux demandes. La première consiste à obtenir userDetails et l'autre est patientDetails.Il me faut donc créer deux classes de modèles utilisateur et patient .
@POST
Call<User> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
@POST
Call<Patient> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
et dans ma classe FragmentUser et FragmentPatient, je vais le faire
ApiCalls api = retrofit.create(ApiCalls.class);
Call<User> call = api.getDataFromServer(StringConstants.URL,hashMap);
call.enqueue(new Callback<User>() {
//Response and failure callbacks
}
ApiCalls api = retrofit.create(ApiCalls.class);
Call<Patient> call = api.getDataFromServer(StringConstants.URL,hashMap);
call.enqueue(new Callback<Patient>() {
//Response and failure callbacks
}
mais ici, le code est en cours de restauration, juste à cause de différentes classes de pojo.J'ai besoin de répéter le même code dans tous les autres fragments pour des requêtes différentes . Je vais juste passer le pojo pour être cartographié.
Android: passer dynamiquement la classe de modèle à la modification du rappel
Il y a 2 façons de faire cela .........
1. Génériques
2. Combine tous les POJO en un seul ......
Génériques
Dans les génériques, vous devez transmettre la méthode à la classe. moyens ont regarder par exemple .....
ApiCalls api = retrofit.create(ApiCalls.class);
Call<User> call = api.getDataFromServer(StringConstants.URL,hashMap);
callRetrofit(call,1);
public static <T> void callRetrofit(Call<T> call,final int i) {
call.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
if(i==1){
User user = (User) response.body(); // use the user object for the other fields
}else if (i==2){
Patient user = (Patient) response.body();
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
}
});
}
NOTE: - Au-dessus de l’appel de conversion TypeCast votre réponse en YOUR OBJECT
, afin que vous puissiez accéder à son champ et à ses méthodes
Combine tous les POJO en un seul
C'est très facile à utiliser. Vous devez combiner toutes vos classes de POJO en une seule et les utiliser à l’intérieur du Retrofit. veuillez regarder ci-dessous exemple ....
J'ai deux identifiants API et utilisateur ......
Dans l'API de connexion, j'ai une réponse JSON comme ceci ...
{"succès": True, "message": "Authentification réussie"}
au-dessus de JSON, POJO ressemble à ceci
public class LoginResult{
private String message;
private boolean success;
//constructor , getter and setter
}
et l’appel de rattrapage ressemble à ceci .....
call.enqueue(new Callback<LoginResult>() {
@Override
public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
}
@Override
public void onFailure(Call<LoginResult> call, Throwable t) {
}
});
Dans l'API utilisateur, j'ai une réponse JSON comme celle-ci ...
{"name": "sushildlh", "place": "hyderabad"}
au-dessus de JSON, POJO ressemble à ceci
public class UserResult{
private String name;
private String place;
//constructor , getter and setter
}
et l’appel de rattrapage ressemble à ceci .....
call.enqueue(new Callback<UserResult>() {
@Override
public void onResponse(Call<UserResult> call, Response<UserResult> response) {
}
@Override
public void onFailure(Call<UserResult> call, Throwable t) {
}
});
Combinez simplement la réponse JSON ci-dessus en une seule .....
public class Result{
private String name;
private String place;
private String message;
private boolean success;
//constructor , getter and setter
}
et utiliser Result dans votre appel API ......
call.enqueue(new Callback<Result>() {
@Override
public void onResponse(Call<Result> call, Response<Result> response) {
}
@Override
public void onFailure(Call<Result> call, Throwable t) {
}
});
Remarque: - Vous combinez directement votre classe de 2 POJO et y accédez. (Ceci est très compliqué si vous avez une réponse très grande et fournissez une duplication si une clé est identique avec un type de variable différent)
Vous pouvez construire un pojo principal comme celui-ci
public class BaseResponse<T>
{
@SerializedName("Ack")
@Expose
private String ack;
@SerializedName("Message")
@Expose
private String message;
@SerializedName("Data")
@Expose
private T data;
/**
*
* @return
* The ack
*/
public String getAck() {
return ack;
}
/**
*
* @param ack
* The Ack
*/
public void setAck(String ack) {
this.ack = ack;
}
/**
*
* @return
* The message
*/
public String getMessage() {
return message;
}
/**
*
* @param message
* The Message
*/
public void setMessage(String message) {
this.message = message;
}
/**
*
* @return
* The data
*/
public T getData() {
return data;
}
/**
*
* @param data
* The Data
*/
public void setData(T data) {
this.data = data;
}
}
Et appelle comme ça
Call<BaseResponse<SetupDetails>> getSetup(@Query("site_id") int id,@Query("ino") String ii);
Vous devez utiliser des génériques. De cette façon, vous pouvez transmettre le type souhaité à l'appel.
@POST
Call<T> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap, Class<T> requestClass);
ApiCalls api = retrofit.create(ApiCalls.class);
Call<T> call = api.getDataFromServer(StringConstants.URL,hashMap);
call.enqueue(new Callback<T>() {
//Response and failure callbacks
}
En passant, je ne suis pas un expert en modernisation (j'utilise principalement mes propres logiciels), mais je pense que vous l'utilisez mal.
Fais-le comme ça :
@POST
Call<Object> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
ApiCalls api = retrofit.create(ApiCalls.class);
Call<Object> call = api.getDataFromServer(StringConstants.URL,hashMap);
call.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<Object> call, Response<Object> response) {
YourModel modelObject = (YourModel) response.body();
}
@Override
public void onFailure(Call<Object> call, Throwable t) {
}
}
Première interface créer:
public interface
ItemAPI {
@GET("setup.php")
Call<SetupDetails> getSetup(@Query("site_id") int id,@Query("ino") String ii);
@GET("setup.php")
void setMy(@Query("site_id") int id, Callback<List<SetupDetails>> sd);
}
Maintenant, créez la classe:
public class Apiclient {
private static final String BASE_URL ="http://www.YOURURL.COM/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
OkHttpClient.Builder httpClient2 = new OkHttpClient.Builder();
httpClient2.addNetworkInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
builder.addHeader("site_id","1");
return chain.proceed(builder.build());
}
});
OkHttpClient client = httpClient2.build();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
Maintenant, dans toute activité, utilisez simplement:
ItemAPI itemAPI = Apiclient.getClient().create(ItemAPI.class);
Call<AllProducts> call=itemAPI.getSetup(1,count);
call.enqueue(new Callback<AllProducts>() {
@Override
public void onResponse(Call<AllProducts> call, Response<AllProducts> response) {
try {
if (response.body().getItem().size()>0){
}
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void onFailedAfterRetry(Throwable t) {
}
});
Afin de généraliser ce que vous voulez, vous pouvez simplement sérialiser votre POJO, puis vous pouvez simplement passer votre POJO à la méthode telle quelle ... .. lorsque vous sérialisez avec Objects, il le convertit en chaîne, qui est ensuite convertie en une Big Json String, qui sont plus faciles à transférer et à manipuler.
Un exemple rapide serait:
exemple de POJO implémentant la sérialisation, assurez-vous ici que les chaînes du Map<String,Object>
correspondent à ce que le serveur s'attend à obtenir, et cette méthode doit être différente dans chaque POJO:
public class YourPojo implements ObjectSerializer
{
private static final long serialVersionUID = -1481977114734318563L;
private String itemName;
private int itemId;
@Override
public Map<String, Object> objectSerialize()
{
Map<String, Object> map = new HashMap<>();
map.put("itemid", itemId); // server will be looking for "itemid"
map.put("itemname", itemName); // server will be looking for "itemname"
}
//other stuff you need....
}
L’interface de sérialisation (vous pouvez donc l’implémenter avec d’autres POJO)
public interface ObjectSerializer extends Serializable
{
public Map<String, Object> objectSerialize();
}
Et un analyseur Json que vous devriez probablement avoir de toute façon:
public class JsonParser
{
public static JSONObject serializeToJsonString(Map<String, Object> jsonParams)
{
Gson gson = new Gson();
String json = gson.toJson(jsonParams);
JSONObject object;
try
{
object = new JSONObject(json);
}
catch (Exception e)
{
object = new JSONObject(jsonParams);
}
return (object);
}
}
Et enfin, votre définition d'API:
@POST("users/createitem")
Call<ResponseBody> someCall(@Body RequestBody params);
Et méthode, qui devrait être dans une classe générale qui gère vos requêtes:
public void someMethodName(YourPojo item, final CustomEventListener<String> listener)
{
JSONObject object = JsonParser.serializeToJsonString(item.objectSerialize());
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8";), object.toString());
Call<ResponseBody> requestCall = serviceCaller.someCall(body);
requestCall.enqueue(new Callback<ResponseBody>()
{
@Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
String response = rawResponse.body().string();
//do what you want with this string
listener.getResult(response);
}
catch (Exception e)
{
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
}
});
}
Je retourne la réponse par un auditeur, c’est un exemple de ce que vous pouvez faire en fonction de votre réponse.
J'espère que cela t'aides!
Mon approche est de créer un POJO appelé ResponseData dans lequel vous aurez un attribut Object, de sorte que vous avez:
@POST
Call<ResponseData> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
Lorsque vous obtenez la réponse, vous devez analyser votre response.body () dans la classe souhaitée. Donc, les avantages: vous n'avez qu'une requête, vous devez analyser la réponse.
utiliser JsonElement dans Response aiderait à:
public interface serviceApi {
// @GET("userinfo")
// Observable<userInfo> getUserIfo();
@GET("gmail/v1/users/me/profile")
Observable<Response<JsonElement>> getUserProfile(@HeaderMap
Map<String,String> Headers);
}
private void executeAPICall(String token) {
HashMap<String, String> headers = new HashMap<>();
Observable<Response<JsonElement>> observable = RetroFitInstance.getInstance().getAPI(token)
.getUserProfile(ImmutableMap.<String, String>of("Authorization", String.format("Bearer %s", token))).observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
Observer<Response<JsonElement>> observer = new Observer<Response<JsonElement>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.d("error:", e.getMessage());
}
@Override
public void onNext(Response<JsonElement> jsonElementResponse) {
UserProfile userProfile =
getObject(jsonElementResponse,UserProfile.class);
EmailTextView.setText("Email Address: " +
userProfile.getEmailAddress());
EmailTextView.setText("Email Address: " +
userProfile.getEmailAddress());
totalEmailsTextView.setText("Total Emails: " + userProfile.getMessagesTotal());
totalThreadsTextView.setText("Total Threads: " + userProfil
};
subscription = observable.subscribe(observer);
}
private <T> T getObject(Response<JsonElement> jsonElementResponse, Class<T>
t){
return new Gson().fromJson(jsonElementResponse.body().getAsJsonObject().toString(),t);
}
J'utilise l'approche suivante: D'abord j'ai implémenté l'appel personnalisé
public class ProxyConvertCall<Tin,Tout> implements Call<Tout> {
Converter<Tin,Tout> converter;
Call<Tin> innerCall;
public ProxyConvertCall2(Call<Tin> jcall, Converter<Tin,Tout> converter){
this.innerCall = jcall;
this.converter = converter;
}
@Override
public Response<Tout> execute() throws IOException {
Response<Tin> response = innerCall.execute();
if (response.isSuccessful()){
return Response.success(converter.Convert(response.body()),response.raw());
}
else return Response.error(response.code(), response.errorBody());
}
@Override
public void enqueue(final Callback<Tout> callback) {
final Call<Tout> self = this;
this.innerCall.enqueue(new Callback<Tin>() {
@Override
public void onResponse(Call<Tin> call, Response<Tin> response) {
if (response.isSuccessful()){
callback.onResponse(self, Response.success(converter.Convert(response.body()), response.raw()));
}
else callback.onResponse(self, Response.error(response.code(), response.errorBody()));
}
@Override
public void onFailure(Call<Tin> call, Throwable t) {
callback.onFailure(self,t);
}
});
}
@Override
public boolean isExecuted() {
return innerCall.isExecuted();
}
@Override
public void cancel() {
innerCall.cancel();
}
@Override
public boolean isCanceled() {
return innerCall.isCanceled();
}
@Override
public Call<Tout> clone() {
return new ProxyConvertCall2<>(innerCall,converter);
}
@Override
public Request request() {
return innerCall.request();
}
}
Il enveloppe Call<Tin>
et convertit son résultat en <Tout>
par convertisseur.
@FunctionalInterface
public interface Converter<Tin, Tout> {
public Tout Convert(Tin in);
}
Pour votre service, vous devez créer une interface de service qui renvoie JsonObject pour un seul objet et JsonArray pour les tableaux.
public interface ApiCalls {
@POST
Call<JsonObject> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
@POST
Call<JsonArray> getArrayFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
}
Enveloppez-le ensuite dans une classe générique, avec des convertisseurs de JsonElement en n'importe quel type <T>
:
public class ApiCallsGeneric<T> {
Converter<JsonObject,T> fromJsonObject;
Converter<JsonArray,List<T>> fromJsonArray;
ApiCalls service;
public ApiCallsGeneric(Class<T> classOfT, ApiCalls service){
this.service = service;
Gson gson = new GsonBuilder().create();
GenericListType<T> genericListTypeOfT = new GenericListType<T>(classOfT);
fromJsonObject = (t)->gson.fromJson(t,classOfT);
fromJsonArray =(t)->gson.fromJson(t,genericListTypeOfT);
}
public Call<T> getDataFromServer(String url, HashMap<String,Object> hashMap){
return new ProxyConvertCall<>(service.getDataFromServer(url, hashMap), fromJsonObject);
}
public Call<List<T>> getArrayFromServer(String url, HashMap<String,Object> hashMap){
return new ProxyConvertCall<>(service.getArrayFromServer(url, hashMap), fromJsonArray);
}
}
GenericListType est ParaterizedType. Il est utilisé pour passer le paramètre de type à gson pour List<T>
import Java.lang.reflect.ParameterizedType;
import Java.lang.reflect.Type;
import Java.util.List;
public class GenericListType<T> implements ParameterizedType {
private Type wrapped;
public GenericListType(Type wrapped) {
this.wrapped = wrapped;
}
public Type[] getActualTypeArguments() {
return new Type[] {wrapped};
}
public Type getRawType() {
return List.class;
}
public Type getOwnerType() {
return null;
}
}
Ensuite, vous pouvez instancier ApiCallsGeneric
avec le type de votre choix.
ApiCallsGeneric<User> userService= new ApiCallsGeneric<User>(User.class, retrofit.create(ApiCalls.class));
Call<User> call = userService.getDataFromServer(StringConstants.URL,hashMap);
call.enqueue(new Callback<User>() {
//Response and failure callbacks
}
Utilisez des génériques standard, avec un peu de piratage
Définissez votre interface comme ceci
public interface ApiCalls<T> {
@POST
Call<T> getResult getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
}
et appelez pour créer un client api, utilisez une méthode d'assistance
class HelperMethods {
@SuppressWarnings("unchecked")
private static <T> ApiCalls<T> getClient() {
return retrofit.create((Class<ApiCalls<T>>)(Class<?>)ApiCalls.class);
}
}
ApiCalls<User> api = HelperMethods.getClient();
Mais en dépit du nombre de fois où cela a été dit ici, je vais le répéter encore une fois ... Ne faites pas cela. Vous renoncez à tout le type de validation de sécurité et de contrat proposé par Retrofit. la chose la plus excitante à ce sujet ..