根据用户类型自动搜索

发布于 2024-11-08 06:20:06 字数 2570 浏览 1 评论 0原文

我有一个 Activity,其中用户输入 EditText,点击搜索按钮,应用程序查询 Web 服务并将结果放置在 ListView 中。

我想取消搜索按钮。

显然我不希望用户输入的每个字符都访问网络服务。我只想在用户完成输入后执行 1 个 Web 服务调用。

我实现此目的的方式如下:

我有一个包含 AsyncTask 的成员变量。当 EditText 中的文本更改时,AsyncTask 会触发。在 doInBackground() 内部,调用了 Thread.sleep()。这个睡眠周期本质上是一个计时器,等待用户是否输入其他内容。在 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
        }
    }   
}

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;

...

    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
        }
    }   
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

合久必婚 2024-11-15 06:20:06

我更倾向于在 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);

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);
绅刃 2024-11-15 06:20:06

您应该考虑调用 cancel(true) 来尝试在任务等待时或对网络服务器的调用已在运行时关闭任务。这可能会为您节省一些处理周期,但您的网络服务器可能会对中断的调用感到不高兴。

如果您想节省一些 gc 周期,您可以重用您的 SearchCriteria 对象(如果可能的话)。

除此之外我看不到任何内存泄漏。您的对象的生命周期很短,并且您不缓存它们。唯一可能出现的问题是运行 http 请求的并行 AsyncTasks 过多,这将导致内存过早耗尽。在猴子测试期间,我们曾经在一个应用程序上遇到过这个问题。

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.

灯角 2024-11-15 06:20:06
@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 中输入然后相关时,用户能够按国家/地区名称搜索国家/地区搜索显示在列表视图中。
例如,如果用户想在世界国家/地区列表中搜索加拿大,则用户只需在 AutoCompleteTextView 中键入 ca,然后另一个列表就会出现在 AutoCompleteTextView 下方,并显示所有国家/地区名称开始 ca 名称,然后用户在此选择加拿大列表然后在列表视图中获取加拿大的所有信息。

@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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文