0

I'm trying to populate a spinner in execution time with data from mysql database when I click in "Buscar"(From portuguese: Search) Button, but when I click in this button I get the following exception: "android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."

My Code

btnBuscarProduto.setOnClickListener(new View.OnClickListener() {  
        public void onClick(View v) {
            new Thread(new Runnable() {
                public void run() {
                    Looper.prepare();
                    /*This is a string*/resultadoBusca = buscar(edtBuscaProduto.getText().toString());
                    System.out.println("Resultado da Busca: "+resultadoBusca);
                    if(resultadoBusca.equalsIgnoreCase("Vazio")){
                        Toast toast = Toast.makeText(getActivity(), "Nada Encontrado", Toast.LENGTH_SHORT);
                        toast.show(); 
                    }else{
                        /*This is a List<String>*/listaBusca = makeList(resultadoBusca);
                        System.out.println("Lista da Busca"+listaBusca);
                        ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_dropdown_item, list);
                        spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_item);
                        spnProdutos.setAdapter(spinnerArrayAdapter);

                    }                                             
                }
              }).start();                 
        }
    });

public String buscar(String termo){
String resp = null;
try{
        httpclient = new DefaultHttpClient();
        httppost = new HttpPost("http://192.168.1.101/android/busca.php");
        nameValuePairs = new ArrayList<NameValuePair>(1);
        nameValuePairs.add(new BasicNameValuePair("busca", termo)); 
        httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));            
        response=httpclient.execute(httppost);           
        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        resp = httpclient.execute(httppost, responseHandler);
    }catch(Exception e){
        Toast toast = Toast.makeText(getActivity(), "Erro: "+e.getMessage(), Toast.LENGTH_SHORT);
        toast.show(); 
    }
return resp;
}

public List<String> makeList(String input){ 
    List<String> list = new ArrayList<String>();
    String[] newArray = input.split("\\|");
    for (int i =0; i<newArray.length; i++){
        list.add(newArray[i].toString());
    }   
    return list;
}  

4 Answers4

1

You get this exception because you try to update the UI on a background Thread here

 Toast toast = Toast.makeText(getActivity(), "Nada Encontrado", Toast.LENGTH_SHORT);
                    toast.show();

There are several ways to handle this and one way, my favorite, is to use an AsyncTask.

You can execute the AsyncTask which will do your network stuff in doInBackground() then you can return a result to onPostExecute() where you can update the UI depending on the result.

Here is an example of using AsyncTask

Community
  • 1
  • 1
codeMagic
  • 44,549
  • 13
  • 77
  • 93
0

I think you must not change the UI from other threads than the UI thread. Look at runOnUiThread. After getting the search results, you should enter the UI thread and can then modify the UI.

Tobias
  • 7,723
  • 1
  • 27
  • 44
0

To keep it simple, I would do this:

btnBuscarProduto.setOnClickListener(new View.OnClickListener() {  
    public void onClick(View v) {
        new Thread(new Runnable() {
            public void run() {
                Looper.prepare();
                /*This is a string*/resultadoBusca = buscar(edtBuscaProduto.getText().toString());
                System.out.println("Resultado da Busca: "+resultadoBusca);
               MyActivity.this.runOnUiThread(new Runnable(){

                    @Override
                    public void run() {
                        if(resultadoBusca.equalsIgnoreCase("Vazio")){
                            Toast toast = Toast.makeText(getActivity(), "Nada Encontrado", Toast.LENGTH_SHORT);
                            toast.show(); 
                         }else{
                           /*This is a List<String>*/listaBusca = makeList(resultadoBusca);
                           System.out.println("Lista da Busca"+listaBusca);
                           ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_dropdown_item, list);
                           spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_item);
                           spnProdutos.setAdapter(spinnerArrayAdapter);

                        }               
                    }});

            }
          }).start();                 
    }
});
Jon F Hancock
  • 3,349
  • 3
  • 23
  • 26
  • It works! I only had to change "MyActivity.this" for "((Activity)getActivity())" – Renan Oliveira Nov 09 '13 at 02:14
  • Great! Now to address the bigger issue. This code will be problematic if you rotate the device or exit the activity in another way while the background thread is happening. See codeMagic's answer for a better solution. AsyncTaskLoader is even better still because it will reattach itself to the context after the device rotation. – Jon F Hancock Nov 09 '13 at 04:29
0

Non ui threads are not allowed to touch views. Do your view operation on the UI thread, by runOnUIThread method as provided by Context.

JoxTraex
  • 13,423
  • 6
  • 32
  • 45