Получить статус погоды в нескольких местах внутри цикла for android

Я разрабатываю погодное приложение, для которого я использую API темного неба, в котором я хочу знать состояние погоды в нескольких местах, которые я сохранил в ArrayList<LatLng>.

Я использую OKHttp для анализа данных JSON из API, поэтому я попытался зациклить весь процесс выборки внутри цикла for, но он не дает желаемого результата.

private void beginTask(ArrayList<LatLng> arrayLis) { 
    //arraylis contains list of locations(LatLng)
    m = 0;
    startTask = true;

    for (int i = 0;i<arrayLis.size();i++) {
        double latitude = ((LatLng)arrayLis.get(i)).latitude;
        double longitude = ((LatLng)arrayLis.get(i)).longitude;
        String url = "https://api.darksky.net/forecast/APIKEY/"
                +latitude+","+longitude+"?units=si";
        LatLng mylatlng = new LatLng(latitude,longitude);
        startProcess(url,mylatlng);

        Log.i("GGGTT",""+latitude+", "+longitude);
    }
}

private void startProcess(String myurl, final LatLng myLatlng){
    OkHttpClient httpClient = new OkHttpClient();
    Request request = new Request.Builder()
            .url(myurl)
            .build();

    Call call  = httpClient.newCall(request);
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            String data = response.body().string();
            Log.i("DATASS",data);
            if (response.isSuccessful()){
                try {
                    getCurrentDetails(data,myLatlng);
                } catch (JSONException e){
                    e.printStackTrace();
                }
            }
        }
    });
}

private void getCurrentDetails(String data,LatLng myLatlng) throws JSONException{
    JSONObject main = new JSONObject(data);
    double la = main.getDouble("latitude");
    double lon = main.getDouble("longitude");
    JSONObject currently = main.getJSONObject("currently");
    String summary = currently.getString("summary");
    double temperature = currently.getDouble("temperature");
    String icon = currently.getString("icon");

    LatLng latLngo = new LatLng(la,lon);
    ter.add(latLngo);
    weatherInfo.add(icon);

    // Output here is not in the same order that I have passed 

    Log.i("LETSCHECK",""+la+", "+lon +icon);
}

Я передаю значения как:

  1. 19.21111,73.07729
  2. 19.20238,73.06582
  3. 19.19383,73.05362
  4. 19.18848,73.04221

Но вывод не в том же порядке внутри метода getCurrentDetails

  1. 19.19383,73.05362
  2. 19.20238,73.06582
  3. 19.18848,73.04221
  4. 19.21111,73.07729

Я думаю, что метод не ждет завершения предыдущего цикла.

Есть ли решения для получения состояния погоды всех мест, хранящихся в ArrayList, без изменения их порядка?

ИЗМЕНИТЬ

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

private void getCurrentDetails(String data,LatLng myLatlng) throws JSONException{

    JSONObject main = new JSONObject(data);
    double la = main.getDouble("latitude");
    double lon = main.getDouble("longitude");
    JSONObject currently = main.getJSONObject("currently");
    String summary = currently.getString("summary");
    double temperature = currently.getDouble("temperature");
    String icon = currently.getString("icon");

    //Log.i("LETSCHECK",""+la+", "+lon +icon+",k");

    loopi++;
    Log.i("LETSCHECK",""+loopi);
    if (loopi < arrayList.size()) {
        getItAgain();
    } else if (loopi == arrayList.size()){
        for (String l:weatherInfo){
            Log.i("LETSCHECK",l); 
            //expected 4 items to show but its showing only 2 items
        }
    }

    Log.i("LETSCHECK",""+la+", "+lon +icon);
    weatherInfo.add(icon); 
}

private void getItAgain() {
    double latitude = ((LatLng)arrayList.get(loopi)).latitude;
    double longitude = ((LatLng)arrayList.get(loopi)).longitude;
    String url = "https://api.darksky.net/forecast/74d8feeda5ecf5ee6667d034778b239d/"
            +latitude+","+longitude+"?units=si";
    LatLng mylatlng = new LatLng(latitude,longitude);
    startProcess(url,mylatlng);
}             

person Adarsh    schedule 10.02.2019    source источник
comment
Это происходит из-за того, что сетевой вызов является асинхронным. Что вы можете сделать, так это без вызова внутри цикла for вы можете выполнять сетевой вызов один за другим. Если первый сетевой вызов завершен, удалите элемент из массива и выполните следующий сетевой вызов.   -  person Ishan Fernando    schedule 10.02.2019
comment
@IshanFernando Привет, спасибо за ваше предложение. Если вы не возражаете, можете ли вы дать мне пример кода, который может мне помочь. Спасибо   -  person Adarsh    schedule 10.02.2019


Ответы (3)


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

Если вы хотите дождаться ответа, чтобы сохранить отсортированный способ, вам может потребоваться сделать следующее.

// Declare a global variable of the list of your locations. 
ArrayList<LatLng> arrayLis; 
int i = 0; 

// Just start the first task without the loop. 
double latitude = ((LatLng)arrayLis.get(i)).latitude;
double longitude = ((LatLng)arrayLis.get(i)).longitude;
String url = "https://api.darksky.net/forecast/APIKEY/"
        +latitude+","+longitude+"?units=si";
LatLng mylatlng = new LatLng(latitude,longitude);
startProcess(url,mylatlng);

Теперь в функции getCurrentDetails вам, возможно, придется сделать следующее.

private void getCurrentDetails(String data,LatLng myLatlng) throws JSONException{
    JSONObject main = new JSONObject(data);
    double la = main.getDouble("latitude");
    double lon = main.getDouble("longitude");
    JSONObject currently = main.getJSONObject("currently");
    String summary = currently.getString("summary");
    double temperature = currently.getDouble("temperature");
    String icon = currently.getString("icon");

    LatLng latLngo = new LatLng(la,lon);
    ter.add(latLngo);
    weatherInfo.add(icon);

    // Now initiate the next call
    i++;
    if(i < arrayLis.size()) getItAgain();

    Log.i("LETSCHECK",""+la+", "+lon +icon);
}

public void getItAgain() {
    // Just start the first task without the loop. 
    double latitude = ((LatLng)arrayLis.get(i)).latitude;
    double longitude = ((LatLng)arrayLis.get(i)).longitude;
    String url = "https://api.darksky.net/forecast/APIKEY/"
            +latitude+","+longitude+"?units=si";
    LatLng mylatlng = new LatLng(latitude,longitude);
    startProcess(url,mylatlng);
}

Обновить

Я не понимаю, почему он показывает 2 вместо 4 результатов. Однако я думаю, что вам нужно изменить код следующим образом.

private void getCurrentDetails(String data,LatLng myLatlng) throws JSONException {

    JSONObject main = new JSONObject(data);
    double la = main.getDouble("latitude");
    double lon = main.getDouble("longitude");
    JSONObject currently = main.getJSONObject("currently");
    String summary = currently.getString("summary");
    double temperature = currently.getDouble("temperature");
    String icon = currently.getString("icon");

    // Move this upto here
    weatherInfo.add(icon);

    loopi++;
    Log.i("LETSCHECK",""+loopi);
    if (loopi < arrayList.size()) {
        getItAgain();
    } else {
        for (String l:weatherInfo) {
            Log.i("LETSCHECK",l); 
            //expected 4 items to show but its showing only 2 items
        }
    }
}
person Reaz Murshed    schedule 10.02.2019
comment
привет Reaz спасибо за вашу помощь, позвольте мне попробовать, и я дам вам знать - person Adarsh; 10.02.2019
comment
Должен ли я перенести весь этот код в класс IntentService, чтобы он не блокировал весь основной пользовательский интерфейс - person Adarsh; 10.02.2019
comment
Нет, это не блокирует основной поток пользовательского интерфейса. Это уже асинхронно. Дайте мне знать, если это поможет! - person Reaz Murshed; 10.02.2019
comment
Привет, я использовал ваше решение, и оно отлично работает, но есть еще одна проблема, пожалуйста, загляните в мой раздел EDIT. - person Adarsh; 10.02.2019
comment
да, конечно, но не могли бы вы разобраться в вышеуказанной проблеме - person Adarsh; 10.02.2019
comment
Пожалуйста, посмотрите обновление моего ответа и дайте мне знать, если это поможет! - person Reaz Murshed; 10.02.2019

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

Вы можете либо использовать RxJava (и его операторы), либо улучшить текущий код. Для вашего startProcess передайте i из цикла for в качестве индекса и создайте массив вне нашей функции. Всякий раз, когда вы получаете ответ от сервера, вы можете сохранить его в i позиции вашего массива. После каждого вызова вы можете проверить, все ли значения были получены (с помощью счетчика или чего-то еще). Гораздо лучше переместить все это в отдельный класс, чтобы он содержался и тестировался.

Следует отметить, как вы собираетесь обрабатывать ошибки, потому что ваши звонки могут быть не приняты успешно.

person Adib Faramarzi    schedule 10.02.2019
comment
Спасибо за вашу помощь, я дам вам знать, если это сработает. - person Adarsh; 10.02.2019

Это пример кода. После каждого успешного вызова удалите текущий элемент обработки из массива и выполните другой запрос API.

private void startProcess(ArrayList<LatLong> elementList){
LatLong myLatlng = elementList.get(0);
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
        .url(myurl)
        .build();
Call call  = httpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {

    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

        String data = response.body().string();
        Log.i("DATASS",data);
        if (response.isSuccessful()){
            try {
                getCurrentDetails(data,myLatlng);
                elementList.remove(0)
                startProcess(elementList)
            }catch (JSONException e){
                e.printStackTrace();
            }


        }
    }

});

}

person Ishan Fernando    schedule 10.02.2019
comment
привет, спасибо за публикацию кода, я уже пробовал другое решение, и оно отлично работает. - person Adarsh; 10.02.2019