非UI线程真的不能更新UI吗?
来源:互联网 发布:数据透视表列总计移动 编辑:程序博客网 时间:2024/04/29 12:22
废话不说,先看一个简单的效果
<span style="font-size:12px;">package com.xiaojiukeji.updateui;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); new Thread(){ @Override public void run() { updataUI(); } }.start(); } private void updataUI() { tv.setText("更新后的数据"); }}</span>
布局就不贴了,就只有一个textView,执行后的效果是
是不是有点匪夷所思?别急,改一下代码,再看!
这里只是把主线程休眠一秒,然后再更新UI
<span style="font-size:12px;">package com.xiaojiukeji.updateui;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); new Thread(){ @Override public void run() { try { Thread.sleep(1000); updataUI(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } private void updataUI() { tv.setText("更新后的数据"); }}</span>果然,这次报了错
<span style="font-size:12px;">FATAL EXCEPTION: Thread-63736 Process: com.xiaojiukeji.updateui, PID: 12295 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7665) at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1119)</span>
很明显,错误提示不能在子线程中更新UI,那这就奇怪了,为什么呢?为什么直接更新就可以呢?且往下看
在setText()源码中可以找到这样一段代码
<span style="font-size:12px;">/** * Check whether entirely new text requires a new view layout * or merely a new text layout. */ private void checkForRelayout() { // If we have a fixed width, we can just swap in a new text layout // if the text height stays the same or if the view height is fixed. if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT || (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth)) && (mHint == null || mHintLayout != null) && (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) { // Static width, so try making a new text layout. int oldht = mLayout.getHeight(); int want = mLayout.getWidth(); int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth(); /* * No need to bring the text into view, since the size is not * changing (unless we do the requestLayout(), in which case it * will happen at measure). */ makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING, mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(), false); if (mEllipsize != TextUtils.TruncateAt.MARQUEE) { // In a fixed-height view, so use our new text layout. if (mLayoutParams.height != LayoutParams.WRAP_CONTENT && mLayoutParams.height != LayoutParams.MATCH_PARENT) { <strong> <span style="color:#FF0000;">invalidate();</span></strong> return; } // Dynamic height, but height has stayed the same, // so use our new text layout. if (mLayout.getHeight() == oldht && (mHintLayout == null || mHintLayout.getHeight() == oldht)) { <em><strong><span style="color:#FF0000;">invalidate();</span></strong></em> return; } } // We lose: the height has changed and we have a dynamic height. // Request a new view layout using our new text layout. requestLayout(); <span style="color:#FF0000;"><em><strong>invalidate();</strong></em></span> } else { // Dynamic width, so we have no choice but to request a new // view layout with a new text layout. nullLayouts(); requestLayout(); <span style="color:#FF0000;"><strong>invalidate();</strong></span> } }</span>可以看到不管怎么样,都会执行到上面代码中我用红色标出来了invalidata()方法,这个是让UI界面绘制的,可以点击去继续看
<span style="font-size:12px;">public void invalidate() { invalidate(true); } /** * This is where the invalidate() work actually happens. A full invalidate() * causes the drawing cache to be invalidated, but this function can be * called with invalidateCache set to false to skip that invalidation step * for cases that do not need it (for example, a component that remains at * the same dimensions with the same content). * * @param invalidateCache Whether the drawing cache for this view should be * invalidated as well. This is usually true for a full * invalidate, but may be set to false if the View's contents or * dimensions have not changed. */ void invalidate(boolean invalidateCache) { invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); } void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { if (mGhostView != null) { mGhostView.invalidate(true); return; } if (skipInvalidate()) { return; } if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || (fullInvalidate && isOpaque() != mLastIsOpaque)) { if (fullInvalidate) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; } mPrivateFlags |= PFLAG_DIRTY; if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } // Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p != null && ai != null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); <span style="font-size:14px;color:#FF0000;"><strong> p.invalidateChild(this, damage);</strong></span> } // Damage the entire projection receiver, if necessary. if (mBackground != null && mBackground.isProjected()) { final View receiver = getProjectionReceiver(); if (receiver != null) { receiver.damageInParent(); } } // Damage the entire IsolatedZVolume receiving this view's shadow. if (isHardwareAccelerated() && getZ() != 0) { damageShadowReceiver(); } } }</span>其中
<span style="font-size:12px;"><span style="font-size:14px;color:#FF0000;"><strong>p.invalidateChild(this, damage);</strong></span></span>在判断当前操作是都在子线程中进行,也就是说,执行成功与否,在于这行代码的判断
感兴趣的同学可以通过source软件查看深层源代码,查看执行过程
总结:
在非UI线程中是可以更新Ui的(在onCreate中创建新线程更新UI):
当刚启动Activity即onCreate里面此时onResume方法还没有执行的时候可以,因为在线程中更新UI时会调用ViewParent.invalidateChild()方法检查当前的Thread是否是UIThread,
若为UIThread则可以更新(ViewParent是一个接口类,其实现是ViewRootImpl;invalidateChild()方法调用checkThread()方法来检查线程是否为主线程)。ViewRootImp是
在onResume方法中初始化的,所以只要在ViewRootImpl创建之前更新UI(在onCreate方法中创建线程并执行,此时还没有初始化ViewRootImp),
就可以逃避掉checkThread()的检查进而更新UI。
0 0
- 非UI线程真的不能更新UI吗?
- Android 中非UI线程真的不能更新UI吗?
- Android提高(16)——第六章 非UI线程真的不能更新UI吗
- android 子线程真的不能更新ui吗
- 子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android 真的不能在子线程更新 UI 吗
- 子线程真的不能更新UI吗?
- Android子线程真的不能更新UI么
- 简单通讯录(以数组的方式实现增删改查)
- Android NativeWindow render 打印输出
- 自定义recycler的分割线
- 有序链表转BST(平衡查找二叉树)
- HDU 5898 odd-even number(2016 ACM/ICPC Asia Regional Shenyang Online)
- 非UI线程真的不能更新UI吗?
- cell上自定义button点击获取cell的信息
- FZU - 2020 组合(逆元+卢卡斯)
- 使用TestNG进行模块自动化压力测试
- SSM ajax实现批量启用和批量禁用
- POJ 1442 Black Box Treap
- GateServer 网关
- 你知道数据运营日常主要工作吗?
- KM算法模板(HDU 2255)