web-dev-qa-db-fra.com

Android: passer dynamiquement la classe de modèle à la modification du rappel

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)

11
Sushil Kumar

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

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.

1
Fco P.

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

            }
        }
1
Sahil Munjal

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

            }
        });
1
Divyesh Patel

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!

1
TommySM

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);
}
0
Irfan Ul Haq

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
    }
0
Tselofan

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

0
koperko