Возвращает строку из метода, использующего Retrofit 2 для java Android

#java #android #retrofit2

#java #Android #retrofit2

Вопрос:

Я пытаюсь запросить Google Maps api, чтобы получить placeId местоположения на основе широты / lng. Проблема, с которой я сталкиваюсь, заключается в том, что мой код переходит к инструкции return до того, как API ответит, и в результате возвращаемая строка имеет значение null вместо placeId из api. Кто-нибудь может помочь мне выяснить, чего мне не хватает?
ОТРЕДАКТИРОВАНО
Я изменил метод getPlaceId на возврат null и просто присвоил getPlaceId из ответа общедоступной переменной. Я получаю данные из API, но выглядит так, как будто json не анализируется, в результате переменная по-прежнему равна null.

Метод getPlaceId с использованием retrofit
ОТРЕДАКТИРОВАНО

 String lat = "39.748378", lng = "-91.857558", placeId;

 Map<String, String> query = new HashMap<>();
    query.put("latlng", lat "," lng);
    query.put("key","API_KEY");

public void getPlaceId(final Map<String, String> query) {
    String BASE_URL = "https://maps.googleapis.com/maps/";
    Retrofit retrofit;

    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(logging)
            .build();

    retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();
    PlacesInterface service = retrofit.create(PlacesInterface.class);
    Call<PlacesFoo> call = service.loadPlace(query);
    call.enqueue(new Callback<PlacesFoo>() {
      @Override
      public void onResponse(Call<PlacesFoo> call, Response<PlacesFoo> response) {
        if(response.isSuccessful()) {
          Log.e("getPlaceId", " Valid Response: "   response.body());
          placeId = response.body().getResults().get(0).getPlaceId();
        } else {
          System.out.println(response.errorBody());
        }
      }

      @Override
      public void onFailure(Call<PlacesFoo> call, Throwable t) {
        t.printStackTrace();
        Log.e("getPlaceId", " Error Response: "   t);
      }
    });
    //return placeId;
  }
  

Интерфейс для API

 public interface PlacesInterface {
   
    // https://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452amp;key=API_KEY
    @GET("api/geocode/json")
    Call<PlacesFoo> loadPlace(@QueryMap Map<String, String> options);

}
  

POJO для ответа API

 public class PlacesFoo {

    @SerializedName("plus_code")
    @Expose
    private PlusCode plusCode;
    @SerializedName("results")
    @Expose
    private List<Result> results = null;
    @SerializedName("status")
    @Expose
    private String status;

    public PlusCode getPlusCode() {
        return plusCode;
    }

    public void setPlusCode(PlusCode plusCode) {
        this.plusCode = plusCode;
    }

    public List<Result> getResults() {
        return results;
    }

    public void setResults(List<Result> results) {
        this.results = results;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public class AddressComponent {

        @SerializedName("long_name")
        @Expose
        private String longName;
        @SerializedName("short_name")
        @Expose
        private String shortName;
        @SerializedName("types")
        @Expose
        private List<String> types = null;

        public String getLongName() {
            return longName;
        }

        public void setLongName(String longName) {
            this.longName = longName;
        }

        public String getShortName() {
            return shortName;
        }

        public void setShortName(String shortName) {
            this.shortName = shortName;
        }

        public List<String> getTypes() {
            return types;
        }

        public void setTypes(List<String> types) {
            this.types = types;
        }

    }

    public class Bounds {

        @SerializedName("northeast")
        @Expose
        private Northeast_ northeast;
        @SerializedName("southwest")
        @Expose
        private Southwest_ southwest;

        public Northeast_ getNortheast() {
            return northeast;
        }

        public void setNortheast(Northeast_ northeast) {
            this.northeast = northeast;
        }

        public Southwest_ getSouthwest() {
            return southwest;
        }

        public void setSouthwest(Southwest_ southwest) {
            this.southwest = southwest;
        }

    }

    public class Geometry {

        @SerializedName("location")
        @Expose
        private Location location;
        @SerializedName("location_type")
        @Expose
        private String locationType;
        @SerializedName("viewport")
        @Expose
        private Viewport viewport;
        @SerializedName("bounds")
        @Expose
        private Bounds bounds;

        public Location getLocation() {
            return location;
        }

        public void setLocation(Location location) {
            this.location = location;
        }

        public String getLocationType() {
            return locationType;
        }

        public void setLocationType(String locationType) {
            this.locationType = locationType;
        }

        public Viewport getViewport() {
            return viewport;
        }

        public void setViewport(Viewport viewport) {
            this.viewport = viewport;
        }

        public Bounds getBounds() {
            return bounds;
        }

        public void setBounds(Bounds bounds) {
            this.bounds = bounds;
        }

    }

    public class Location {

        @SerializedName("lat")
        @Expose
        private Double lat;
        @SerializedName("lng")
        @Expose
        private Double lng;

        public Double getLat() {
            return lat;
        }

        public void setLat(Double lat) {
            this.lat = lat;
        }

        public Double getLng() {
            return lng;
        }

        public void setLng(Double lng) {
            this.lng = lng;
        }

    }

    public class Northeast {

        @SerializedName("lat")
        @Expose
        private Double lat;
        @SerializedName("lng")
        @Expose
        private Double lng;

        public Double getLat() {
            return lat;
        }

        public void setLat(Double lat) {
            this.lat = lat;
        }

        public Double getLng() {
            return lng;
        }

        public void setLng(Double lng) {
            this.lng = lng;
        }

    }

    public class Northeast_ {

        @SerializedName("lat")
        @Expose
        private Double lat;
        @SerializedName("lng")
        @Expose
        private Double lng;

        public Double getLat() {
            return lat;
        }

        public void setLat(Double lat) {
            this.lat = lat;
        }

        public Double getLng() {
            return lng;
        }

        public void setLng(Double lng) {
            this.lng = lng;
        }

    }

    public class PlusCode {

        @SerializedName("compound_code")
        @Expose
        private String compoundCode;
        @SerializedName("global_code")
        @Expose
        private String globalCode;

        public String getCompoundCode() {
            return compoundCode;
        }

        public void setCompoundCode(String compoundCode) {
            this.compoundCode = compoundCode;
        }

        public String getGlobalCode() {
            return globalCode;
        }

        public void setGlobalCode(String globalCode) {
            this.globalCode = globalCode;
        }

    }

    public class PlusCode_ {

        @SerializedName("compound_code")
        @Expose
        private String compoundCode;
        @SerializedName("global_code")
        @Expose
        private String globalCode;

        public String getCompoundCode() {
            return compoundCode;
        }

        public void setCompoundCode(String compoundCode) {
            this.compoundCode = compoundCode;
        }

        public String getGlobalCode() {
            return globalCode;
        }

        public void setGlobalCode(String globalCode) {
            this.globalCode = globalCode;
        }

    }

    public class Result {

        @SerializedName("address_components")
        @Expose
        private List<AddressComponent> addressComponents = null;
        @SerializedName("formatted_address")
        @Expose
        private String formattedAddress;
        @SerializedName("geometry")
        @Expose
        private Geometry geometry;
        @SerializedName("place_id")
        @Expose
        private String placeId;
        @SerializedName("plus_code")
        @Expose
        private PlusCode_ plusCode;
        @SerializedName("types")
        @Expose
        private List<String> types = null;

        public List<AddressComponent> getAddressComponents() {
            return addressComponents;
        }

        public void setAddressComponents(List<AddressComponent> addressComponents) {
            this.addressComponents = addressComponents;
        }

        public String getFormattedAddress() {
            return formattedAddress;
        }

        public void setFormattedAddress(String formattedAddress) {
            this.formattedAddress = formattedAddress;
        }

        public Geometry getGeometry() {
            return geometry;
        }

        public void setGeometry(Geometry geometry) {
            this.geometry = geometry;
        }

        public String getPlaceId() {
            return placeId;
        }

        public void setPlaceId(String placeId) {
            this.placeId = placeId;
        }

        public PlusCode_ getPlusCode() {
            return plusCode;
        }

        public void setPlusCode(PlusCode_ plusCode) {
            this.plusCode = plusCode;
        }

        public List<String> getTypes() {
            return types;
        }

        public void setTypes(List<String> types) {
            this.types = types;
        }

    }

    public class Southwest {

        @SerializedName("lat")
        @Expose
        private Double lat;
        @SerializedName("lng")
        @Expose
        private Double lng;

        public Double getLat() {
            return lat;
        }

        public void setLat(Double lat) {
            this.lat = lat;
        }

        public Double getLng() {
            return lng;
        }

        public void setLng(Double lng) {
            this.lng = lng;
        }

    }

    public class Southwest_ {

        @SerializedName("lat")
        @Expose
        private Double lat;
        @SerializedName("lng")
        @Expose
        private Double lng;

        public Double getLat() {
            return lat;
        }

        public void setLat(Double lat) {
            this.lat = lat;
        }

        public Double getLng() {
            return lng;
        }

        public void setLng(Double lng) {
            this.lng = lng;
        }

    }

    public class Viewport {

        @SerializedName("northeast")
        @Expose
        private Northeast northeast;
        @SerializedName("southwest")
        @Expose
        private Southwest southwest;

        public Northeast getNortheast() {
            return northeast;
        }

        public void setNortheast(Northeast northeast) {
            this.northeast = northeast;
        }

        public Southwest getSouthwest() {
            return southwest;
        }

        public void setSouthwest(Southwest southwest) {
            this.southwest = southwest;
        }
    }
}
  

Комментарии:

1. Вы не можете получить placeId из возвращаемого значения метода, сначала выполняется «return placeId;», затем вызывается метод onResponse или OnFailure. вы должны создать интерфейс (например, OnPlaceIdFoundListener) и изменить сигнатуру вашего метода на getPlaceId (запрос окончательной карты<Строка, String>, прослушиватель OnPlaceIdFoundListener)

2. @abbasoveissi, можете ли вы помочь мне с примером или местом для ссылки?

Ответ №1:

Retrofit поддерживает синхронное и асинхронное выполнение запросов. Пользователи определяют конкретное выполнение, задавая тип возвращаемого значения (синхронный) или нет (асинхронный) для методов обслуживания.

Редактировать Вам нужно передать результат от асинхронного вызова через интерфейс

  public interface MyCallback {
    void onSuccess(String placeId);
    void onError(String error);
}
  

и ваш вызов будет выглядеть

 getPlaceId(new MyCallback() {
        @Override
        public void onSuccess(String placeId) {
            // here you get place Id you can do your work with id
            Log.e("PlaceID",placeId);
        }

        @Override
        public void onError(String error) {
            // in case of error occurred
        }
    });
  

Вызов Retrofit

   private void getPlaceId(MyCallback myCallback) {
    .....
    .....
    PlacesInterface service = retrofit.create(PlacesInterface .class);
    Call<PlacesFoo> call = service.loadPlace(query);
    call.enqueue(new Callback<PlacesFoo>() {
        @Override
        public void onResponse(@NotNull Call<PlacesFoo> call, @NotNull Response<PlacesFoo> response) {
            if (response.isSuccessful()) {
                String placeId = response.body().getResults().get(0).getPlaceId();
                myCallback.onSuccess(placeId);

            } else {
                if (response.errorBody() != null) {
                    myCallback.onError(response.errorBody().toString());
                }

            }
        }

        @Override
        public void onFailure(@NotNull Call<PlacesFoo> call, @NotNull Throwable t) {
            t.printStackTrace();
            myCallback.onError(t.getLocalizedMessage());
        }
    });

}
  

чтобы узнать разницу между асинхронным и синхронным запросом, проверьте это

Комментарии:

1. спасибо за ваш ответ. Я сделал, как вы предложили, но теперь я получаю: I/okhttp.OkHttpClient: <-- HTTP FAILED: android.os.NetworkOnMainThreadException @Mohammed

2. Да, вы правы, вы определенно получите это исключение, если только вы не запустите код в фоновом потоке, я отредактировал код с помощью асинхронного вызова, который выполняется в фоновом режиме, и передал результат обратно через интерфейс

3. было бы здорово, если бы я мог просто присвоить переменной getPlaceId из тела ответа. Было бы это неверно? @Mohammed

4. если вы присвоите ее переменной, а затем вернете эту переменную, она снова будет равна null, как вы указали в вопросе, поскольку вызову api требуется время для возврата ответа, поскольку я отредактировал ответ, вы можете выполнить свой код, зависящий от placeId in onSuccess , также, если вы используете глобальную переменную, вам нужно проверить, присвоено ли ей значение, прежде чем использовать ее

5. Я понимаю, о чем вы сейчас говорите, но как мне передать значения карты моего запроса в вызов retrofit? Также похоже, что сейчас я получаю данные json, но это не запускает onResponse или onSuccess @Mohammed

Ответ №2:

сначала создайте интерфейс, подобный приведенному ниже:

 public interface OnPlaceIdFoundListener {
    void onPlaceIdFound(String placeId);
}
  

во-вторых, вы должны изменить подпись и тело метода getPlaceId, как показано ниже (см. знак «@@здесь @@!!!!!»):

 public void getPlaceId(final Map<String, String> query, OnPlaceIdFoundListener callback) {
    String BASE_URL = "https://maps.googleapis.com/maps/";
    Retrofit retrofit;

    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(logging)
            .build();

    retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();
    PlacesInterface service = retrofit.create(PlacesInterface.class);
    Call<PlacesFoo> call = service.loadPlace(query);
    call.enqueue(new Callback<PlacesFoo>() {
        @Override
        public void onResponse(Call<PlacesFoo> call, Response<PlacesFoo> response) {
            if(response.isSuccessful()) {
                Log.e("getPlaceId", " Valid Response: "   response.body());
                String placeId = response.body().getResults().get(0).getPlaceId();
                callback.onPlaceIdFound(placeId); // @@here@@!!!!!
            } else {
                System.out.println(response.errorBody());
                callback.onPlaceIdFound(""); // @@here@@!!!!!

            }
        }

        @Override
        public void onFailure(Call<PlacesFoo> call, Throwable t) {
            t.printStackTrace();
            callback.onPlaceIdFound(""); // @@here@@!!!!!
            Log.e("getPlaceId", " Error Response: "   t);
        }
    });
    //return placeId;
}
  

затем используйте свой метод.

 getPlaceId(query, new OnPlaceIdFoundListener() {
    @Override
    public void onPlaceIdFound(String placeId) {
         Log.d("TAG", "place id is found = "  placeId);
    }
});