为什么非UI线程可以修改UI?

时间:2018-02-11 01:12:34

标签: android multithreading debugging

是的,它不应该,但我在mainActivity onCreate中创建的主题可以修改用户界面,如下所示:

Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                someTextview.setText("Hello");
            }
        });
        thread.start();

我正在使用android studio 2.2.2。

4 个答案:

答案 0 :(得分:6)

简短的回答:你可以在主线程进入onResume()之前做到这一点

详情:

从非UI线程

修改UI时,通常会出现这样的错误
android.view.ViewRootImpl$CalledFromWrongThreadException:
    Only the original thread that created a view hierarchy can touch its views.

此异常在ViewRootImpl

中抛出
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

mThread是主线​​程。创建ViewRootImpl实例后,UI修改操作将需要检查线程。

那么什么时候创建了ViewRootImpl实例?

android.view.WindowManagerImpl.java

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

android.view.WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    ...
    ViewRootImpl root;
    ...
    root = new ViewRootImpl(view.getContext(), display);
    ...
}

那么什么时候调用addView方法?

android.app.ActivityThread.java

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ...
        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 (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient && !a.mWindowAdded) {
                a.mWindowAdded = true;
                wm.addView(decor, l);
            }

}

 public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                ...
                case RESUME_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
                            args.argi3, "RESUME_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                ...
            }
            Object obj = msg.obj;
            if (obj instanceof SomeArgs) {
                ((SomeArgs) obj).recycle();
            }
            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
        }

在这里你可以看到ViewRootImpl实例是在RESUME_ACTIVITY事件周围创建的,所以在主线程处理resume事件之前,你可以从其他线程修改UI。你只需要快点。

不建议这样做。

答案 1 :(得分:0)

如果要在非UI线程上修改UI,可以通过

进行
  1. Activity.runOnUIThread()
  2. Runnable
  3. View.Post(Runnable)
  4. AsyncTask
  5. 在您的代码中,您使用的是Activity.runOnUIThread()

    查看https://developer.android.com/guide/components/processes-and-threads.html

答案 2 :(得分:0)

  

为什么非UI线程可以修改UI?

从技术上讲,非UI线程可以做到这一点。但这是一个非常糟糕的主意。

为什么?

因为如果你这样做,线程安全会有各种各样的问题。 UI框架的设计假设它可以在不同步的情况下访问UI数据结构。 (这极大地简化了事情,并消除了UI和非UI线程之间死锁的可能性......对于凡人程序员来说,这几乎是不可能的。)

这可能导致最严重的错误;即那些你无法测试的,你无法可靠地复制......或者根本不能复制......在你的硬件上。

Android框架试图保护您不要对自己做这类事情......

答案 3 :(得分:0)

您无法从UI线程或&#34; main&#34;以外的任何线程更新UI。线程。

仅使用:

  1. Activity.runOnUIThread(runnable)
  2. View.Post(Runnable)view.postDelayed()
  3. AsyncTask
  4. 重要的问题是为什么您的代码正在修改UI?

    可能是,您从Thread开始onCreate()。例如,如果您尝试在Button onClick()内运行此主题,则您的应用会崩溃 CalledFromWrongThreadException