子线程真的不能更新UI吗?
来源:互联网 发布:mac系统删除文件 编辑:程序博客网 时间:2024/05/16 01:30
子线程真的不能更新UI吗?其实,在onResume以及onResume之前,开启一个子线程来更新UI,都有可能是会成功的,并且成功率相当大,失败的情况应该也会有,比较极端的情况下,UI线程一直霸占的CPU,子线程一直执行不到。
子线程更新UI代码如下:
@Override protected void onResume() { new Thread(new Runnable() { @Override public void run() { textView.setText("子线程更新UI");//经测试,这里是成功的,那么onCreate,onStart就更不用说了 } }).start(); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { textView.setText("再试试子线程更新UI");//这里就不行了 } }).start(); } }); super.onResume(); }
揭晓原理:
我们都知道,在子线程中更新UI,会抛如下异常:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
翻译过来是:只有创建这个View层级的原始线程才能更新这些view. 也就是并不是一定非要在UI线程.
这个异常来自ViewRootImpl的checkThread方法:
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
只有ViewRootImpl创建完成以后,才会检查线程,那么ViewRootImpl什么时候创建的呢?
是在Activity的makeVisible中创建的:
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes());//ViewRootImpl就是在这个过程中创建的,请自行追踪源码 mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
那么makeVisible又是什么时候调用的呢?
是在onResume回调之后调用的。
这里简要介绍Activity启动流程中的一小部分:
ActivityThread.handleLaunchActivity {ActivityThread.performLaunchActivity —> activity.attach } — >【这中间还有onCreate和onStart的回调过程】—> ActivityThread.handleResumeActivity —>ActivityThread.performResumeActivity —> activity.performResume() —> Instrumentation.callActivityOnResume
—> activity.onResume —> activity.makeVisible()【这一步是在ActivityThread.handleResumeActivity中调用的】
看ActivityThread.handleResumeActivity源码,标出了重点代码行:
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { ...省略... ActivityClientRecord r = performResumeActivity(token, clearHide);//重点 if (r != null) { final Activity a = r.activity; ...省略... // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { ...省略... if (r.activity.mVisibleFromClient) { r.activity.makeVisible();//重点 } } ...省略... } else { // If an exception was thrown when trying to resume, then // just end this activity. try { ActivityManagerNative.getDefault() .finishActivity(token, Activity.RESULT_CANCELED, null, false); } catch (RemoteException ex) { } } }
ActivityThread.performResumeActivity中调用了activity.performResume()
public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide) { ActivityClientRecord r = mActivities.get(token); if (localLOGV) Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished); if (r != null && !r.activity.mFinished) { if (clearHide) { r.hideForNow = false; r.activity.mStartedActivity = false; } try { r.activity.onStateNotSaved(); r.activity.mFragments.noteStateNotSaved(); if (r.pendingIntents != null) { deliverNewIntents(r, r.pendingIntents); r.pendingIntents = null; } if (r.pendingResults != null) { deliverResults(r, r.pendingResults); r.pendingResults = null; } r.activity.performResume();//重点行 EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(), r.activity.getComponentName().getClassName()); r.paused = false; r.stopped = false; r.state = null; r.persistentState = null; } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to resume activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } } return r; }
activity.performResume()中通过Instrumentation.callActivityOnResume回调了Activity.onResume,
由上可以看出Activity.makeVisible是在onResume之后调用的,进而ViewRootImpl也是在onResume之后创建,所以就会有文章开头描述的现象:
在onResume以及onResume之前,开启一个子线程来更新UI,都有可能是会成功的,并且成功率相当大.
当ViewRootImpl创建完成,之后更新UI,比如TextView.setText(),会调用invalidate或者requestLayout,都会调用checkThread来检查线程的。
这个知识点的相关知识点其实很多:Activity的启动流程以及工作流程,Activity、Window、View之间的关系等
Activity启动流程,推荐老罗的Android之旅
Activity、Window、View之间的关系,就推荐下面这一篇吧
Android Activity 、 Window 、 View之间的关系
http://blog.csdn.net/u011733020/article/details/49465707
- android 子线程真的不能更新ui吗
- 子线程真的不能更新UI吗?
- Android 真的不能在子线程更新 UI 吗
- 子线程真的不能更新UI吗?
- Android子线程真的不能更新UI么
- Android子线程真的不能更新UI么
- Android子线程真的不能更新UI么
- 子线程中真的不能更新UI?
- 非UI线程真的不能更新UI吗?
- Android 中非UI线程真的不能更新UI吗?
- 多线程学习之--真的不能在子线程里更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Android中子线程真的不能更新UI吗?
- Advanced Analytics with Spark, 2nd Edition.pdf 英文原版 免费下载
- J
- 【转载】字符串常量到底存放在哪个存储区
- 171118 逆向-再探QQ防撤回
- 线段树区间加板子
- 子线程真的不能更新UI吗?
- CodeForces
- 如何下载并安装maven,以及在eclipse中的使用
- Learn iOS Application Distribution.pdf 英文原版 免费下载
- 4.3 构造方法与对象的创建
- java ArrayList排序不区分大小写
- 搜索策略实现-DFS,BFS,爬山法,分支界限法
- Maven-使用-DskipTests跳过单元测试
- hdu_2014 青年歌手大奖赛_评委会打分