2011-05-18 11 views
6

Tengo una actividad en la que un usuario escribe un texto de edición, pulsa un botón de búsqueda y la aplicación consulta un servicio web y coloca los resultados en un ListView.Búsqueda automática como tipos de usuario

Me gustaría eliminar el botón de búsqueda.

Obviamente, no quiero que todos los caracteres que el usuario escriba lleguen al servicio web. Solo quiero ejecutar 1 llamada de servicio web cuando el usuario termine de escribir.

La forma en que estoy lograr esto es así:

que tienen una variable miembro que mantiene una AsyncTask. Cuando el texto en EditText cambia, la AsyncTask se dispara. Dentro de doInBackground(), se golpea una llamada a Thread.sleep(). Este período de suspensión es esencialmente un temporizador esperando para ver si el usuario escribe algo más. Después de la llamada de espera, la llamada al servicio web se realiza si la AsyncTask no se ha cancelado. Si el usuario escribe otra letra, se llama a cancel() en AsyncTask (para evitar que se llame al servicio web), la variable miembro que contiene AsyncTask se establece en nulo y se crea una nueva instancia de AsyncTask.

Tengo algunas preguntas aquí: ¿Tengo pérdida de memoria? ¿Esto es particularmente malo de alguna manera? Entiendo que puede que no sea lo más eficiente, ¿pero voy a ralentizar seriamente el teléfono de alguien? ¿Hay una mejor manera de hacer esto?

private SearchTask mSearchTask = null; 

...

mSearchText.addTextChangedListener(new TextWatcher() { 

     public void onTextChanged(CharSequence s, int start, int before, int count) { 
      // Auto-generated method stub 
     } 

     public void beforeTextChanged(CharSequence s, int start, int count, 
       int after) { 
      // Auto-generated method stub 
     } 

     public void afterTextChanged(Editable s) { 
       if (s != null && s.length() > 0) { 
        // stop any current search thread 
        if (mSearchTask != null && !mSearchTask.isCancelled()) { 
         mSearchTask.cancel(false); 
        } 


         // search for products 
         SearchCriteria crit = new SearchCriteria(); 
         crit.strSearchWord = mSearchText.getText().toString().trim(); 
         mSearchTask = null; 
         mSearchTask = new SearchTask(); 
         mSearchTask.execute(crit); 
       } 
     } 
    }); 

...

private class SearchTask extends AsyncTask<SearchCriteria, Integer, Boolean> { 
    protected Boolean doInBackground(SearchCriteria... params) { 
     SearchCriteria crit = null; 
     if (params.length > 0) { 
      crit = params[0]; 

      if (crit != null) { 
       try { 
        Thread.sleep(1000L); 
        if (!isCancelled()) { 
         // perform search 
         return true; 
        } 
       } 
       catch(Exception e) { 
       } 
      } 
     } 

     return false; 
    } 

    protected void onPostExecute(Boolean success) { 
     if (success != null && success == true) { 
      // do something 
     } 
     else { 
      // do something else 
     } 
    } 
} 

Respuesta

9

estaría más tentado a lanzar un hilo de x milisegundos y hacer el cheque a continuación, en lugar de lanzar el enhebrar inmediatamente con un sueño allí.

private Handler mMessageHandler = new Handler(); 

private Runnable mSearchRunnable = new Runnable() { 
     public void run() { 
      if (!isCancelled()) { 
       // perform search 
      } 
     } 
    }; 

entonces se puede poner esto en que afterTextChanged:

mMessageHandler.postDelayed(mSearchRunnable, 1000); 

A continuación, puede cancelar el hilo si el usuario introduce más datos con:

mMessageHandler.removeCallbacks(mSearchRunnable); 
+0

Aconsejaría esto, usar AsyncTask es más seguro que usar Runnables y tener que manejar los hilos por su cuenta. – blindstuff

+0

He usado este método anteriormente sin problemas ni pérdida de memoria. Fuera de interés, ¿qué problemas pueden surgir al usar este método? – Martyn

+1

No me refiero a que esté mal en absoluto. AsyncTask es justo lo que el equipo de Android diseñó para que no tengas que usar Handlers y administrar varios hilos al mismo tiempo. Su método funciona, pero tiene herramientas para facilitar su uso. – blindstuff

3

Debería pensar en cancelar (verdadero) para intentar cerrar la tarea mientras espera o si la llamada al servidor web ya se está ejecutando. Eso podría ahorrarle algunos ciclos de proceso, aunque su servidor web podría no estar interesado en las llamadas interrumpidas.

Si desea guardar algunos ciclos de gc, puede reutilizar su objeto SearchCriteria si eso es posible.

Aparte de esto no puedo ver ninguna pérdida de memoria. Sus objetos tienen ciclos de vida cortos y no los almacena en caché. El único problema que puede surgir es que hay demasiadas AsyncTasks paralelas con la ejecución de solicitudes http que provocarán que se agote la memoria. Tuvimos ese problema una vez con una aplicación durante la prueba de mono.

0
@Override 
public void onCreate(Bundle icicle) { 
    super.onCreate(icicle); 
    setContentView(R.layout.main); 
    lv1 = (ListView) findViewById(R.id.ListView01); 
    ed = (AutoCompleteTextView) findViewById(R.id.EditTextSearch); 

    // AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.autocomplete_country); 
     ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this, R.layout.list_item, countryName); 
     ed.setAdapter(adapter1); 

    this.getWindow().setSoftInputMode(
      WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 
    final List<HashMap<String, String>> fillMaps = new ArrayList<HashMap<String, String>>(); 
    for (int i = 0; i < countryName.length; i++) { 
     HashMap<String, String> map = new HashMap<String, String>(); 
     map.put("flag", "" + imageId[i]); 
     map.put("country", countryName[i].toString()); 
     map.put("capital", capitalName[i].toString()); 
     map.put("countrytime", 
       convertDateTimeToGMT(GMTplusMinusInMillisecond[i], 
         plusMinus[i])); 
     map.put("GMT", GMTplusMinus[i].toString()); 

     fillMaps.add(map); 
    } 

    // fill in the grid_item layout 
    SimpleAdapter adapter = new SimpleAdapter(this, fillMaps, 
      R.layout.grid_item, from, to); 
    lv1.setAdapter(adapter); 

    ed.addTextChangedListener(new TextWatcher() { 

     public void afterTextChanged(Editable s) { 
     } 

     public void beforeTextChanged(CharSequence s, int start, int count, 
       int after) { 
     } 

     public void onTextChanged(CharSequence s, int start, int before, 
       int count) { 
      fillMaps.clear(); 
      textlength = ed.getText().length(); 
      for (int i = 0; i < countryName.length; i++) { 
       if (textlength <= countryName[i].length()) { 
        if (ed.getText() 
          .toString() 
          .equalsIgnoreCase(
            (String) countryName[i].subSequence(0, 
              textlength))) { 
         HashMap<String, String> map = new HashMap<String, String>(); 
         map.put("flag", "" + imageId[i]); 
         map.put("country", countryName[i].toString()); 
         map.put("capital", capitalName[i].toString()); 
         map.put("countrytime", 
           convertDateTimeToGMT(
             GMTplusMinusInMillisecond[i], 
             plusMinus[i])); 
         map.put("GMT", GMTplusMinus[i].toString()); 

         fillMaps.add(map); 
        } 
       } 
      } 
      if(!fillMaps.isEmpty()) 
      { 
      SimpleAdapter adapter = new SimpleAdapter(
        WorldClockActivity.this, fillMaps, R.layout.grid_item, 
        from, to); 
      lv1.setAdapter(adapter); 
      } 
      else 
      {  String[] COUNTRIES = new String[] {"No record found"}; 
      lv1.setAdapter(new ArrayAdapter<String>(WorldClockActivity.this,R.layout.list_item, COUNTRIES)); 
      } 

      // lv1.setAdapter(new 
      // ArrayAdapter<String>(WorldClockActivity.this,android.R.layout.simple_list_item_1 
      // , arr_sort)); 

     } 
    }); 
} 

public static String convertDateTimeToGMT(long millis, int plusMinus) { 

    Calendar CalGMT; 
    TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 
    CalGMT = new GregorianCalendar(TimeZone.getTimeZone("GMT")); 
    CalGMT.get(Calendar.DAY_OF_MONTH); 
    CalGMT.get(Calendar.MONTH); 
    CalGMT.get(Calendar.YEAR); 
    CalGMT.get(Calendar.HOUR_OF_DAY); 
    CalGMT.get(Calendar.MINUTE); 
    CalGMT.get(Calendar.SECOND); 

    if (plusMinus == 1) { 
     CalGMT.setTimeInMillis(CalGMT.getTimeInMillis() + millis); 
    } else if (plusMinus == 0) { 
     CalGMT.setTimeInMillis(CalGMT.getTimeInMillis() - millis); 
    } 
    String sendDateTimeInGMT = CalGMT.get(Calendar.HOUR_OF_DAY) + ":" 
      + CalGMT.get(Calendar.MINUTE) + ":" 
      + CalGMT.get(Calendar.SECOND); 

    return sendDateTimeInGMT; 
} 

}

he hecho la aplicación con el código anterior en esta solicitud que tengo uso AutoCompleteTextView para proporcionar servicio de búsqueda en la vista de la lista a continuación la vista de lista muestra el nombre de todo el país y el usuario podrá buscar país por nombre de país cuando el usuario escribe en AutoCompleteTextView y luego la búsqueda relacionada se muestra en la vista de lista. Ex si el usuario desea buscar Canadá en la lista de países del mundo, entonces el usuario solo escriba ca en AutoCompleteTextView luego aparece otra lista, haga clic en AutoCompleteTextView y muestre el nombre de inicio de todo Canadá, luego el usuario elige Canadá en esta lista y luego obtenga el toda la información de Canadá en la lista.

Cuestiones relacionadas