真的必须要在主线程更新UI吗?

一般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,用来和系统进程进行通信操作,接收系统进程发送的通知。 下文可专讲这两个线程的创建和使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值