开发者

Automatic searching as user types

I have an Activity where a user types in an EditText, hits a search button, and the app queries a web service and places the results in a ListView.

I'd like to do away with the search button.

Obviously I don't want every character the user types to hit the web service. I want to only execute 1 web service call when the user is finished typing.

The way I'm achieving this is like so:

I have a member variable which holds an AsyncTask. When the text in the EditText changes, the AsyncTask fires. Inside doInBackground(), a call to Thread.sleep() is hit. This sleep period is essentially a timer waiting to see if the user types anything else. After the sleep call, the call to the web service is made if the AsyncTask has not been cancelled. If the user types another letter, cancel() is called on the AsyncTask (to stop the web service from being called), the member variable holding the AsyncTask is set to null, and a new instance of the AsyncTask is created.

I have a few questions here: Am I leaking memory? Is this particularly bad in any way? I understand it may not be most efficient, but am I going to seriously slow down someone's phone? Is there a better way of doing this?

private SearchTask mSearchTask = null;

...

    mS开发者_C百科earchText.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
        }
    }   
}


I would be more tempted to launch a thread in x milliseconds and do the check then, as opposed to launching the thread immediately with a sleep in there.

private Handler mMessageHandler = new Handler();

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

then you can put this in you afterTextChanged:

mMessageHandler.postDelayed(mSearchRunnable, 1000);

you can then cancel the thread if the user enters more data with:

 mMessageHandler.removeCallbacks(mSearchRunnable);


You should think about calling cancel(true) to try to shutdown the task while it is waiting or if the call to the webserver is running already. That might save you some process-cycles tho your webserver could be unamused about the broken calls.

If you want to save some gc-cycles you could reuse your SearchCriteria object if that is possible.

Apart from this I can't see any memory leaks. Your objects have short lifecycles and you don't cache them. The only problem that might arise is too many parallel AsyncTasks with running http-requests which will cause an early out of memory. We had that problem once with one app during the monkey-test.


hi this is the link which may be helps you..

http://thinkandroid.wordpress.com/2010/02/08/writing-your-own-autocompletetextview/


@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 have done the application using the above code in this application i have use AutoCompleteTextView for provide search facility in list view then list view show the name of all the country and user able to search country by country name when user type in AutoCompleteTextView then related search is show in listview. Ex if user want to search Canada in the world country list then user only type ca in AutoCompleteTextView then a another list is appear blow the AutoCompleteTextView and show the all country name start ca name then user chose Canada in this list then get the all information of Canada in listview.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜