Android使用TextWatcher匹配email造成ANR的解决

背景

最近QA给我报了一个bug,说是在编辑框快速输入邮箱的时候有时会导致页面无响应。
这个编辑框是专门输入邮箱的,所以监听了TextWatcher,每次字符的改变都会用正则判断是否是正确的邮箱,如果不是的话会显示错误提示。这里我没有考虑到用户快速的填写内容,会触发多次的afterTextChanged回调这个case,这样就会因为正则匹配次数太多造成ANR。下面我谈谈自己是怎样修复这个bug的:

一、减少afterTextChanged回调

afterTextChanged方法里面通过handler延迟发送消息,在延迟时间之内的消息全部删除,代码如下:

 @Override
            public void afterTextChanged(Editable editable) {
                if (TextUtils.isEmpty(editable.toString())) {
                    return;
                }

                final Message msg = handler.obtainMessage(MATCH_EMAIL_WHAT);
                msg.obj = editable.toString();
                handler.removeMessages(MATCH_EMAIL_WHAT);
                handler.sendMessageDelayed(msg, SEND_MSG_DELAY_MILLS);
            }

    Handler handler = new Handler() {
        @Override
        public void dispatchMessage(Message msg) {
            if (msg.what == MATCH_EMAIL_WHAT) {
                final String email = msg.obj.toString();
                // check email
            }
        }
    };

二、在线程中执行check mail

做完第一步以后,我发现在快速输入文字的时候,尽管能够过滤掉延迟阀值里面的请求,但是依然会造成ANR。说明check email操作是耗时的,于是就在handler的dispatchMessage中开一个线程来check email,执行完再回调给UI线程显示UI,代码如下:

new AsyncTask() {
                    @Override
                    protected Boolean doInBackground(String... strings) {
                        Log.i(TAG, "doInBackground -> " + strings[0]);
                        return isEmailValid(strings[0]);
                    }

                    @Override
                    protected void onPostExecute(Boolean aBoolean) {
                        Log.i(TAG, "onPostExecute");
                        if (aBoolean) {
                            Log.i(TAG, "true");
                            // hide error_tip UI
                        } else {
                            Log.i(TAG, "false");
                            // show error_tip UI
                        }
                    }
                }.executeOnExecutor(SINGLE_THREAD_POOL, msg.obj.toString());

三、优化正则表达式

执行完第二步后ANR的问题解决了,但是如果频繁去更改编辑框的字符,AsyncTaskonPostExecute回调会很慢,就会出现用户输入'[email protected]',过了几秒钟才显示【电子邮箱格式错误】这个提示。这个时候我们就要找出ANR的真正源头了,那就是我们项目里面已有的匹配邮箱的正则表达式执行效率太低了,这里我的思路是:

  • 1、程序在执行正则匹配之前先判断出显然不符合电子邮箱格式的字符串,能不使用正则就尽量不使用,代码如下:
  public static boolean isEmailValid(String email) {
        // 邮箱地址为空
        if (TextUtils.isEmpty(email)) {
            return false;
        }

        if (!email.contains("@")) {
            return false;
        }

        // 邮箱地址长度超过50个字符
        if (email.length() > 50) {
            return false;
        }
        // 邮箱地址最后一位是"."
        if (email.endsWith(".")) {
            return false;
        }
        // 邮箱地址中包含有一个以上的连续的".",此校验主要是为了防止用户多输入一个"."
        if (email.contains("..")) {
            return false;
        }
        String strPattern = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$";

        Pattern p = Pattern.compile(strPattern);
        Matcher m = p.matcher(email);
        if (m.matches()) {
            return true;
        } else {
            return false;
        }
    }
  • 2、使用更高效的正则表达式,减少正则匹配的次数

综上,这个bug就算解决了,正常使用一般不会复现这个问题的。这个bug给我的教训是:编写代码一定要考虑到性能的问题!

你可能感兴趣的:(Android)