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

Posted

技术标签:

【中文标题】Android:动态传递模型类来改造回调【英文标题】:Android:dynamically pass model class to retrofit callback 【发布时间】: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 看看答案,我想它对你有帮助.... 【参考方案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
    

【讨论】:

以上是关于Android:动态传递模型类来改造回调的主要内容,如果未能解决你的问题,请参考以下文章

Java通过匿名类来实现回调函数

使用 Android 改造解析动态键 JSON 数组

如何在正文中传递api参数而不是@Query标签android kotlin改造

如何在 Android 的改造请求中传递 JSON 对象并检索 JSON 对象?

如何使用改造 Android 调用具有动态参数的 api?

零散的东西