【问题标题】:Android:dynamically pass model class to retrofit callbackAndroid:动态传递模型类来改造回调
【发布时间】:2017-04-15 16:04:08
【问题描述】:

在将 json 响应映射到 pojo 的改造中,我们通常会这样做

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

其中 User 是我的 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 类转换为单个方法并获得响应。这只是为了避免一次又一次地重写相同的代码。这可能吗

更新 详细说明:

假设我需要提出两个请求。第一个是获取 userDetails,另一个是 PatientDetails。所以我必须创建两个模型类 User 和 Patient。 所以在改造 api 中我会有这样的东西

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

但是这里的代码只是因为不同的 pojo 类而重复。我需要在每个其他片段中重复相同的代码以处理不同的请求。 所以我需要创建一个通用方法,它可以接受任何 pojo 类,然后从片段中传递要映射的 pojo。

【问题讨论】:

  • 什么?这正是 Retrofit 试图阻止的!如果你想做这样的事情,最好使用另一个库,或者只使用没有改造的 OkHttp,或者使用 Ion/Volley/Some other random http lib
  • @sushildlh:更新问题请查收。
  • @Androidjack 看看答案,我想它对你有帮助....

标签: android retrofit retrofit2 pojo


【解决方案1】:

Android:将模型类动态传递给改造回调

有两种方法可以做到这一点............

1.泛型

2。将所有 POJO 合二为一……

泛型

在泛型中,您必须将方法与类一起传递。请看例子.....

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

            }
        });

    }

注意:- 上面的改造调用 TypeCast 你的回复到YOUR OBJECT,所以你可以访问它的字段和方法

将所有 POJO 合二为一

它非常易于使用。您必须将所有 POJO 类合并为一个,并在 Retrofit 中使用它们。请看下面的例子....

我有两个 API 登录和用户......

在登录 API 中,我得到了这样的 JSON 响应...

{ "success": True, "message": "认证成功"}

在 JSON 之上,POJO 是这样的

public class LoginResult{

    private String message;
    private boolean success;

    //constructor , getter and setter 
}

和 Retrofit 调用看起来像这样.....

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


                }

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

                }
            });

在用户 API 中,我得到了这样的 JSON 响应 ...

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

在 JSON 之上,POJO 是这样的

 public class UserResult{

        private String name;
        private String place;

        //constructor , getter and setter 
    }

和 Retrofit 调用看起来像这样.....

 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 调用中使用 Result ......

  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 与不同的变量类型相同则提供重复,这将非常复杂)

【讨论】:

    【解决方案2】:

    你可以像这样构建 main 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);
    

    【讨论】:

    • 你到底想知道什么
    • 我的情况与您的回答指出的相同,但我无法为动态模型创建服务和接口。
    • Call> 用模型名称代替 T
    • 这不起作用 - Retrofit 不允许动态类型 - 它需要在 RUN 时知道 ResponseType
    • @HannahLouisaCarney hehehehe Bhai 它总是有效的,你做错了。这是很好的测试。投票前三思
    【解决方案3】:

    您需要使用泛型。这样,您就可以将所需的类型传递给调用。

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

    顺便说一句,我不是改装专家(我主要使用自己的东西),但我认为你用错了。

    【讨论】:

    • 这不起作用:你会得到一个java.lang.IllegalArgumentException: Method return type must not include a type variable or wildcard: retrofit2.Call&lt;T&gt; 解决这个问题,首先使用 JsonElement 作为返回类型,然后再转换为特定类型:val userInfo = Gson().fromJson(jsonElement?.asJsonObject.toString(), UserInfo::class.java)
    【解决方案4】:

    首先创建接口:

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

    【讨论】:

    • 需要为每个请求创建新客户端吗?
    • 我在我的应用程序中使用它。而且我没有优化的方法来做到这一点
    • @Bhargav 如何使用单例模式来使用 apiclient 类?
    • 如果你理解单例模式,那么if (null != retrofit) return retrofit; 否则,做其他事情来创建客户端
    • 啊,你已经对retrofit 进行了空检查,只需将 okhttp 构建器类的实例化移动到 if 块中
    【解决方案5】:

    我的方法是创建一个名为 ResponseData 的 POJO,其中您将拥有一个属性 Object,因此您有:

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

    当您收到响应时,您必须将您的 response.body() 解析为所需的类。所以优点:你只有一个请求,而不是你必须解析响应。

    【讨论】:

      【解决方案6】:

      这样做:

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

      【讨论】:

      • 如何反序列化对象。运行时需要完整的信息。
      【解决方案7】:

      为了概括您想要的,您可以简单地序列化您的 POJO,然后您可以将您的 POJO 原样传递给方法。 当你使用 Objects 序列化时,它基本上将其转换为字符串,然后再转换为一个大的 Json 字符串,这样更易​​于传输和操作。

      一个简单的例子是:

      示例 POJO 实现序列化,这里你应该确保 Map&lt;String,Object&gt; 中的字符串对应于服务器期望得到的,并且这个方法在每个 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....
       }
      

      序列化接口(因此您可以跨其他 POJO 实现它)

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

      我通过侦听器返回响应,这是您可以根据响应执行的操作的一个示例。

      希望这会有所帮助!

      【讨论】:

        【解决方案8】:

        在响应中使用 JsonElement 会有所帮助:

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

        【讨论】:

          【解决方案9】:

          使用标准的泛型,并进行一些修改

          这样定义你的界面

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

          并调用创建api客户端使用辅助方法

          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 提供的整个类型安全和合同验证……这实际上是它最令人兴奋的事情..

          【讨论】:

          • 嘿@koperko,我们如何将 OnResponse 和 OnError 等回调方法分开,即我不想将它们包含在我的 Activity/Fragment 中
          【解决方案10】:

          我使用以下方法: 首先我实现了自定义调用

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

          它包装Call&lt;Tin&gt; 并通过转换器将其结果转换为&lt;Tout&gt;

          @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 到任何类型 &lt;T&gt; 的转换器:

          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 for List&lt;T&gt;

          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
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-10-27
            • 1970-01-01
            • 2016-12-09
            • 2018-04-06
            • 1970-01-01
            • 2018-11-24
            • 2011-09-17
            相关资源
            最近更新 更多