如果从 activity 生命周期方法内部启动,线程能够更改 ui。为什么?
Thread is able to change ui if started from inside activity lifecycle methods. why?
我正在测试此代码以检查应用程序是否因从后台线程更改 ui 组件而崩溃。但它没有。
这里在下面添加了代码。我在 MainActivity
的 onCreate()
方法中启动了一个新线程,它应该按照 android docs 说的
崩溃了
In the class, the Runnable.run() method contains the code that's
executed. Usually, anything is allowable in a Runnable. Remember,
though, that the Runnable won't be running on the UI thread, so it
can't directly modify UI objects such as View objects.
所以我以为它会崩溃。它没有。见代码-
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
thread.start();
}
}
而如果我在从 onClicklistener()
启动线程时尝试更改 ui,如下所示,它确实会崩溃。这是预期的。
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
}
};
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
thread.start();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.setOnClickListener(listener);
}
}
现在,第二个代码片段崩溃了,这是预料之中的,而第一个代码片段没有。
请解释为什么会这样,因为我每次都在不同的地方创建一个新的工作线程。官方文档参考将不胜感激。
UI不是thread-safe see processes-and-threads,所以你很幸运没有击中等待你的众多地雷中的一个。
如果你不喜欢靠运气那么:
你应该使用:
runOnUiThread(runnable);
而不是:
thread = new Thread(runnable);
AsyncTask 可以正确和轻松地使用 UI 线程。
正如@krish 在对我的问题的评论中指出的那样,我找到了这种行为背后的原因。原因是,线程只能在 TextView
对象中进行更改,直到它在 UI 屏幕上不可见,即不呈现。只有在视图渲染之后,除主线程外的任何线程都不能对任何 UI 组件进行更改。我尝试使用视图观察器来查看视图是否在更改之前呈现。这表明在视图渲染之前进行了更改。
这是我试过的代码。
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
Log.d("ThreadTest", "The Text was changed.");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Log.d("ThreadTest", "The TextView was rendered");
}
});
}
@Override
protected void onResume() {
super.onResume();
thread.start();
}
}
使用上面的代码。您将在输出中看到:
The Text was changed.
The TextView was rendered
这意味着文本在视图呈现之前已更改。如果您尝试启动线程以更改 onGlobalLayout 方法。应用程序崩溃。
我正在测试此代码以检查应用程序是否因从后台线程更改 ui 组件而崩溃。但它没有。
这里在下面添加了代码。我在 MainActivity
的 onCreate()
方法中启动了一个新线程,它应该按照 android docs 说的
In the class, the Runnable.run() method contains the code that's executed. Usually, anything is allowable in a Runnable. Remember, though, that the Runnable won't be running on the UI thread, so it can't directly modify UI objects such as View objects.
所以我以为它会崩溃。它没有。见代码-
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
thread.start();
}
}
而如果我在从 onClicklistener()
启动线程时尝试更改 ui,如下所示,它确实会崩溃。这是预期的。
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
}
};
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
thread.start();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.setOnClickListener(listener);
}
}
现在,第二个代码片段崩溃了,这是预料之中的,而第一个代码片段没有。
请解释为什么会这样,因为我每次都在不同的地方创建一个新的工作线程。官方文档参考将不胜感激。
UI不是thread-safe see processes-and-threads,所以你很幸运没有击中等待你的众多地雷中的一个。
如果你不喜欢靠运气那么:
你应该使用:
runOnUiThread(runnable);
而不是:
thread = new Thread(runnable);
AsyncTask 可以正确和轻松地使用 UI 线程。
正如@krish 在对我的问题的评论中指出的那样,我找到了这种行为背后的原因。原因是,线程只能在 TextView
对象中进行更改,直到它在 UI 屏幕上不可见,即不呈现。只有在视图渲染之后,除主线程外的任何线程都不能对任何 UI 组件进行更改。我尝试使用视图观察器来查看视图是否在更改之前呈现。这表明在视图渲染之前进行了更改。
这是我试过的代码。
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
Log.d("ThreadTest", "The Text was changed.");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Log.d("ThreadTest", "The TextView was rendered");
}
});
}
@Override
protected void onResume() {
super.onResume();
thread.start();
}
}
使用上面的代码。您将在输出中看到:
The Text was changed.
The TextView was rendered
这意味着文本在视图呈现之前已更改。如果您尝试启动线程以更改 onGlobalLayout 方法。应用程序崩溃。