Android:dynamically pass model class to retrofit callback

后端 未结 10 1721
伪装坚强ぢ
伪装坚强ぢ 2020-12-05 04:34

In retrofit to map json response to pojo usually we do this

@POST
Call getDataFromServer(@Url String url, @Body HashMap ha         


        
相关标签:
10条回答
  • 2020-12-05 05:07

    I use following approach: First I have implemented custom Call

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

    It wrappes Call<Tin> and converts it's result to <Tout> by converter.

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

    For your service you must create service interface, that return JsonObject for single object and JsonArray for arrays

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

    Then wrap it with generic class, with converters from JsonElement to any 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 is ParaterizedType. It is used for passing type parameter to gson for 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;
        }
    
    }
    

    Then you can instantiate ApiCallsGeneric with type you want.

    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 讨论(0)
  • 2020-12-05 05:08

    Do it like this :

        @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) {
    
                }
            }
    
    0 讨论(0)
  • 2020-12-05 05:08

    In Order to generalize what you want, you can simply serialize your POJO, and then you can just pass your POJO to the method as is. when you serialize with Objects it basically converts it to string, which are later converted to one big Json String, which are easier to transfer and manipulate.

    A quick example would be:

    example POJO implementing the serialization, here you should make sure the strings in the Map<String,Object> correspond to what the server is expecting to get, and this method should be different in each 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....
     }
    

    The serialization interface (so you can implement it across other POJOs)

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

    And a Json parser you shoul probably have anyways:

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

    And last, your API defintion:

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

    And method, which should sit in a general class that manages your requests:

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

    I return the response through a listener, that's one example of what you can do depending on your response.

    Hope this helps!

    0 讨论(0)
  • 2020-12-05 05:09

    Android:dynamically pass model class to retrofit callback

    There is 2 ways you can do this .........

    1. Generics

    2. Combine all POJO into one ......

    Generics

    In the Generics you have to pass the method with the class. pleas have look on example .....

    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:- Above retrofit call TypeCast your response into YOUR OBJECT, so you can access its field and methods

    Combine all POJO into one

    It is very easy to use . You have to combine your all POJO class into one and use them inside the Retrofit. please have look on below example ....

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

    In Login API i have get JSON response like this ...

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

    above JSON , POJO look like this

    public class LoginResult{
    
        private String message;
        private boolean success;
    
        //constructor , getter and setter 
    }
    

    and Retrofit call look like this .....

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

    In User API i have get JSON response like this ...

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

    above JSON , POJO look like this

     public class UserResult{
    
            private String name;
            private String place;
    
            //constructor , getter and setter 
        }
    

    and Retrofit call look like this .....

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

    Just combine both of above JSON response into one .....

    public class Result{
    
                private String name;
                private String place;
                private String message;
                private boolean success;
    
                //constructor , getter and setter 
            }
    

    and use Result inside Your API call ......

      call.enqueue(new Callback<Result>() {
                @Override
                public void onResponse(Call<Result> call, Response<Result> response) {
    
    
                }
    
                @Override
                public void onFailure(Call<Result> call, Throwable t) {
    
                }
            }); 
    

    Note:- You directly combine your 2 POJO class and accessing it. (This is very complicate if you have response very large and provide duplication if some KEY is same with different Variable type )

    0 讨论(0)
  • 2020-12-05 05:10

    My approach is make a POJO called ResponseData in which you will have an attribute Object, so you have:

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

    When you get the response you have to parse your response.body() to the desired class. So the pros: you just have one request, instead you have to parse the response.

    0 讨论(0)
  • 2020-12-05 05:22

    using JsonElement in Response would help:

         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 讨论(0)
提交回复
热议问题