web-dev-qa-db-ja.com

Android:モデルクラスをレトロフィットコールバックに動的に渡す

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クラスを作成し、応答を取得できます。これは、同じコードを何度も何度も書き換えないようにするためです。

更新さらに詳しく説明するには:

2つの要求を行う必要があるとします。最初の1つはuserDetailsを取得することで、もう1つはpatientDetailsです。そのため、UserとPatientという2つのモデルクラスを作成する必要があります。レトロフィット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を渡すだけです。

Android:モデルクラスをレトロフィットコールバックに動的に渡す

これを行うには2つの方法があります.........

1。ジェネリック

2。すべてのPOJOを1つに結合......

ジェネリック

Genericsでは、メソッドをクラスに渡す必要があります。嘆願書の例をご覧ください.....

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を1つに結合

使い方はとても簡単です。すべてのPOJOクラスを1つに結合して、Retrofit内で使用する必要があります。以下の例をご覧ください....

私は2つのAPIログインとユーザーを持っています......

Login APIでは、このようなJSON応答を取得しています...

{「成功」:True、「メッセージ」:「認証成功」}

JSON上、POJOは次のようになります

public class LoginResult{

    private String message;
    private boolean success;

    //constructor , getter and setter 
}

およびレトロフィット呼び出しは次のようになります.....

 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応答を取得しました..

{「名前」:「sushildlh」、「場所」:「ハイデラバード」}

JSON上、POJOは次のようになります

 public class UserResult{

        private String name;
        private String place;

        //constructor , getter and setter 
    }

およびレトロフィット呼び出しは次のようになります.....

 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応答の両方を1つに結合するだけで.....

public class Result{

            private String name;
            private String place;
            private String message;
            private boolean success;

            //constructor , getter and setter 
        }

およびAPI呼び出し......内で結果を使用

  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が異なる変数タイプと同じ場合に複製を提供する場合、これは非常に複雑です)

11
Sushil Kumar

このようにメインのポジョを作成できます

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

ジェネリックを使用する必要があります。そのようにして、目的のタイプを呼び出しに渡すことができます。

@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
    }

ちなみに、私は改造の専門家ではありません(私は自分のものを主に使用しています)が、あなたはそれを間違って使用していると思います。

1
Fco P.

このようにします:

    @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

最初の作成インターフェイス:

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

            }
        });
1
Divyesh Patel

必要なものを一般化するために、POJOをシリアル化するだけで、POJOをそのままメソッドに渡すことができます。 Objectでシリアル化すると、基本的にそれを文字列に変換し、後で1つの大きな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)
        {

        }
    });
    }

リスナーを介して応答を返します。これは、応答に応じてできることの一例です。

お役に立てれば!

1
TommySM

私のアプローチは、ResponseObjectというPOJOを作成することです。このDataObjectには属性Objectが含まれます。

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

応答を取得したら、response.body()を目的のクラスに解析する必要があります。したがって、長所:要求は1つだけで、代わりに応答を解析する必要があります。

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

私は次のアプローチを使用します:最初にカスタムコールを実装しました

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です。 List<T>のgsonに型パラメーターを渡すために使用されます

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

標準的なジェネリックを使用し、少しハッキングする

このようにインターフェースを定義します

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が提供している型の安全性と契約の検証全体を放棄しています..それは実際ですそれについて最もエキサイティングなこと..

0
koperko