我正在尝试在Android应用中实现搜索功能,该应用从AutoCompleteTextView
获取文本,如果在最后1.5秒内没有进行更改并显示搜索结果,则会等待。为此,我使用TextWatcher
类。
但是,我尝试实现此行为的所有尝试都遇到了麻烦,只有在UI线程本身(通过runOnUIThread
)或之前调用过Looper.prepare()
的线程中允许某些函数。
在所有尝试中,当输入其他字符或删除某些字符时,应用会随机崩溃,不显示任何搜索结果或重新加载到开始活动。
以下是我最近尝试的简单再现,我使用Handler
。
search.getResults
是长计算,而matches
是一个必须在 delayableAdapterCreation
创建ArrayAdapterWithSpaceFilter
之前填充的数组。
public class SearchFragment extends Fragment {
public final static int MAX_NUMBER_OF_SUGGESTIONS = 4; // only show a max of 4 suggestions if more were found
public final static int SEARCH_CHAR_AMOUNT = 3; // only search if at least 3 characters were typed
public final static long SEARCH_DELAY_MILLIS = (long) 1500; // the time to wait for no text changes in milliseconds
private Search search;
private AutoCompleteTextView textView;
private String[] matches;
private String userStartRequest;
private Entry[] suggestions;
private FragmentListenter sListener;
private EntryFunctions ef = new EntryFunctions();
private Runnable delayableSearch;
private Runnable delayableAdapterCreation;
private Handler delayableSearchHandler;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
delayableSearchHandler = new Handler();
delayableSearch = new Runnable() {
@Override
public void run() {
userStartRequest = textView.getText().toString();
sListener.onFragmentFinish(userStartRequest);
suggestions = search.getResults(userStartRequest);
matches = ef.fillMatches(suggestions);
}
};
delayableAdapterCreation = new Runnable() {
@Override
public void run() {
ArrayAdapterWithSpaceFilter<String> adapter =
new ArrayAdapterWithSpaceFilter<String>(getActivity(),
android.R.layout.simple_list_item_1,
matches);
textView.setAdapter(adapter);
}
};
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_search, container, false);
}
@Override
public void onStart() {
super.onStart();
textViewHandler();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (!(context instanceof FragmentListenter)) throw new AssertionError();
sListener = (FragmentListenter) context;
}
/**
* Interface for communicate to activity
*/
public interface FragmentListenter {
void onFragmentFinish(String userStartRequest);
}
/**
* Handler for the AutoCompleteTextView
*/
private void textViewHandler() {
try {
textView = (AutoCompleteTextView) getView().findViewById
(R.id.startNaviAutoCompleteTextView);
search = new Search();
System.out.println("Created Search object");
textView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
System.out.println("TextWatcher beforeTextChanged");
}
@Override
public void onTextChanged(CharSequence s, final int start, int before, int count) {
delayableSearchHandler.removeCallbacks(delayableSearch); userStartRequest = textView.getText().toString();
sListener.onFragmentFinish(userStartRequest);
if (textView.getText().length() >=
SEARCH_CHAR_AMOUNT) {
new Thread(delayableSearch).start();
delayableSearchHandler.postDelayed
(delayableAdapterCreation, SEARCH_DELAY_MILLIS);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
此时,对我来说无关紧要,只要在AutoCompleteTextView
中输入新字符并且取消最终旧搜索或在1.5秒后开始搜索,计算是否已经开始。
如果搜索字词没有结果且结果列表存在问题,则上述代码会崩溃。有时它显示出之前输入的几个键击(所以如果我慢慢搜索abcd我得到abc的搜索结果),有时它根本不显示。我的猜测是竞争条件或多次调用textViewHandler
或onTextChanged
方法时出现问题,即使delayableSearchHandler.removeCallbacks(delayableSearch)
应该阻止这种情况发生。
任何人都可以解释一下,工作线程和UI线程之间的交互是什么样的,所以保证搜索能够提供它的结果吗?
提前致谢,
乔
答案 0 :(得分:0)
任何长时间运行的操作(网络调用,数据库搜索...)都需要很长时间才能执行,从而阻止了UI。在冰淇淋三明治之前,这种行为被android运行时所容忍。
This article可能是一个很好的阅读