Mudar método assíncrono

Olá!

Então, seguinte
Tenho um método que faz o retorno dos dados que eu quero. Até aí, ele está retornando sim, mas eu preciso levar esses dados para outros lugares, para usar externo e da maneira como estou fazendo, não estou conseguindo. Alguém pode me ajudar a encontrar uma forma de escrever esse código para que eu possa levar os dados para outros lugares do projeto, que não sejam dentro desse método?

Basicamente, eu gostaria que o método deixasse de ser void e retornasse String, por exemplo
Eu sei que desta maneira que está escrita não dá, mas queria saber como posso fazer isso

public void getFrom(String url) throws Exception {
        Request request = new Request.Builder()
                .url(url)
                .build();
        //
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) { }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (response != null) {
                        String json = response.body().string();
                        JSONObject jsonObject = new JSONObject(json);
                        JSONArray jsonArray = jsonObject.getJSONArray("routes");
                        //
                        for(int i=0 ; i<jsonArray.length() ; i++) {
                            JSONObject result = jsonArray.getJSONObject(i);
                            print("RESULTADO >>> " + result.getString("name"));
                        }
                    }
                } catch (Exception e) { }
            }
        });
    }

Observação:
Essa função print é apenas um System.out.println. E o result.getString("name") está funcionando, é essa informação que eu quero fazer o método retornar.

Se você usar uma lista de Strings não funciona ?

Algo mais ou menos assim:

public List<String> onResponse(Call call, Response response) throws IOException {
	try {
		if (response != null) {
			List<String> names = new ArrayList<>(); //Lista de names
			
			String json = response.body().string();
			JSONObject jsonObject = new JSONObject(json);
			JSONArray jsonArray = jsonObject.getJSONArray("routes");
			
			for(int i=0 ; i<jsonArray.length() ; i++) {
				JSONObject result = jsonArray.getJSONObject(i);
				list.add(result.getString("name"));
			}
			
			return names;
		}
	} catch (Exception e) { 
		throw new Exception(e);
	}
}

Não posso, porque o método onResponse vem do Callback… Não posso alterá-lo

Não tinha me atentado nisso, e se caso você criar a lista externamente ao callback, declaração a nível de classe e só utilizá-la dentro do callback para recuperar os nomes ?

Cara, fui tentar isso, mas não funcionou.
Botei pra debugar, mostrou o ArrayList sendo preenchido, mas quando eu chamei ele, via método e atribuí a outro ArrayList, ele não exibiu nada.

fail

Como você está fazendo o retorno da lista dentro do método ?

// no inicio da classe eu instancio o List
List<String> strLista = new ArrayList<>();

...

public List<String> getURLFrom(String url) throws Exception {
        //
        Request request = new Request.Builder().url(url).build();
        //
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) { }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (response != null) {
                        String json = response.body().string();
                        JSONObject jsonObject = new JSONObject(json);
                        JSONArray results = jsonObject.getJSONArray("results");
                        //
                        Bundle bundle = new Bundle();
                        for(int i=0 ; i<results.length() ; i++) {
                            JSONObject routes = results.getJSONObject(i);
                            // no debug aparece a lista sendo preenchida
                            strLista.add("" + routes.getString("name"));
                            // o print sai normal
                            print(" RESULTADO DO FOR [ " + i + " ]" + routes.getString("name"));
                        }
                    }
                } catch (Exception e) { e.printStackTrace(); }
            }
        });
        return strLista;
    }

Mas me disseram que não vai funcionar assim. Resolvi tentar, de fato, ele nunca traz nada…

// o método fica dentro de uma classe chamada FrmDados
List<String> strLista = new ArrayList<>();
strLista = frmDados.getURLFrom(mUrl);
// quando boto pra exibir, ele não traz nada. Nada mesmo, nem chega a exibir texto
for(int i=0 ; i<strLista.size() ; i++) {
     frmDados.print("Resultado da bagaça >>>>>>> " + strLista.get(i));
}
// nem o texto "Resultado da bagaça" não é exibido. Nada mesmo.

Realmente é cabuloso o negócio :face_with_raised_eyebrow:
Cara, não consigo nem imaginar o que pode estar acontecendo.

1 curtida

Resolvi assim…
Me rendi basicamente. Coloquei o método direto na classe que eu ia usá-lo. Separei o método em um Callback e fiz o método ali.

Chamei o runOnUiThread e nele eu adicionei o List (opcoes) no meu Adapter (adaptador) com o addAll. Em seguida, notifyDataSetChanged para a ListView saber que alterei algo

Imagem auto explicativa

1 curtida

O que você quer é transformar um método assíncrono em um método síncrono. Uma forma de fazer isso é utilizar a classe CompletableFuture. Com essa classe você consegue fazer uma dataflow variable, um conceito de programação concorrente declarativa.

Em resumo, a ideia é que você vai bloquear a thread que está invocando o procedimento até que o callback seja chamado pelo método assíncrono. Esse callback vai entregar o resultado assíncrono à variável de fluxo, permitindo então que a thread principal seja liberada e o programa continue.

Tem um livro chamado Java Concurrency in Practice. Lá você vai aprender um monte de conceitos de programação concorrente usando Java, inclusive esse aqui que estamos discutindo. Vou deixar aqui um exemplo para você adaptar para o seu caso. Leia a documentação das classes para entender como funciona, e qualquer coisa pergunte.


import java.util.function.Consumer;
import java.util.concurrent.*;

public class Program {

    // Esse método é o assíncrono, ele vai executar um procedimento em background e chamar o seu callback quando terminar.
    static void doSomethingAsync (Consumer<String> callback) {
	new Thread(() -> {
		try {
		    for (int i = 0; i < 5; i++) {
			System.out.println(".");
			Thread.sleep(1000);
		    }
		    callback.accept("Magic Value");
		} catch (Exception e) {}
	}).start();
    }

    // Esse é o método que transforma o assíncrono em síncrono. Repare como o callback simplesmente repassa o resultado que recebeu para o synchronizer, e no return onde a thread principal fica travada até o .get() retornar.
    static String doSomethingSync () throws Exception {
	CompletableFuture<String> synchronizer = new CompletableFuture<>();
	doSomethingAsync((result) -> {
		synchronizer.complete(result);
	    });
	return synchronizer.get();
    }

    public static void main (String[] args) throws Exception {
	String result = doSomethingSync();
	System.out.println(result);
    }
}
2 curtidas

Pois é… Me falaram algo sobre Future. Mas eu REALMENTE não tenho conhecimento nenhum sobre isso… Obrigado pelo exemplo, com certeza vou pesquisar sobre isso! Muito obrigado mesmo! :smiley: