我有一个Activity,其中用户键入EditText,点击搜索按钮,应用程序查询Web服务并将结果放在ListView中。
我想取消搜索按钮。
显然,我不希望用户输入的每个字符都能访问Web服务。我想在用户输入完成后只执行1个Web服务调用。
我实现这一目标的方式如下:
我有一个包含AsyncTask的成员变量。当EditText中的文本发生更改时,AsyncTask将触发。在doInBackground()内部,调用Thread.sleep()。这个休眠期基本上是一个等待查看用户是否输入任何其他内容的计时器。在睡眠调用之后,如果尚未取消AsyncTask,则会调用Web服务。如果用户键入另一个字母,则在AsyncTask上调用cancel()(以阻止调用Web服务),将保存AsyncTask的成员变量设置为null,并创建AsyncTask的新实例。
我在这里有几个问题:我是否泄漏记忆?这在任何方面都特别糟糕吗?我知道它可能效率不高,但我是否会严重减慢某人的电话速度?有没有更好的方法呢?
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
}
}
}
答案 0 :(得分:9)
我会更愿意在x毫秒内启动一个线程然后进行检查,而不是立即启动线程并在那里进行休眠。
private Handler mMessageHandler = new Handler();
private Runnable mSearchRunnable = new Runnable() {
public void run() {
if (!isCancelled()) {
// perform search
}
}
};
然后你可以把它放在afterTextChanged
:
mMessageHandler.postDelayed(mSearchRunnable, 1000);
如果用户输入更多数据,则可以取消该线程:
mMessageHandler.removeCallbacks(mSearchRunnable);
答案 1 :(得分:3)
你应该考虑调用cancel(true)来尝试在等待任务时关闭任务,或者是否已经调用了对webserver的调用。这可能会为您节省一些流程周期,因为您的网络服务器可能会因断电而无法使用。
如果你想保存一些gc-cycles,你可以重用你的SearchCriteria对象。
除此之外,我看不到任何内存泄漏。您的对象具有较短的生命周期,并且您不会缓存它们。可能出现的唯一问题是运行http请求的并行AsyncTasks太多会导致早期内存不足。在猴子测试期间,我们曾经有一个应用程序遇到过这个问题。
答案 2 :(得分:2)
答案 3 :(得分: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;
}
}
我已经在此应用程序中使用上述代码完成了应用程序我使用AutoCompleteTextView在列表视图中提供搜索工具然后列表视图显示所有国家/地区的名称和用户在用户键入AutoCompleteTextView时能够按国家/地区名称搜索然后相关搜索显示在listview中。 例如,如果用户想要在世界国家/地区列表中搜索加拿大,那么用户只需在AutoCompleteTextView中键入 ca ,然后会出现另一个列表,打开AutoCompleteTextView并显示所有国家/地区名称开始ca名称然后用户选择加拿大列表然后在列表视图中获取加拿大的所有信息。