2016-12-01 1 views
9

В модификации на карте JSon ответ на Pojo обычно мы делаем этоAndroid: динамически передать класс модели для модификации обратного вызова

@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 
    } 

где Пользователь мой класс Pojo. Но для каждого другого запроса мне нужно сделать другое pojo и написать тот же код для переназначения вызова. Я хочу сделать один метод для вызова api и передать соответствующий класс pojo для перенастройки вызова. как этот

ApiCalls api = retrofit.create(ApiCalls.class); 
Call<*ClassPassed*> call = api.getDataFromServer(StringConstants.URL,hashMap); 
call.enqueue(new Callback<*ClassPassed*>() { 
    //Response and failure callbacks 
} 

так что теперь я могу любой POJO класс один метод и получить response.This только, чтобы избежать перезаписи снова тот же код и again.is это возможно

Update Выработать больше:

Предположим, мне нужно сделать два запроса. Первый заключается в том, чтобы получить userDetails, а другой - с терпеливымDetails.So мне нужно создать два класса модели User и 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); 

и в моем FragmentUser и классе FragmentPatient я буду делать это

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 
} 

, но здесь код repaeting просто: Из-разные классы pojo. Мне нужно повторить один и тот же код во всех других фрагментах для разных запросов. Так что мне нужно сделать общий метод, где он может принять любой класс pojo, а затем из фрагмента я просто передам pojo для сопоставления.

+0

Что? Это именно то, что Retrofit пытается ПРЕДОТВРАТИТЬ! если вы хотите сделать что-то вроде этого, лучше использовать другую библиотеку или просто OkHttp без Retrofit или использовать Ion/Volley/Some other random http lib – Bhargav

+0

@sushildlh: обновите вопрос, пожалуйста, проверьте. –

+0

@Androidjack посмотрите на ответ, я думаю, это поможет вам .... – sushildlh

ответ

1

Первый Создание интерфейса:

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

Теперь создадим класс:

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; 
    } 

Теперь в любой деятельности, просто использовать:

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

Что нужно для создания нового клиента для каждого запроса? – Bhargav

+0

Я использую это в своем приложении. И у меня нет оптимизированного метода для этого –

+0

@Bhargav, как я могу использовать одноэлементный шаблон для использования класса apiclient? –

1

Мой подход сделать POJO под названием ResponseData, в котором у вас будет атрибут Object, поэтому у вас есть:

@POST 
Call<ResponseData> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap); 

Когда вы получите ответ, вам нужно проанализировать ваш response.body() в нужном классе. Таким образом, профи: у вас есть только один запрос, вместо этого вам нужно разобрать ответ.

1

Делают это так:

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

      } 
     } 
8

Вы можете построить главный POJO как этот

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; 
    } 
} 

И позвони, как этот

Call<BaseResponse<SetupDetails>> getSetup(@Query("site_id") int id,@Query("ino") String ii); 
+0

Какой подход сработал для вас? –

+0

Что именно вы хотите знать –

+0

У меня такой же сценарий, как и ваш ответ, но я не могу создать сервис и интерфейс для динамической модели. –

5

Существует 2 способа вы можете сделать это. ........

1.Дженерики

2. Объединить все POJO в один ......

Generics

В Дженерики вы должны пройти метод с классом. мольбы были выглядеть на примере .....

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

      } 
     }); 

    } 

ПРИМЕЧАНИЯ: - Над дооснащение вызова напечатанного материала вашего ответ в YOUR OBJECT, так что вы можете получить доступ к его полю и методы

Combine all POJO into one

Это очень легко использовать . Вы должны объединить свой класс POJO в один и использовать их в составе дооснащения. пожалуйста, смотрите на пример ниже ....

I have two API login and user......

В Вход API я есть получить ответ JSON, как это ...

{ "success": True, "message": "Authentication successful"}

выше JSON, POJO выглядеть следующим образом

public class LoginResult{ 

    private String message; 
    private boolean success; 

    //constructor , getter and setter 
} 

и дооснащение вызова выглядит так .....

call.enqueue(new Callback<LoginResult>() { 
       @Override 
       public void onResponse(Call<LoginResult> call, Response<LoginResult> response) { 


       } 

       @Override 
       public void onFailure(Call<LoginResult> call, Throwable t) { 

       } 
      }); 

В User API я есть получить ответ JSON, как это ...

{"name": "sushildlh", "place": "hyderabad"}

выше JSON, POJO выглядеть как этот

public class UserResult{ 

     private String name; 
     private String place; 

     //constructor , getter and setter 
    } 

и ДООСНАСТКЕ вызова выглядят как это .....

call.enqueue(new Callback<UserResult>() { 
       @Override 
       public void onResponse(Call<UserResult> call, Response<UserResult> response) { 


       } 

       @Override 
       public void onFailure(Call<UserResult> call, Throwable t) { 

       } 
      }); 

Просто объединить оба выше JSON ответ в один .....

public class Result{ 

      private String name; 
      private String place; 
      private String message; 
      private boolean success; 

      //constructor , getter and setter 
     } 

и использовать результат внутри вашего 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) { 

      } 
     }); 

Примечание: - Вы сразу объединить класс 2 POJO и доступа к нему. (Это очень сложно, если у вас есть ответ очень большой и обеспечить дублирование, если какой-то KEY одинаковый с другим типом Variable)

1

Для того чтобы обобщить то, что вы хотите, вы можете просто сериализовать свое POJO, а затем вы можете просто пройти ваш POJO к методу как есть. , когда вы сериализуете объекты, в основном преобразует их в строку, которые затем преобразуются в одну большую Json String, которую проще переносить и манипулировать.

Быстрый пример будет:

пример POJO реализации сериализации, здесь вы должны убедиться, что строки в Map<String,Object> соответствуют тому, что сервер ожидает получить, и этот метод должен быть различными в каждом 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.... 
} 

интерфейс сериализации (так что вы можете реализовать его через другие POJOs)

public interface ObjectSerializer extends Serializable 
{ 
    public Map<String, Object> objectSerialize(); 
} 

И вровень JSon серы вы Шоул, вероятно, в любом случае:

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

И, наконец, ваш API дефиниция:

@POST("users/createitem") 
Call<ResponseBody> someCall(@Body RequestBody params); 

И метод, который должен сидеть в общем классе, который управляет вашими запросами:

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

     } 
    }); 
    } 

Я возвращаю ответ через слушателя, это один пример того, что вы можете сделать в зависимости от вашего ответа.

Надеюсь, это поможет!

1

Вам необходимо использовать дженерики. Таким образом, вы можете передать желаемый тип в вызов.

@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 
    } 

Кстати, я не эксперт Модифицированная (я использую свой собственный материал в основном), но я думаю, что вы используете это неправильно.

0

Использование стандартных дженериков, с немного взлома вокруг

Определите интерфейс, как этот

public interface ApiCalls<T> { 
    @POST 
    Call<T> getResult getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap); 
} 

и призывают к созданию апи клиента использовать вспомогательный метод

class HelperMethods { 
    @SuppressWarnings("unchecked") 
    private static <T> ApiCalls<T> getClient() { 
     return retrofit.create((Class<ApiCalls<T>>)(Class<?>)ApiCalls.class); 
    } 
} 

ApiCalls<User> api = HelperMethods.getClient(); 

Но, несмотря на о том, сколько раз было сказано здесь, я собираюсь сказать это снова ... Не делайте этого. Вы отказываетесь от всего типа безопасности и подтверждения контракта, что Retrofit i s предложение .. Это действительно самая захватывающая вещь об этом ..

+0

Эй, @ koperko, как мы можем отделить методы обратного вызова, такие как OnResponse и OnError. Независимо от того, что я не хочу включать их в свою активность/фрагмент –

0

Я использую следующий подход: Сначала я реализовал пользовательский Вызов

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(); 
    } 
} 

Он wrappes Call<Tin> и преобразует это приводит к <Tout> конвертером.

@FunctionalInterface 
public interface Converter<Tin, Tout> { 
    public Tout Convert(Tin in); 
} 

Для вашего сервиса вы должны создать интерфейс сервиса, что возвращение JSONObject для одного объекта и JsonArray для массивов

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

Затем оберните его родовым класс, с преобразователями от JsonElement к любому типу <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 - ParaterizedType. Он используется для передачи параметров типа в gson для 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; 
    } 

} 

Тогда вы можете создать экземпляр ApiCallsGeneric с типом вы хотите.

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 
    } 
Смежные вопросы