zoukankan      html  css  js  c++  java
  • Android:子线程到底能不能更新UI?

    问题由来

    我们知道,Andoird由于修改UI是线程不安全的,只能在主线程中修改。如果多个线程修改UI肯定会花屏,于是谷歌做了限制,只能在主线程中修改UI。但是有次我在子线程中修改了UI没弹异常。

    先来看两段代码

    //正常运行
    btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { resultTv.setText("更新TextView"); } }).start(); } });

    闪退,控制台异常为:Only the original thread that created a view hierarchy can touch its views.

    //弹出异常
    btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { resultTv.setText("更新TextView ");//这里不一样,多了个换行符 } }).start(); } });

    源码解读

    之前的博客有解读ViewRootImpl是负责View的绘制,在requestLayout这个方法中会检查是否是当前线程。所以只要子线程修改UI但不改变UI布局时,不会弹出非主线程的异常。

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

    那么问题来了,假设我在onCreate的时候修改UI,layout也变了,为什么没报错呢?

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new Thread(new Runnable() {
            @Override
            public void run() {
                resultTv.setText("onCreate
    ");
            }
        }).start();
    }

    在ActivityThread中发现,ViewRootImpl是在onResume的时候被初始化的,上面那段代码sleep久一点等ViewRootImpl初始化完毕就会报错

    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
             if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();//ViewRootImpl在这里被初始化
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }       
        }
    }

    总结

    1. 子线程可以在部分情况下修改UI,如不改变布局,在onResume之前
    2. 不推荐在子线程中修改UI
  • 相关阅读:
    在Applet中实现数字签名
    zend studio 8 注册码
    Debian(Linux)+XAMPP(LAMPP)+Zend Studio + PHP +XDebug 完整的开发环境配置方法。
    ssh免密码登陆方法
    vmware中按Ctrl+Alt+Del的困扰
    Bcastr v2.0播放器切换图片
    免费网络硬盘哪个赚钱?国内外可做网赚网络硬盘
    国外最好的BT站点
    PHP调用Webservice实例
    非常漂亮的Flash纯脚本生成图
  • 原文地址:https://www.cnblogs.com/billshen/p/13328680.html
Copyright © 2011-2022 走看看