- 众所周知在
Android
中,子线程是不能更新UI
的; - 那么我在想,为什么不能,会产生什么问题;
- 是否真的就一定不能在子线程更新
UI
;
答案是可以的,比如以下代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.tv);
new Thread(new Runnable() {
@Override
public void run() {
tv.setText("测试是否报出异常");
}
}).start();
}
运行结果并无异常,可以正常的在子线程中更新了TextView
控件;假如让线程休眠1000ms
,就会发生错误:
Only the original thread that created a view hierarchy can touch its views.
这句话的意思是只有创建视图层次结构的原始线程才能更新这个视图,也就是说只有主线程才有权力去更新UI
,其他线程会直接抛异常的; 从at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7905)
的异常路径可以看到抛出异常的最终在ViewRootIml
的checkThread
方法里,ViewRootIml
是View
的根类,其控制着View
的测量、绘制等操作,那么现在我们转到ViewRootImpl.java
源码观察:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
scheduleTraversals()
里是对View
进行绘制操作,而在绘制之前都会检查当前线程是否为主线程mThread
,如果不是主线程,就抛出异常;这样做法就限制了开发者在子线程中更新UI的操作; 但是为什么最开始的在onCreate()
里子线程对UI
的操作没有报错呢,可以设想一下是因为ViewRootImp
此时还没有创建,还未进行当前线程的判断; 现在,我们寻找ViewRootImp
在何时创建,从Activity
启动过程中寻找源码,通过分析可以查看ActivityThread.java
源码,并找到handleResumeActivity
方法:
final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume) {
···
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
···
}
内部调用了performResumeActivity
方法: ··· public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide) {if (r != null && !r.activity.mFinished) { r.activity.performResume(); } 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方法,可以肯定应该是要回调生命周期的
onResume```方法了:
final void performResume() {
···
mCalled = false;
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onResume()");
}
···
}
,然后又调用了Instrumentation
的callActivityOnResume
方法:
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?