子线程真的不能更新UI吗?
2017-11-24 01:21
176 查看
子线程真的不能更新UI吗?其实,在onResume以及onResume之前,开启一个子线程来更新UI,都有可能是会成功的,并且成功率相当大,失败的情况应该也会有,比较极端的情况下,UI线程一直霸占的CPU,子线程一直执行不到。
子线程更新UI代码如下:
揭晓原理:
我们都知道,在子线程中更新UI,会抛如下异常:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
翻译过来是:只有创建这个View层级的原始线程才能更新这些view. 也就是并不是一定非要在UI线程.
这个异常来自ViewRootImpl的checkThread方法:
只有ViewRootImpl创建完成以后,才会检查线程,那么ViewRootImpl什么时候创建的呢?
是在Activity的makeVisible中创建的:
那么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源码,标出了重点代码行:
ActivityThread.performResumeActivity中调用了activity.performResume()
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之间的关系,就推荐下面这一篇吧
子线程更新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交流群:230274309相关文章推荐
- 为什么ios中,子线程为什么不能更新UI,必须回到主线程
- android中的UI视图更新不能放在子线程中操作
- 子线程一定不能更新UI吗?
- Android 中确定子线程不能更新UI吗?
- android 子线程真的不能更新ui吗
- Andoird主子线程通信,子线程不能更新UI,4大方法子线程更新UI
- 为什么子线程不能更新UI的完全解析!!
- 子线程真的不能更新UI吗?
- ui不能直接在子线程里更新,必须通过Handler
- Android开发之——子线程中使用Toast或者更新UI
- Android子线程真的不能更新UI吗?让我们从源码的角度一探究竟
- Android能否在子线程中更新UI呢?
- Android 真的不能在子线程更新 UI 吗
- 非UI线程不能更新View源码探索
- Android子线程真的不可以更新UI吗???
- 为什么我的子线程更新了 UI 没报错?借此,纠正一些Android 程序员的一个知识误区
- Android子线程居然可以更新UI?
- 多线程学习之--真的不能在子线程里更新UI吗?
- 子线程中无法刷新更新 UI
- Android中子线程真的不能更新UI吗?