Получить результат от вызова, который не возвращает результат?

Я использую API, который вызывает результаты, но не возвращает сам результат. После выполнения вызова вызывается отдельный метод (слушатель), который содержит результат. Вот пример того, чего я пытаюсь достичь:

public static void main (String[] args) {
    Object obj = someMethod();

    System.out.println("The object is" + obj + ", wooh!");
}

public void callObject() {
    // This is the call that sends a request for the object
}

public void receiveObject(Object object) {
    // This is the API method that is invoked with the result (cannot be changed)
}

// What I want to be able to do
public Object someMethod() {
    callObject();
    // return received object once received, but how?
}

callObject() не возвращает объект, а только инициирует его извлечение. Я хочу иметь метод, который вызывает объект, а затем возвращает его, когда он получен. Я изучал результаты Callables и Future, но не знаю, как их реализовать.

Есть идеи? Спасибо!


person Anonomoose    schedule 15.12.2015    source источник
comment
Ваш вопрос не ясен.   -  person Perdomoff    schedule 16.12.2015
comment
Это должно быть неблокирующим? Одним из распространенных решений является передача объекта обратного вызова в метод callObject.   -  person Elliott Frisch    schedule 16.12.2015
comment
@Perdomoff Я внесу правку, потерпите меня.   -  person Anonomoose    schedule 16.12.2015
comment
@ElliottFrisch Это не блокирует в том смысле, что другие задачи должны иметь возможность выполняться одновременно (отдельный поток), но можно ссылаться на результат, пока он ожидает результата (например, System.out.println)   -  person Anonomoose    schedule 16.12.2015
comment
Естественно, чтобы вернуть Object, полученный callObject(), callObject() также должен вернуть Object. Тогда someMethod() может просто использовать return callObject();, если только вам не нужно сначала выполнить другие операции.   -  person Calvin P.    schedule 16.12.2015
comment
@КэлвинП. Я только что внес изменения, но callObject на самом деле не извлекает объект, а только запрашивает получение объекта. Затем объект позже принимается в receiveObject().   -  person Anonomoose    schedule 16.12.2015
comment
вам разрешено переопределять этот метод receiveObject?   -  person Milan    schedule 16.12.2015
comment
@ Mil4n Да, вот как это реализовано, я могу делать с этим методом все, что хочу.   -  person Anonomoose    schedule 16.12.2015
comment
Итак, someMethod() должен блокировать?   -  person ZeroOne    schedule 16.12.2015
comment
@ZeroOne Да, я плохо объяснил, да.   -  person Anonomoose    schedule 16.12.2015
comment
Кроме того, можно ли иметь несколько потоков, вызывающих callObject() или someMethod(), или может существовать только один вызов, который выполняется одновременно?   -  person ZeroOne    schedule 16.12.2015
comment
@ZeroOne Должен выполняться одновременно с другими вызовами, но каждый из них должен блокироваться — если это имеет смысл.   -  person Anonomoose    schedule 16.12.2015
comment
И все ли обращения к API возвращают другой результат? Вы не можете использовать результат вызова API, недавно инициированный каким-то другим потоком?   -  person ZeroOne    schedule 16.12.2015
comment
@ZeroOne API возвращает разные результаты, но в том порядке, в котором был сделан вызов.   -  person Anonomoose    schedule 16.12.2015
comment
Итак, вам удалось решить вашу проблему? Если да, примите ответ, который вы считаете лучшим. :)   -  person ZeroOne    schedule 23.12.2015


Ответы (3)


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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class ToyConc1 {
    public static void main(String[] args) throws InterruptedException,
            ExecutionException {
        ExecutorService exec = Executors.newCachedThreadPool();
        try {
            Callable<String> op1 = new Callable<String>() {
                @Override
                public String call() throws Exception {
                    return slowlyGetHttpPage("http://www.google.com");
                }
            }; 
            Callable<String> op2 = new Callable<String>() {
                @Override
                public String call() throws Exception {
                    return slowlyGetHttpPage("http://edition.cnn.com/");
                }
            }; 
            Future<String> resF1 = exec.submit(op1);
            Future<String> resF2 = exec.submit(op2);

            for (int i = 0; i < 20; i++) {
                System.out.println("doing useful work on the main thread...");
            }
            System.out.println("The object 1 is " + limit(resF1.get(), 1000) + ", wooh!");
            System.out.println("The object 2 is " + limit(resF2.get(), 1000) + ", wooh!");
        } finally {
            exec.shutdown();
            exec.awaitTermination(60, TimeUnit.SECONDS);
        }
    }

    private static String slowlyGetHttpPage(String urlName) throws InterruptedException,
            IOException {
        Thread.sleep(2000);
        URL url = new URL(urlName);
        URLConnection conn = url.openConnection();
        InputStream in = conn.getInputStream();
        InputStreamReader reader = new InputStreamReader(in); // wrong charset maybe
        BufferedReader br = new BufferedReader(reader);
        StringBuilder sb = new StringBuilder();
        try {
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        } finally {
            br.close();
        }
    }

    private static String limit(String s, int length) {
        if (s == null) {
            return null;
        }
        if (s.length() <= length) {
            return s;
        }
        return s.substring(0, length) + " [and " + (s.length() - length) + " more]";
    }
}

В качестве альтернативы библиотека/фреймворк/техника, которую вы используете для отправки запроса и возврата результата, может уже дать вам что-то вроде Future или принять что-то вроде обратного вызова (например, AsyncHttpClient делает и то, и другое).

person starikoff    schedule 16.12.2015

Как насчет этого. Введите новое поле в свой класс:

private Object result;

Затем в методе receiveObject(Object object) вы устанавливаете его:

this.result = object;

В некотором методе():

callObject();
while(this.result==null) {
    Thread.sleep(100);
}
Object answer = this.result;
this.result = null;
return answer;

Должно работать нормально, если вы создаете новый экземпляр этого класса для каждого запроса API. Не пытайтесь сделать этот класс одноэлементным, это только излишне усложнит ситуацию.

Если вам действительно нужен синглтон, вы можете обобщить этот private Object result; в карту и придумать способ идентифицировать каждый отдельный вызов API, например, с некоторым идентификатором вызывающего абонента.

Наконец, вы также можете заменить private Object result; на java .util.concurrent.CompletableFuture. Затем завершите это будущее в методе receiveObject и дождитесь его (.get() оно) в someMethod. Это было бы немного более идиоматично, чем использование цикла ожидания потока.

person ZeroOne    schedule 15.12.2015

Как возможно вернуть что-то, что нечего возвращать, в someMethod(), где есть return слово, а callObject() ничего не вернуть, потому что это void. Таким образом, он вернет объект

public Object someMethod() {
    return new Object();
}
person Bahramdun Adil    schedule 16.12.2015