是的,它不应该,但我在mainActivity
onCreate
中创建的主题可以修改用户界面,如下所示:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
someTextview.setText("Hello");
}
});
thread.start();
我正在使用android studio 2.2.2。
答案 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,可以通过
进行Activity.runOnUIThread()
Runnable
View.Post(Runnable)
AsyncTask
在您的代码中,您使用的是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。线程。
仅使用:
Activity.runOnUIThread(runnable)
View.Post(Runnable)
,view.postDelayed()
AsyncTask
重要的问题是为什么您的代码正在修改UI?
可能是,您从Thread
开始onCreate()
。例如,如果您尝试在Button
onClick()
内运行此主题,则您的应用会崩溃
CalledFromWrongThreadException