Bei der Nachrüstung der Json-Antwort auf Pojo tun wir dies normalerweise
@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
}
dabei ist User meine Pojo-Klasse ..__ Aber für jede andere Anfrage muss ich einen anderen Pojo erstellen und denselben Code für den Retrofit-Aufruf schreiben. so was
ApiCalls api = retrofit.create(ApiCalls.class);
Call<*ClassPassed*> call = api.getDataFromServer(StringConstants.URL,hashMap);
call.enqueue(new Callback<*ClassPassed*>() {
//Response and failure callbacks
}
so kann ich jetzt jede pojo-Klasse für eine einzelne Methode verwenden und die Antwort erhalten. Das ist nur, um zu vermeiden, dass derselbe Code immer wieder neu geschrieben wird
Update Um mehr zu erfahren:
Angenommen, ich muss zwei Anfragen stellen. Das erste ist, userDetails zu bekommen, und das andere ist patientDetails. Also muss ich zwei Modellklassen erstellen: User und Patient . Im Retrofit api werde ich so etwas haben
@POST
Call<User> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
@POST
Call<Patient> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
und in meiner Klasse FragmentUser und FragmentPatient mache ich das
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
}
aber hier ist der Code nur aus verschiedenen Pojo-Klassen wiederhergestellt. Ich muss den gleichen Code in allen anderen Fragmenten für unterschiedliche Anforderungen wiederholen .. Daher muss ich eine generische Methode erstellen, in der jede Pojo-Klasse und dann das Fragment akzeptiert werden kann Ich werde nur den Pojo übergeben, um gemappt zu werden.
Android: Modellklasse dynamisch an Retrofit-Callback übergeben
Es gibt 2 Möglichkeiten dies zu tun .........
1. Generika
2. Kombiniere alle POJO zu einem ......
Generika
In den Generics müssen Sie die Methode mit der Klasse übergeben. bitte schau mal nach beispiel .....
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) {
}
});
}
HINWEIS: - Über Retrofit rufen Sie TypeCast Ihre Antwort in YOUR OBJECT
, damit Sie auf das Feld und die Methoden zugreifen können
Kombiniere alle POJO zu einem
Es ist sehr einfach zu bedienen. Sie müssen Ihre gesamte POJO-Klasse in einer kombinieren und im Rahmen des Retrofit verwenden. Bitte schauen Sie sich das folgende Beispiel an ....
Ich habe zwei API-Login und Benutzer ......
In Login API habe ich JSON-Antwort wie folgt erhalten ...
{"success": True, "message": "Authentication successful"}
über JSON sieht POJO so aus
public class LoginResult{
private String message;
private boolean success;
//constructor , getter and setter
}
nd Nachrüstaufruf sieht so aus .....
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 der Benutzer-API habe ich JSON-Antwort wie folgt erhalten ...
{"name": "sushildlh", "place": "hyderabad"}
über JSON sieht POJO so aus
public class UserResult{
private String name;
private String place;
//constructor , getter and setter
}
nd Nachrüstaufruf sieht so aus .....
call.enqueue(new Callback<UserResult>() {
@Override
public void onResponse(Call<UserResult> call, Response<UserResult> response) {
}
@Override
public void onFailure(Call<UserResult> call, Throwable t) {
}
});
Kombinieren Sie einfach beide obigen JSON-Antworten zu einer .....
public class Result{
private String name;
private String place;
private String message;
private boolean success;
//constructor , getter and setter
}
nd benutze Result in Deinem API-Aufruf ......
call.enqueue(new Callback<Result>() {
@Override
public void onResponse(Call<Result> call, Response<Result> response) {
}
@Override
public void onFailure(Call<Result> call, Throwable t) {
}
});
Hinweis: - Sie kombinieren Ihre 2 POJO-Klasse direkt und greifen auf sie zu. (Dies ist sehr kompliziert, wenn Sie eine sehr große Antwort haben und wenn einige KEYs mit unterschiedlichen Variablentypen identisch sind
Sie können Haupt-Pojo so bauen
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;
}
}
Und so anrufen
Call<BaseResponse<SetupDetails>> getSetup(@Query("site_id") int id,@Query("ino") String ii);
Sie müssen Generika verwenden. Auf diese Weise können Sie den gewünschten Typ an den Anruf übergeben.
@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
}
Ich bin übrigens kein Retrofit-Experte (ich benutze meistens meine eigenen Sachen), aber ich denke, Sie benutzen es falsch.
Mach es so :
@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) {
}
}
Erstes Interface erstellen:
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);
}
Erstellen Sie jetzt eine Klasse:
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;
}
Verwenden Sie jetzt in jeder Aktivität einfach:
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) {
}
});
Um zu verallgemeinern, was Sie möchten, können Sie Ihr POJO einfach serialisieren, und dann können Sie Ihr POJO so an die Methode übergeben, wie es ist, wenn Sie es mit Objekten serialisieren großer Json String, der einfacher zu übertragen und zu manipulieren ist.
Ein schnelles Beispiel wäre:
beispiel POJO, das die Serialisierung implementiert, hier sollten Sie sicherstellen, dass die Zeichenfolgen in Map<String,Object>
dem entsprechen, was der Server erwartet, und diese Methode sollte in jedem POJO anders sein:
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....
}
Die Serialisierungsschnittstelle (damit Sie sie in anderen POJOs implementieren können)
public interface ObjectSerializer extends Serializable
{
public Map<String, Object> objectSerialize();
}
Und einen Json-Parser, den Sie wahrscheinlich haben sollten:
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);
}
}
Und zuletzt Ihre API-Definition:
@POST("users/createitem")
Call<ResponseBody> someCall(@Body RequestBody params);
Und Methode, die in einer allgemeinen Klasse sitzen sollte, die Ihre Anfragen verwaltet:
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)
{
}
});
}
Ich gebe die Antwort über einen Listener zurück. Dies ist ein Beispiel dafür, was Sie je nach Ihrer Antwort tun können.
Hoffe das hilft!
Mein Ansatz ist ein POJO namens ResponseData, in dem Sie ein Attribut Object haben werden.
@POST
Call<ResponseData> getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
Wenn Sie die Antwort erhalten, müssen Sie Ihre Antwort.body () zur gewünschten Klasse analysieren. Also die Profis: Sie haben nur eine Anfrage, stattdessen müssen Sie die Antwort analysieren.
die Verwendung von JsonElement als Antwort würde helfen:
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);
}
Ich verwende den folgenden Ansatz: Zuerst habe ich einen benutzerdefinierten Anruf implementiert
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();
}
}
Es umschließt Call<Tin>
und konvertiert sein Ergebnis durch Konverter in <Tout>
.
@FunctionalInterface
public interface Converter<Tin, Tout> {
public Tout Convert(Tin in);
}
Für Ihren Dienst müssen Sie eine Dienstschnittstelle erstellen, die JsonObject für ein einzelnes Objekt und JsonArray für Arrays zurückgibt
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);
}
Umschließen Sie es dann mit einer generischen Klasse mit Konvertern von JsonElement in einen beliebigen Typ <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 ist ParaterizedType. Es wird für die Übergabe des Typparameters an gson für List<T>
verwendet.
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;
}
}
Dann können Sie ApiCallsGeneric
mit dem gewünschten Typ instanziieren.
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
}
Verwenden Sie Standard-Generics mit ein wenig Hacken
Definieren Sie Ihre Schnittstelle so
public interface ApiCalls<T> {
@POST
Call<T> getResult getDataFromServer(@Url String url, @Body HashMap<String,Object> hashMap);
}
und rufen Sie zum Erstellen eines API-Clients eine Hilfsmethode auf
class HelperMethods {
@SuppressWarnings("unchecked")
private static <T> ApiCalls<T> getClient() {
return retrofit.create((Class<ApiCalls<T>>)(Class<?>)ApiCalls.class);
}
}
ApiCalls<User> api = HelperMethods.getClient();
Aber trotz der Tatsache, wie oft hier schon gesagt wurde, werde ich es noch einmal sagen. Tun Sie das nicht. Sie geben die gesamte Typensicherheit und Vertragsgültigkeit auf, die Retrofit anbietet das Spannendste daran ..