内容简介:运行结果:正常运行!!!运行结果:异常不是说,子线程不能更新UI吗,为什么情形一可以正常运行,情形二不能正常运行呢;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = findViewById(R.id.home_tv); ImageView imageView = findViewById(R.id.home_img); new Thread(new Runnable() { @Override public void run() { textView.setText("更新TextView"); imageView.setImageResource(R.drawable.img); } }).start(); } 复制代码
运行结果:正常运行!!!
情形二
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = findViewById(R.id.home_tv); ImageView imageView = findViewById(R.id.home_img); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } textView.setText("更新TextView"); imageView.setImageResource(R.drawable.img); } }).start(); } 复制代码
运行结果:异常
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6357) at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:874) at android.view.View.requestLayout(View.java:17476) at android.view.View.requestLayout(View.java:17476) at android.view.View.requestLayout(View.java:17476) at android.view.View.requestLayout(View.java:17476) at android.view.View.requestLayout(View.java:17476) at android.view.View.requestLayout(View.java:17476) at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:360) at android.view.View.requestLayout(View.java:17476) at android.widget.TextView.checkForRelayout(TextView.java:6871) at android.widget.TextView.setText(TextView.java:4057) at android.widget.TextView.setText(TextView.java:3915) at android.widget.TextView.setText(TextView.java:3890) at com.dong.demo.MainActivity$1.run(MainActivity.java:44) at java.lang.Thread.run(Thread.java:818) 复制代码
不是说,子线程不能更新UI吗,为什么情形一可以正常运行,情形二不能正常运行呢;
1.子线程修改UI出现异常,与什么方法有关
1.子线程什么时候可以更新UI
2.Android为什么要规定主线程更新UI
一、子线程修改UI出现异常,与什么方法有关
首先从出现异常的log日志入手,发现出现异常的方法调用顺序如下:
TextView.setText(TextView.java:4057)
TextView.checkForRelayout(TextView.java:6871)
View.requestLayout(View.java:17476)
RelativeLayout.requestLayout(RelativeLayout.java:360)
View.requestLayout(View.java:17476)
ViewRootImpl.requestLayout(ViewRootImpl.java:874)
ViewRootImpl.checkThread(ViewRootImpl.java:6357)
更改ImageView时,出现的异常类似;
首先看TextView.setText()方法的源码
private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) { //省略其他代码 if (mLayout != null) { checkForRelayout(); } sendOnTextChanged(text, 0, oldlen, textLength); onTextChanged(text, 0, oldlen, textLength); //省略其他代码 复制代码
然后,查看以下checkForRelayout()方法的与源码。
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 //省略代码 // We lose: the height has changed and we have a dynamic height. // Request a new view layout using our new text layout. requestLayout(); invalidate(); } else { // Dynamic width, so we have no choice but to request a new // view layout with a new text layout. nullLayouts(); requestLayout(); invalidate(); } } 复制代码
checkForReLayout方法,首先会调用需要改变的View的requestLayout方法,然后执行invalidate()重绘操作;
TextView没有重写requestLayout方法,requestLayout方法由View实现;
查看RequestLayout方法的源码:
public void requestLayout() { //省略其他代码 if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; } } 复制代码
View获取到父View(类型是ViewParent,ViewPaerent是个接口,requestLayout由子类来具体实现),mParent,然后调用父View的requestLayout方法,比如示例中的父View就是xml文件的根布局就是RelativeLayout。 RelativeLayout的requestLayout()方法源码:
@Override public void requestLayout() { super.requestLayout(); mDirtyHierarchy = true; } 复制代码
继续跟踪super.requestLayout()方法,即ViewGroup没有重新,即调用的是View的requestLayout方法。
经过一系列的调用ViewParent的requestLayout方法,最终调用到ViewRootImp的requestLayout方法。ViewRootImp实现了ViewParent接口,继续查看ViewRootImp的requestLayout方法源码。
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } } 复制代码
ViewRootImp的requestLayout方法中有两个方法:
一、checkThread,检查线程,源码如下
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } } 复制代码
判断当前线程,是否是创建ViewRootImp的线程,而创建ViewRootImp的线程就是主线程,当前线程不是主线程的时候,就抛出异常。 二、scheduleTraversals(),查看源码:
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } 复制代码
查看mTraversalRunnable中run()方法的具体操作
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } 复制代码
继续追踪doTraversal()方法
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } } 复制代码
查看到performTraversals()方法,熟悉了吧,这是View绘制的起点。
总结一下:
1.Android更新UI会调用View的requestLayout()方法,在requestLayout方法中,获取ViewParent,然后调用ViewParent的requestLayout()方法,一直调用下去,直到调用到ViewRootImp的requestLayout方法;
2.ViewRootImp的requetLayout方法,主要有两部操作一个是checkThread()方法,检测线程,一个是scheduleTraversals,执行绘制相关工作;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Android UI 线程更新UI也会崩溃???
- 如何使用Android Handler更新UI线程中的TextView?
- 关于synchronized锁在Spring事务中进行数据更新同步,仍出现线程安全问题
- java中线程安全,线程死锁,线程通信快速入门
- ObjC 多线程简析(一)-多线程简述和线程锁的基本应用
- Java多线程之线程中止
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java学习笔记
林信良 / 清华大学出版社 / 2015-3-1 / CNY 68.00
●本书是作者多年来教学实践经验的总结,汇集了学员在学习课程或认证考试中遇到的概念、操作、应用等问题及解决方案 ●针对Java SE 8新功能全面改版,无论是章节架构或范例程序代码,都做了重新编写与全面翻新 ●详细介绍了JVM、JRE、Java SE API、JDK与IDE之间的对照关系 ●从Java SE API的源代码分析,了解各种语法在Java SE API中的具体应用 ......一起来看看 《Java学习笔记》 这本书的介绍吧!