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您需要使用泛型。这样,您就可以将所需的类型传递给调用。
@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<T>
解决这个问题,首先使用 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<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....
序列化接口(因此您可以跨其他 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<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 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;
然后你可以用你想要的类型实例化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:动态传递模型类来改造回调的主要内容,如果未能解决你的问题,请参考以下文章
如何在正文中传递api参数而不是@Query标签android kotlin改造