子线程不能更新UI线程总结
2017-01-09 22:10
495 查看
子线程不能更新UI线程总结
子线程整的不能更新UI线程吗android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
有时候大家做项目的时候偶尔会碰到这个错误。不用说大家都知道是子线程更新主线程(UI)线程的问题,同样大家也会给出相对应的解法:使用handle+Thread方法通过发送Message进行更新UI线程。
eg:
new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } iv_img.setBackground(ContextCompat.getDrawable(MainActivity.this,R.drawable.ic_launcher)); btn_commit.setText("iiiiiiiiiiiiiiiiiiiii "); Toast.makeText(MainActivity.this,"阿斯蒂芬噶是的发送到",Toast.LENGTH_LONG).show(); } }).start();
此时就会出现一下错误:
此时我们都知道最简单的一种解决方式就是:
new Thread(new Runnable() { @Override public void run() { Message msg = handler.obtainMessage(); msg.what = 100; msg.obj = "发送消息"; handler.sendMessage(msg); } }).start(); }
接下来我们就来探究一下:子线程和UI线程之间更新问题。
首先我们要知道:
android应用程序遵循的是按照单线程模式的原则
这是因为:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
android的UI线程就是主线程,当成粗第一次启动的时候 Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。这个和Java的运行机制是不同的。
andorid在UI中进行绘图和处理事件启动一个监听的作用 ,此时就必须要在UI中时时刻刻的进行相应用户的点击事件和UI操作事件。异步操作以及耗时操作需要另外气一个线程,不然UI线程在5s内未响应用户的操作,系统就会弹出弹出对话框停止程序终止进程的提示。
我们想看看报错的这行代码在ViewRootImpl.java:
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
mThread是一个线程,如果改线程是当前的线程的时候,则继续向下走,不会抛出异常。大家可以看到,我在onCreate方法,只有一个线程,肯定是当前的线程,那为什么会报错呢?我们接着往下看。
接下来我们看看checkThread()在那几处用到了,其中有
@Override public ViewParent invalidateChildInParent(int[] location, Rect dirty) { checkThread(); if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty); ....... invalidateRectOnScreen(dirty); return null; }
@Override public void requestFitSystemWindows() { checkThread(); mApplyInsetsRequested = true; scheduleTraversals(); }
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
从报错的点 at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:848)我们可以看到是848行是invalidateChildInParent方法里调用的。我们进一步与发现invalidateChildInParent又在invalidateChild()里调用,在View中那个地方调用了呢?我们进一步向下跟进。
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { ...... 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); p.invalidateChild(this, damage); } ...... }
而invalidateInternal方法在invalidate()方法中是这样的
void invalidate(boolean invalidateCache) { invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); }
到这里一目了然了。原来View中再重新回执的时候刷新方法里调用了ViewRootImpl的checkThre(),刷新是检查该线程是不是当前的线程,即主线程。
我们根据源码可以知道invalidate()–>checkThread()是一步一步去调用的。
android.view.View.setBackground()
android.view.View.setBackground()—->setBackgroundDrawable()–> invalidate()–>invalidateInternal()–>invalidateChild()–>checkThread()–>invalidateRectOnScreen()
现在我们清楚了,严格的来说原来子线程是不能刷新UI线程的。
解决方式:
第一种方式:
new Thread(new Runnable() { @Override public void run() { Message msg = handler.obtainMessage(); msg.what = 100; msg.obj = "发送消息"; handler.sendMessage(msg); } }).start(); }
第二中方式:
runOnUiThread(new Runnable() { @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } iv_img.setBackground(ContextCompat.getDrawable(MainActivity.this,R.drawable.ic_launcher)); } });
第三种方式: 利用AsyncTask方法。
以上都是进程间通讯的几种方式。这里我不再做过多的描述。
如果您觉我的文章对您有所帮助,
QQ交流群 :232203809,欢迎入群
微信公众号:终端研发部
(欢迎关注学习和交流)
相关文章推荐
- 在Java中自定义的一个key对多个Value的map
- vue树形组件
- ACM—Number Sequence(HDOJ1005)
- layout、values和drawable屏幕自适应
- Android Bluetooth 蓝牙通信(二)
- build.gradle常见配置方式
- 【转】简析SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
- 【J2SE】——String、StringBuffer与StringBuilder
- requests源码分析
- 19、Power Query-快速分析各产品的销售情况
- serializable 类 XXXX 未声明类型为 long 的静态终态 serialVersionUID 字段
- vue-cli 中 使用vue-resource 输出后台数据
- build_release/lib/libcaffe.so: undefined reference to cv::imread(cv::String const&, int)'
- UVA 1327 King's Quest(强联通+二分图)
- ios在UITableViewController里使用UISearchDisplayController报错"[UISearchResultsTableView dequeueReusableCel
- Vue中使用set方法过程的一个小发现
- JavaServer Faces 2.0 requires Dynamic Web Module 2.5 or newer..Maven Java EE Configuration
- vuex构建笔记本应用学习
- soapUI通过groovy脚本设置超时时间
- 创建Qt Quick项目