Android中控件setVisibility(View.Gone)失效(经测试是非UI线程导致)

  • 前提:这个错误在Demo中无法展示,因为是实际场景的复杂情况。
  • 场景:只有一部分手机出现。

简单来说,业务当时的界面是有两种输入登录模式,其中密码登录模式有三个EditText还有一个显示验证码的ImageView(用Bitmap动态加载图片),然后手机号登录模式中只有两个EditText。

我在代码中通过传入的值来切换模式的切换,具体类似下面的代码。

private void initMode() {
    clearAnimation();

    switch (mode){
        case Login.LOGIN_PASSWORD:
            etLoginArea.setVisibility(View.GONE);
            etLoginPhone.setVisibility(View.GONE);
            etLoginAccount.setVisibility(View.VISIBLE);
            llLoginPassword.setVisibility(View.VISIBLE);
            llLoginValidCode.setVisibility(View.VISIBLE);
            
            getValidCode(); //通过网络获取验证码

            btnLogin.setEnabled(false);
            break;
        case Login.LOGIN_PHONE:
            etLoginAccount.setVisibility(View.GONE);
            llLoginPassword.setVisibility(View.GONE);
            llLoginValidCode.setVisibility(View.GONE);
            etLoginArea.setVisibility(View.VISIBLE);
            etLoginPhone.setVisibility(View.VISIBLE);

            btnLogin.setEnabled(false);
            break;
    }

}

private void clearAnimation() {
    etLoginAccount.clearAnimation();
    llLoginPassword.clearAnimation();
    llLoginValidCode.clearAnimation();
    etLoginArea.clearAnimation();
    etLoginPhone.clearAnimation();
    btnLogin.clearAnimation();
}

然后在界面中出现了错误。

具体错误表现为:

  1. setVIsibility(View.GONE)的实际效果为setVIsibility(View.INVISIBLE)
  2. setVIsibility(View.VISIBLE)无效
  3. 整个控件的布局没有重新测量宽高。

我的解决措施:

  1. 网络上的clearAnimation方法,加入了无效。
  2. requestLayout()方法,无效。
  3. 在xml文件的相关控件中加入visibility="gone"标签,无效。

在三个方法都没有显著效果之后,我开始精准调参数,来定位到底是哪个控件出现问题,最后定位到时ImageView中加载bitmap时,才会出现之前的错误。

public void showValidCode(Bitmap bitmap) {
    try {
    	//获取网络图片成功后调用这一行,其中ivLoginValidCode是llLoginValidCode内部的一个ImageView
        ivLoginValidCode.setImageBitmap(bitmap);
    }catch (Exception e){
        e.printStackTrace();
    }
}

在第一次加载bitmap时会出现错误日志:

2019-12-13 11:50:12.071 10658-10818/com.sys.washmashine W/System.err: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
2019-12-13 11:50:12.071 10658-10818/com.sys.washmashine W/System.err:     at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753)
2019-12-13 11:50:12.071 10658-10818/com.sys.washmashine W/System.err:     at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1225)
2019-12-13 11:50:12.071 10658-10818/com.sys.washmashine W/System.err:     at android.view.View.requestLayout(View.java:23093)
2019-12-13 11:50:12.072 10658-10818/com.sys.washmashine I/chatty: uid=10254(com.sys.washmashine) identical 8 lines
2019-12-13 11:50:12.072 10658-10818/com.sys.washmashine W/System.err:     at android.view.View.requestLayout(View.java:23093)
2019-12-13 11:50:12.072 10658-10818/com.sys.washmashine W/System.err:     at android.widget.ImageView.setImageDrawable(ImageView.java:571)
2019-12-13 11:50:12.072 10658-10818/com.sys.washmashine W/System.err:     at android.support.v7.widget.AppCompatImageView.setImageDrawable(AppCompatImageView.java:100)
2019-12-13 11:50:12.072 10658-10818/com.sys.washmashine W/System.err:     at android.widget.ImageView.setImageBitmap(ImageView.java:705)
2019-12-13 11:50:12.072 10658-10818/com.sys.washmashine W/System.err:     at android.support.v7.widget.AppCompatImageView.setImageBitmap(AppCompatImageView.java:108)

结果发现是传统的非主线程不能修改ui的错误。

然后才发现当时在申请图片的post接口中是new Thread().run()执行的。结果申请之后并没有通过handler到主线程再加载。最终就出了这个错误。

public void getValidCodeCode(String uuid) {
    final String path = ("xxx" + uuid); //地址不是原先的地址

    new Thread() {
        public void run() {
            try {
                HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
                conn.setConnectTimeout(30 * 1000);
                conn.setRequestMethod("POST");
                InputStream inputStream = conn.getInputStream();
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                inputStream.close();
                showValidCode(bitmap);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }.start();
}

但是实际上只有在第一次执行setImageView的时候,才会报错,后面执行的时候是不会报错的。也就是说其实子线程是可以执行Imageview.setImageBitmap这行代码。

所以我猜测出错的原因是在一部分手机上出现了线程不同步的错误,才导致了这个问题。

最后为了解决这个问题,用上了只有一个线程的线程池来节约反复打开线程的消耗问题。

你可能感兴趣的:(Android,实际问题,android)