等待完成计算而不冻结UI

时间:2017-06-05 16:59:51

标签: java android multithreading

我正在尝试在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的搜索结果),有时它根本不显示。我的猜测是竞争条件或多次调用textViewHandleronTextChanged方法时出现问题,即使delayableSearchHandler.removeCallbacks(delayableSearch)应该阻止这种情况发生。

任何人都可以解释一下,工作线程和UI线程之间的交互是什么样的,所以保证搜索能够提供它的结果吗?

提前致谢,

1 个答案:

答案 0 :(得分:0)

任何长时间运行的操作(网络调用,数据库搜索...)都需要很长时间才能执行,从而阻止了UI。在冰淇淋三明治之前,这种行为被android运行时所容忍。

This article可能是一个很好的阅读