Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

16
respostas

Como receber dados do WebService???

Bom dia.

Estou trabalhando com uma activity que tem 2 fragments. Quando entro em um fragment, ele chama uma classe Async, que faz solicitação ao webservice e pega um json.

Preciso que esse json popule um spinner.

Onde devo inserir o código? No onPostExecute? No fragment? Podem me ajudar por favor?

Muito obrigado.

16 respostas

Olá Nicolas,

Isso mesmo, o local correto para disparar a atualização do seu spinner é o onPostExecute da AsyncTask que busca o JSON. Nesse ponto você pode atualizar qualquer componente da tela sem que o sistema operacional dispare uma exception. Como o spinner vai estar disponível somente dentro do seu fragment, o ideal é que você passe uma referência do fragment para a sua task e então invoque algum método do fragment para popular o spinner.

Entendi o sentido.

Mas não estou conseguindo aplicar isso. Poderia me dar uma ajuda ou então indicar algum material de estudo?

Obrigado.

Certo, vamos usar seu código como base. Poste aqui o código do seu fragment que contém o Spinner e também o código da classe que representa a sua AsyncTask.

Eu consegui fazer, mas não sei se é o recomendado, e ainda não consegui preencher o spinner com o json recebido. Eu fiz o seguinte:

No Fragment, eu criei o spinner e chamei a AsyncTask passando o context e a view.

 spinner = (Spinner) view.findViewById(R.id.intro_spin_selecionaEstado);

new BuscaEstadosTASK(getContext(), view).execute();

Na AsyncTask , método onPostExecute, eu tive que criar novamente o spinner e consegui fazer o spinner receber um array de testes, passado através de um array adapter:

String[] estados= {"Selecione seu estado...", "São Paulo", "Minas Gerais", "Rio de Janeiro"};

ArrayAdapter<String> adapterOpcoes = new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, estados);

Spinner spinner = (Spinner) view.findViewById(R.id.intro_spin_selecionaEstado);
        spinner.setAdapter(adapterOpcoes);

Ele chegou a preencher o spinner corretamente, mas meu problema é que eu recebo um JSON do webservice, e precisava preencher o spinner com esse json:

{"estados":[{"id":"3","nome":"São Paulo"},{"id":"2","nome":"Minas Gerais"},{"id":"1","nome":"Rio de Janeiro"}]}

Como fazer para colocar esse json no spinner? E mais uma coisa, o acento que vem do json dá erro. Eu tenho que tratar isso no PHP ou no Android?

PS: Muito obrigado pela ajuda.

Olá Nicolas,

A forma de popular o Spinner não está incorreta, a única coisa que você tem que notar é que sua task acaba ficando acoplada demais a um fragment específico.

Para popular o seu spinner com o JSON, você vai precisar extrair os dados da String do JSON recebido. Para isso, você pode utilizar as classes JSONArray e JSONObject. A primeira coisa a fazer é transformar a String JSON em um JSONObject e isso é bem tranquilo de fazer bastando apenas passar a String no construtor do JSONObject:

String jsonDeResposta = // aqui vem a sua string JSON recebida
JSONObject json = new JSONObject(jsonDeResposta);

Ok, agora temos um objeto JSON mas dentro desse objeto temos uma lista de estados, ou melhor ainda, um JSONArray! Para extrair o JSONArray do nosso JSONObject, basta utilizar o método getJSONArray(...) especificando o nome da chave que representa o array no JSON:

JSONArray estados = json.getJSONArray("estados");

Agora precisamos pegar cada JSONObject dentro desse array e extrair o campo com a chave nome. Algo como no código abaixo:

for (int i = 0; i < estados.length(); i++) {
    JSONObject estado = estados.getJSONObject(i);
    String nomeDoEstado = estado.getString("nome");
    // aqui você adicionaria o estado lido no seu array que vai para o spinner
}

Na hora de implementar esse código, você vai precisar tratar a JSONException que pode ocorrer caso o JSON esteja com formato incorreto, por exemplo.

Tenta implementar e qualquer problema é só avisar.

Como eu faria então para nao deixar a Task presa a esse fragment?

Eu consigo retornar um valor para a fragment ao final do onPostExecute?? Tentei colocar um:

return respota;  //JSON RECEBIDO PELO WEBSERVICE

Mas não consigo receber nada no fragment.

Vou tentando popular o Spinner enquanto isso. rsrsrs

Olá Nicolas,

Ao invés de colocar o código de popular o spinner no onPostExecute suponha que você colocou esse código dentro de um método no seu Fragment:

public class FragmentComSpinner extends Fragment {
    private Spinner spinner;

    public View onCreateView(...) {
        // código normal do createview com inflate

        // depois do inflate, precisa pegar o spinner e guardar no atributo
        this.spinner = (Spinner) view.findViewById(R.id.intro_spin_selecionaEstado);
    }

    // outros métodos

    public void populaSpinner(List<String> estados) {
        ArrayAdapter<String> adapterOpcoes = new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, estados);
        spinner.setAdapter(adapterOpcoes)        
    }
}

Agora, na sua AsyncTask, ao invés de receber uma View, você pode passar uma referência para o seu fragment e invocar dentro do onPostExecute(...) o método populaSpinner(...) passando os estados já extraídos do JSON.

Perceba que agora a responsabilidade de popular o spinner fica com o próprio fragment. A task só faz o requisição e extrai os dados da resposta e depois passa o resultado para o fragment invocando o método populaSpinner(...).

Eu consegui fazer o Fragment chamar a AsyncTASK, onde ela pega os dados do webservice e armazena em um JSON.

Apenas um detalhe, ao usar:

 ArrayAdapter<String> adapterOpcoes = new ArrayAdapter<String>(getContext(), android.R.layout.simple_spinner_item, estados);

O primeiro parâmetro como context não estava funcionando, então coloquei getContext(), que funcionou corretamente.

No método onPostExecute eu chamei o populaSpinner(), que também funcionou. (Usei um array simples de testes).

Ou seja, agora está quase tudo funcionando.

Só não consigo fazer ele pegar o JSON e popular o Spinner. Poderia dar uma olhada no código?

protected void onPostExecute(String resposta) {
        dialog.dismiss();
       try {
            JSONObject json = new JSONObject(resposta); //resposta é o JSON que veio do webservice através do:   String resposta = client.post();
            JSONArray estados = json.getJSONArray("estados");
            List<String> estadosArray = new ArrayList();

            for (int i = 0; i < estados.length(); i++) {
                JSONObject estado = estados.getJSONObject(i);
                String nomeDoEstado = estado.getString("nome");

                estadosArray.add(nomeDoEstado);
            }
            fragmentCom.populaSpinner(estadosArray);

        } catch (JSONException e) {
            e.printStackTrace();
            Toast.makeText(fragmentCom.getContext(), "Erro ao carregar spinner..", Toast.LENGTH_SHORT).show();
        }
}

E o populaSpinner() do Fragment está assim:

public void populaSpinner(List<String> estados) {
        ArrayAdapter<String> adapterOpcoes = new ArrayAdapter<String>(getContext(), android.R.layout.simple_spinner_item, estados);
        spinner.setAdapter(adapterOpcoes);
    }

O que pode ser o problema? Pois ele não está fazendo o que está dentro do try{}, vai direto ao catch.

Olá Nicolas!

Se ele está entrando no catch é porque aconteceu algum problema quando ele tentou parsear o JSON. Vamos ter que imprimir um log pra ver o que tá chegando de resposta do seu webservice.

Tenta colocar um Log.i("RESPOSTA", resposta); no começo do seu onPostExecute, roda o código novamente e depois checa no console qual o JSON que está chegando pra ver se tem algo estranho. Posta aqui depois pra gente ir tentando descobrir o que está errado.

Então, eu fiz o que você disse.

A resposta:

10-25 11:29:23.815 8264-8264/? I/RESPOSTA: {"estados":[{"id":"3","nome":"Minas
10-25 11:29:23.825 8264-8264/? W/System.err: org.json.JSONException: Unterminated string at character 35 of {"estados":[{"id":"3","nome":"Minas

Ele cortou após o espaço separando as palavras...

Olá Nicolas,

Acho que nesse caso o problema está no webservice. Dá uma verificada se no webservice ele tá enviando o JSON sem quebras de linha. Se estiver, tem que tratar a String pra retirar essas quebras antes de enviar a resposta.

Bom dia Jeferson, tudo certo?

Eu estava olhando, o código em php devolve o JSON em uma linha, sem quebras...

Quando ele pega um campo do banco de dados, com espaços em branco, por exemplo "São Paulo", ele dá erro no android e não executa.

Quando eu altero o campo para "São_Paulo", sem espaços em branco, ele carrega normalmente no spinner, executando as funções corretamente.

Tem alguma dica?

Olá Nicolas,

A classe Scanner do Java utiliza por padrão o caractere de espaço como delimitador para fazer a separação da String no momento da leitura. Isso significa que toda vez que invocamos o método next() no Scanner, ele vai ler a String até chegar no primeiro espaço em branco.

Para resolver o problema, vamos alterar o delimitador para a quebra de linha, o caractere \n.

Localize a sua classe que faz a requisiçao para o web service e que instancia o Scanner.

Vamos substituir a parte onde instanciamos o Scanner para:

Scanner scanner = new Scanner(connection.getInputStream()).useDelimiter("\n");

Faça essa alteração e teste novamente pra ver se resolve o problema!

Boa tarde Jeferson, tudo bem??

Fiz o que você falou, mas ele é um objeto Scanner, então eu criei uma String para receber esse Scanner.

Funcionou corretamente, só queria saber se o que eu coloquei depois seria uma boa prática realmente:

 Scanner jResposta = new Scanner(connection.getInputStream()).useDelimiter("\n");

 String respostaString = jResposta.next();
 return respostaString;

É correto fazer o que eu fiz aí em cima?

Opa, que bom que funcionou!

O que você fez faz sentido sim. Quando você invoca o método next() no Scanner ele devolve uma String que você pode devolver como resposta do seu método sem problemas.

Muito obrigado Jeferson por sua ajuda e presteza.

Agora está tudo funcionando corretamente. Faz uma solicitação ao webservice, preenchendo um spinner com a resposta, utilizando fragments (com Async TASK).

Muito obrigado, até mais.