`

Android 里子线程真的不能刷新UI吗?

 
阅读更多
如果你在网上搜索CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views. 那么你肯定能看到很多文章说android里子线程不能刷新UI。这句话不能说错,只是有些不太严谨。其实线程能否刷新UI的关键在于ViewRoot是否属于该线程。
让我们一起看看代码吧!
首先,CalledFromWrongThreadException这个异常是有下面的代码抛出的:
view source
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
}
该段代码出自 framework/base/core/java/android/view/ViewRoot.java
其次,看看RootView的构造函数:
public ViewRoot(Context context) {
    super();
    if (MEASURE_LATENCY && lt == null) {
        lt = new LatencyTimer(100, 1000);
    }
    // For debug only
    //++sInstanceCount;
    // Initialize the statics when this class is first instantiated. This is
    // done here instead of in the static block because Zygote does not
    // allow the spawning of threads.
    getWindowSession(context.getMainLooper());
    mThread = Thread.currentThread();
    mLocation = new WindowLeaked(null);
    mLocation.fillInStackTrace();
    mWidth = -1;
    mHeight = -1;
    mDirty = new Rect();
    mTempRect = new Rect();
    mVisRect = new Rect();
    mWinFrame = new Rect();
    mWindow = new W(this, context);
    mInputMethodCallback = new InputMethodCallback(this);
    mViewVisibility = View.GONE;
    mTransparentRegion = new Region();
    mPreviousTransparentRegion = new Region();
    mFirst = true; // true for the first time the view is added
    mAdded = false;
    mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
    mViewConfiguration = ViewConfiguration.get(context);
    mDensity = context.getResources().getDisplayMetrics().densityDpi;
}
最后,我们看看ViewRoot.checkThread的调用顺序:
    com.david.test.helloworld.MainActivity$TestThread2.run
      -> android.widget.TextView.setText
        -> android.widget.TextView.checkForRelayout
          -> android.view.View.invalidate
            -> android.view.ViewGroup.invalidateChild
              -> android.view.ViewRoot.invalidateChildInParent
                -> android.view.ViewRoot.invalidateChild
                  -> android.view.ViewRoot.checkThread
到这里相信网友已经明白CalledFromWrongThreadException为什么出现了。那到底非主线程以外的线程能否刷新UI呢?呵呵,答案当然是能,前提条件是它要拥有自己的ViewRoot。如果你要直接创建ViewRoot的实例的话,你会失望的发现不能找到这个类。那么我们要如何做呢?让我们用实例来说说吧,代码如下:
       class TestThread1 extends Thread{    
                  @Override    
                  public void run() {    
                         Looper.prepare();
                         TextView tx = new TextView(MainActivity.this);    
                         tx.setText("test11111111111111111");
                         WindowManager wm = MainActivity.this.getWindowManager();    
                      WindowManager.LayoutParams params = new WindowManager.LayoutParams(    
    250, 250, 200, 200, WindowManager.LayoutParams.FIRST_SUB_WINDOW,    
                                   WindowManager.LayoutParams.TYPE_TOAST,PixelFormat.OPAQUE);                          
                         wm.addView(tx, params);                        
                         Looper.loop();    
                  }    
        }

MainActivity是建立android工程时生成的入口类,TestThread1是MainActivity的内部类。感兴趣的话,试试吧!看看是不是在屏幕上看到了"test11111111111111111"?
最后,说说那里创建了ViewRoot,这里:wm.addView(tx, params)。还是看看具体流程吧:
WindowManagerImpl.addView(View view, ViewGroup.LayoutParams params)
  -> WindowManagerImpl.addView(View view, ViewGroup.LayoutParams params, boolean nest),奥妙就在这里,具体看看代码吧!
    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
    {
        if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException(
                    "Params must be WindowManager.LayoutParams");
        }
        final WindowManager.LayoutParams wparams
                = (WindowManager.LayoutParams)params;
        ViewRoot root;
        View panelParentView = null;           
        synchronized (this) {
            // Here's an odd/questionable case: if someone tries to add a
            // view multiple times, then we simply bump up a nesting count
            // and they need to remove the view the corresponding number of
            // times to have it actually removed from the window manager.
            // This is useful specifically for the notification manager,
            // which can continually add/remove the same view as a
            // notification gets updated.
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (!nest) {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                root = mRoots[index];
                root.mAddNesting++;
                // Update layout parameters.
                view.setLayoutParams(wparams);
                root.setLayoutParams(wparams, true);
                return;
            }
 
            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews != null ? mViews.length : 0;
                for (int i=0; i<count; i++) {
                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews[i];
                    }
                }
            }             
            root = new ViewRoot(view.getContext());
            root.mAddNesting = 1;
            view.setLayoutParams(wparams);
            if (mViews == null) {
                index = 1;
                mViews = new View[1];
                mRoots = new ViewRoot[1];
                mParams = new WindowManager.LayoutParams[1];
            } else {
                index = mViews.length + 1;
                Object[] old = mViews;
                mViews = new View[index];
                System.arraycopy(old, 0, mViews, 0, index-1);
                old = mRoots;
                mRoots = new ViewRoot[index];
                System.arraycopy(old, 0, mRoots, 0, index-1);
                old = mParams;
                mParams = new WindowManager.LayoutParams[index];
                System.arraycopy(old, 0, mParams, 0, index-1);
            }
            index--;
            mViews[index] = view;
            mRoots[index] = root;
            mParams[index] = wparams;
        }
        // do this last because it fires off messages to start doing things
        root.setView(view, wparams, panelParentView);
}
出自:frameworks/base/core/java/android/view/WindowManagerImpl.java

Ok,相信到了这里,大家都已经明白了:子线程是能够刷新UI的!!!

原文出处:http://blog.csdn.net/imyfriend/article/details/6877959

分享到:
评论

相关推荐

    C#(winform)里子窗体修改父窗体(子窗体刷新父窗体数

    ### C# WinForms 子窗体刷新父窗体数据机制详解 #### 背景介绍 在C# WinForms应用程序开发中,有时我们需要实现在子窗体中对数据进行修改后,能够立即更新主窗体(父窗体)中显示的数据。这种需求常见于多级界面的...

    从“里子”怎么看出口?_&出口研判系列九:外需变化是“表”,结构变化是“里”(34页).pdf

    从“里子”怎么看出口?_&出口研判系列九:外需变化是“表”,结构变化是“里”(34页).pdf

    20210706-华创证券-从“里子”怎么看出口?_&出口研判系列九:外需变化是“表”,结构变化是“里”.pdf

    20210706-华创证券-从“里子”怎么看出口?_&出口研判系列九:外需变化是“表”,结构变化是“里”.pdf

    遍历已知父窗里子窗所有句柄

    在Windows操作系统中,窗口是应用程序与用户交互的基本单元。每个窗口都有一个唯一的标识,被称为窗口句柄(HWND)。窗口句柄是一种整数值,系统通过它来识别和操作窗口。本篇将深入探讨如何在已知父窗口句柄的情况...

    电子-一种抗静电里子绸的生产工艺

    在电子行业中,抗静电技术是至关重要的一个领域,特别是在生产里子绸这类纺织品时。里子绸,作为衣物的内衬,与人体皮肤接触密切,因此其静电性能直接影响到穿着舒适度和安全性。本生产工艺的重点就是如何通过特殊...

    scrollView 里子view 划出屏幕后的操作

    针对“scrollView 里子view 划出屏幕后的操作”这一主题,我们需要讨论如何监控子视图的状态变化,并根据它们是否可见执行相应的操作,比如在视频视图不可见时暂停视频播放。 首先,了解`UIScrollView`的工作原理是...

    NX二次开发,树列表用法介绍

    ### NX二次开发中Block UI Style树列表用法详细介绍 #### 一、概述 在NX二次开发过程中,Block UI Style树列表是一种非常实用的组件,它主要用于展示层级结构的数据,并提供了丰富的交互方式。本文将详细介绍Block...

    数据仓库的设计(一) _ 面子与里子1

    业务源系统是原始数据的来源,如销售系统或客户管理系统,其数据格式由各自系统定义,不能直接用于分析。ETL(抽取、转换、加载)系统负责将业务源系统的数据转换成适合数据仓库的格式。在这个过程中,数据经过清洗...

    数据仓库的设计(二) _ 面子与里子1

    数据仓库的设计是一个复杂而关键的过程,它涉及到对业务流程的理解、数据粒度的确定、维度和事实表的设计等多个环节。本文以零售行业的数据仓库为例,深入讲解了这些关键步骤。 首先,选择业务过程是设计数据仓库的...

    自动配裁服装里子样板模型的设计及应用* (2005年)

    基于图形变换原理,对自动配裁服装里子样板模型中各型值点的确定,提出了“动态”变换矩阵模型及对特殊曲线上中间型值点进行双向柔性化处理的有效设计方法;三次样条函数作为里子样板曲线模型的设计方案。结合服装...

    公交的“面子”地铁的“里子”——金华新能源地铁巴士运营看点多.pdf

    金华市公交系统近日推出了一项创新之举——新能源地铁巴士,这不仅是公交行业的“面子”工程,更是公交服务的“里子”升级。这款新型公交车辆融合了地铁的便捷性与公交车的灵活性,旨在提供更优质的城市公共交通服务...

    详解VUE里子组件如何获取父组件动态变化的值

    Vue.js 是一款流行的前端框架,用于构建用户界面。在 Vue 中,组件是可复用的代码块,它们之间可以通过 props 进行数据传递。当父组件的状态发生变化时,子组件通常需要根据这些变化来更新自己的状态。...

    一个VUE组件子元素scroll父元素容器不跟随滚动兼容PC移动端

    在Vue.js应用中,我们经常会遇到这样的需求:子元素具有滚动功能,而父元素保持静止,不随子元素滚动。这种布局模式在各种界面设计中都很常见,特别是在需要固定顶部导航或者侧边栏的时候。在PC和移动端,由于设备...

    200SMART PLC在符号表中修改变量的符号名后,程序中仍然显示修改前的符号和波浪线?.docx

    确保正确执行以上步骤后,200SMART PLC的程序应该能正确反映出你在符号表中所做的修改。对于初学者来说,理解寻址方式和符号表的运作原理至关重要,这有助于避免类似的问题,并且能够提高编程效率。在进行符号表操作...

    法那科主程式读子程式格式

    对于刀具比较多的零件加工程式调机方便可以对刀具分开后处理调试程式。

    SPC管理程序

    - **离线访问**:客户端可以缓存部分数据,在网络不稳定或离线时仍能工作。 - **定制化界面**:可根据用户需求定制用户界面,提升用户体验。 不过,C/S架构也存在一些挑战,如升级维护困难(需要同时更新客户端和...

    里子衍生神经网络模型及其在图像恢复中的应用 (2013年)

    为提高图像恢复质量,提出一种量子衍生神经网络模型及算法.该模型为3层结构,隐层为量子神经元,输出层为普通神经元.量子神经元由量子旋转门和多位受控非门组成,利用多位受控非门中目标量子位的输出向输入端的反馈...

    易语言获取目录下所有子目录

    易语言是一种专为中国人设计的编程语言,它以简体中文作为编程语句,降低了编程的门槛,使得更多的人能够理解和使用编程技术。...通过易语言的这种直观的语法,即使是编程新手也能轻松理解和实现这类功能。

    ECM技术简介PPT课件.pptx

    储运时的箱子和袋子等不能采用塑料等可能产生静电的材料,使用金属或纸的容器;组立工作后的接线因尽可能快的完成;手套等化学纤维制品不能使用;采用手腕金属带接地,效果会更好。 5. ECM 的应用 ECM 广泛应用于...

Global site tag (gtag.js) - Google Analytics