Auto search as custom types

I have an Activity where a user types in an EditText, clicks the search button, and the application requests a web service and puts the results in a ListView.

I want to end the search button.

Obviously, I don’t want every character that the user typed to get into the web service. I want to make only one web service call when the user completes the input.

The way I achieve this is:

I have a member variable that contains AsyncTask. When the text in the EditText changes, AsyncTask fires. Inside doInBackground (), a call to Thread.sleep () gets caught. This sleep period, in fact, is a timer that expects the user to enter something else. After a sleep call, a web service call is made if AsyncTask has not been canceled. If the user enters a different letter, the cancel () function is called in AsyncTask (to stop the web service call), the member variable containing AsyncTask is null, and a new AsyncTask instance is created.

I have a few questions here: Am I losing my memory? Is this especially bad? I understand that this may not be the most effective, but can I seriously slow down the work of some phone? Is there a better way to do this?

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 } } } 
+6
source share
4 answers

I would be more tempted to start the thread in milliseconds and then run a check, rather than start the thread immediately with sleep there.

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

then you can put this in yourself afterTextChanged :

 mMessageHandler.postDelayed(mSearchRunnable, 1000); 

you can cancel the stream if the user enters more data with:

  mMessageHandler.removeCallbacks(mSearchRunnable); 
+9
source

You should consider calling cancel (true) to try to exit the task while waiting or if the call is already running on the web server. This can save you some technological cycles in which your web server might not be noticed due to broken calls.

If you want to save some gc loops, you can reuse your SearchCriteria object, if possible.

In addition, I do not see memory leaks. Your objects have short life cycles, and you do not cache them. The only problem that may arise is that there are too many concurrent AsyncTasks running http requests that cause out of memory. We had this problem once with one application during a monkey test.

+3
source
 @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; } 

}

I made an application using the above code in this application. I have an AutoCompleteTextView function to allow search in a list, then the list view displays the name of the entire country and a user who can search for a country by country name, when a user enters autostart in AutoCompleteTextView, the corresponding search is displayed in the list. Ex, if the user wants to find Canada in the list of countries of the world, then the user enters only ca in the AutoCompleteTextView, then another list appears reflecting the AutoCompleteTextView and showing the country name for the start of the name ca, then the user selects Canada in this then enter all the Canada information in the list .

0
source

Source: https://habr.com/ru/post/888443/


All Articles