wake-up-neo.com

Android: Übergeben Sie die Modellklasse dynamisch, um den Callback nachzurüsten

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

11
Sushil Kumar

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.

1
Fco P.

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

            }
        }
1
Sahil Munjal

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

            }
        });
1
Divyesh Patel

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!

1
TommySM

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);
}
0
Irfan Ul Haq

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
    }
0
Tselofan

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 .. 

0
koperko