一般UI更新都会调用requestLayout 或者invalidate, 自定义view 也要如此 ???。
@android.view.RemotableViewMethod
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
@Override
public void requestLayout() {//不会调用draw
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();// 1
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
“Only the original thread that created a view hierarchy can touch its views.”);
}
}
而在onCreate直接去更新UI,不走上述requestLayout 或者invalidate。其堆栈是这样的:
2020-08-09 09:39:48.374 3561-3561/com.yz.myapplication D/yyzz: android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3614) android.app.ActivityThread
2020-08-09 09:39:48.374 3561-3561/com.yz.myapplication D/yyzz: com.yz.myapplication.MainActivity.update(MainActivity.java:28) com.yz.myapplication.MainActivity
2020-08-09 09:39:48.374 3561-3561/com.yz.myapplication D/yyzz: android.app.ActivityThread.main(ActivityThread.java:7625) android.app.ActivityThread
2020-08-09 09:39:48.374 3561-3561/com.yz.myapplication D/yyzz: java.lang.reflect.Method.invoke(Native Method) java.lang.reflect.Method
2020-08-09 09:39:48.374 3561-3561/com.yz.myapplication D/yyzz: com.android.internal.os.RuntimeInit
M
e
t
h
o
d
A
n
d
A
r
g
s
C
a
l
l
e
r
.
r
u
n
(
R
u
n
t
i
m
e
I
n
i
t
.
j
a
v
a
:
524
)
c
o
m
.
a
n
d
r
o
i
d
.
i
n
t
e
r
n
a
l
.
o
s
.
R
u
n
t
i
m
e
I
n
i
t
MethodAndArgsCaller.run(RuntimeInit.java:524) com.android.internal.os.RuntimeInit
MethodAndArgsCaller.run(RuntimeInit.java:524)com.android.internal.os.RuntimeInitMethodAndArgsCaller
2020-08-09 09:39:48.374 3561-3561/com.yz.myapplication D/yyzz: android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3409) android.app.ActivityThread
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) android.app.servertransaction.TransactionExecutor
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.app.Activity.performCreate(Activity.java:7458) android.app.Activity
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:86) android.app.servertransaction.LaunchActivityItem
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.app.Activity.performCreate(Activity.java:7448) android.app.Activity
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1286) android.app.Instrumentation
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) android.app.servertransaction.TransactionExecutor
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: com.yz.myapplication.MainActivity.onCreate(MainActivity.java:21) com.yz.myapplication.MainActivity
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987) com.android.internal.os.ZygoteInit
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.os.Looper.loop(Looper.java:216) android.os.Looper
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.os.Handler.dispatchMessage(Handler.java:112) android.os.Handler
2020-08-09 09:39:48.375 3561-3561/com.yz.myapplication D/yyzz: android.app.ActivityThread
H
.
h
a
n
d
l
e
M
e
s
s
a
g
e
(
A
c
t
i
v
i
t
y
T
h
r
e
a
d
.
j
a
v
a
:
2199
)
a
n
d
r
o
i
d
.
a
p
p
.
A
c
t
i
v
i
t
y
T
h
r
e
a
d
H.handleMessage(ActivityThread.java:2199) android.app.ActivityThread
H.handleMessage(ActivityThread.java:2199)android.app.ActivityThreadH
在onCreate阶段子线程去快速更新UI,没有问题
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text=findViewById(R.id.aa);
new Thread(){
@Override
public void run() {
super.run();
update();
}
}.start();
其堆栈是:
2020-08-09 09:51:30.397 6600-6642/com.yz.myapplication D/yyzz: com.yz.myapplication.MainActivity$1.run(MainActivity.java:25) com.yz.myapplication.MainActivity$1
2020-08-09 09:51:30.397 6600-6642/com.yz.myapplication D/yyzz: com.yz.myapplication.MainActivity.update(MainActivity.java:35) com.yz.myapplication.MainActivity
因此也不影响更新UI
在onCreate阶段子线程延时1000ms去更新UI,产生问题
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text=findViewById(R.id.aa);
new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
update();
}
}.start();
2020-08-09 09:56:39.224 7100-7161/com.yz.myapplication E/AndroidRuntime: FATAL EXCEPTION: Thread-5
Process: com.yz.myapplication, PID: 7100
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8632)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1380)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3172)
at android.view.View.requestLayout(View.java:23377)
at android.widget.TextView.checkForRelayout(TextView.java:9221)
at android.widget.TextView.setText(TextView.java:5935)
at android.widget.TextView.setText(TextView.java:5776)
at android.widget.TextView.setText(TextView.java:5733)
at com.yz.myapplication.MainActivity.update(MainActivity.java:36)
at com.yz.myapplication.MainActivity$1.run(MainActivity.java:30)
这里很好解释:ViewRootImpl 在 onCreate() 时还没创建;
在 onResume()时,即ActivityThread 的 handleResumeActivity() 执行后才创建,
才调用 requestLayout(),走到 checkThread() 时就报错了。
那上面怎么解释?
看:
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();//ViewRootImpl 在 onCreate() 时还没创建;
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {//满足这两个条件才往下走!
mParent.requestLayout();//向父容器请求布局,最终到达ViewRootImpl的requestLayout:堆栈:
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1380)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at android.view.View.requestLayout(View.java:23377)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3172)
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
说到「只能在主线程中更新UI」我又想到另一个问题「不能在主线程中进行网络操作」NetworkOnMainThreadException:网络请求在主线程进行异常。
.为什么系统不建议在子线程访问UI?
Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态
这时你可能会问为何系统不对UI控件的访问加上锁机制呢?因为
加锁机制会让UI访问逻辑变的复杂
加锁机制会降低UI的访问效率,因为加锁会阻塞某些线程的执行。
主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,来消息能唤醒处理,能一直处理事件就不会产生ANR异常。
主线程都堵住了,怎么响应用户操作和回调Activity声明周期相关的方法?
答:application启动时,可不止一个main线程,还有其他两个Binder线程:ApplicationThread 和 ActivityManagerProxy,用来和系统进程进行通信操作,接收系统进程发送的通知。 下文可专讲这两个线程的创建和使用。