本文主要是介绍面试时,很多面试官都喜欢采用这种方法,来考察求职者的能力,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
面试时,很多面试官都喜欢采用这种方法,来考察求职者的能力,基础是否扎实。(循环渐进法)这个知识点,80%的求职者很容易陷入面试官精心设计的陷阱这篇文章就是关于Activity生命周期的一个陷阱,就是一个很好的例子。
今天我们来聊一聊另一个话题:非UI主线程能不能操作UI?
进入话题前,先来看一下这段代码,这段代码的大意是尝试在Thread中更新一个Button的文字。或许你听了会很惊讶,在Thread中更新一个Button的状态,这怎么可能?不急,我们先来看看。
1 public class MainActivity extends AppCompatActivity {
2 private Button btn;
3 @Override
4 protected void onCreate(Bundle savedInstanceState) {
5 super.onCreate(savedInstanceState);
6 setContentView(R.layout.activity_main);
7 initView();
8}
9private void initView() {
10 btn = (Button) findViewById(R.id.bt_start);
11 btn.setOnClickListener(this);
12 newThead();
13}
14private void newThead() {
15 new Thread() {
16 @Override
17 public void run() {
18 super.run();
19 btn.setText("update");
20 }
21 }.start();
22 }
23}
编译运行了,可以正常更改按钮的文字。这是为什么,开发文档不是明确地指出吗?
Do not access the Android UI toolkit from outside the UI thread
确实,把代码稍微加上这么一句
1 private void newThead() {
2 new Thread() {
3 @Override
4 public void run() {
5 super.run();
6 try {
7 Thread.sleep(1000);
8 } catch (InterruptedException e) {
9 e.printStackTrace();
10 }
11 btn.setText("update");
12 }
13 }.start();
14 }
15}
编译运行,出现
1 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
2 at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
3 at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
4 at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081)
5 at android.view.View.invalidateInternal(View.java:12713)
6 at android.view.View.invalidate(View.java:12677)
7 at android.view.View.invalidate(View.java:12661)
8 at android.widget.TextView.checkForRelayout(TextView.java:7159)
9 at android.widget.TextView.setText(TextView.java:4342)
10 at android.widget.TextView.setText(TextView.java:4199)
11 at android.widget.TextView.setText(TextView.java:4174)
12 at com.aikaifa.splashview.activity.MainActivity$1$override.run(MainActivity.java:59)
13 at com.aikaifa.splashview.activity.MainActivity$1$override.access$dispatch(MainActivity.java)
14 at com.aikaifa.splashview.activity.MainActivity$1.run(MainActivity.java:0)
下面,我们再把代码稍微改一下,增加一个onClick事件看看:
1 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
2 private Button btn;
3 @Override
4 protected void onCreate(Bundle savedInstanceState) {
5 super.onCreate(savedInstanceState);
6 setContentView(R.layout.activity_main);
7 initView();
8}
9private void initView() {
10 btn = (Button) findViewById(R.id.bt_start);
11 btn.setOnClickListener(this);
12}
13@Override
14public void onClick(View view) {
15switch (view.getId()) {
16 case R.id.bt_start:
17 newThead();
18 break;
19 }
20}
21private void newThead() {
22 new Thread() {
23 @Override
24 public void run() {
25 super.run();
26 btn.setText("update");
27 }
28 }.start();
29 }
30}
等程序运行完毕,我们点击一下按钮,在onClick的时候crash了。这是什么情况,在onCreate的时候执行不会crash,在按钮下执行就crash了。
我们来看一下错误日志:
日志说不能在非UI线程中去更新UI控件。我们跟进源码中去查找一下ViewRootImpl.java这个文件。看看怎么一回事,找到这个异常:
1void checkThread() {
2 if (mThread != Thread.currentThread()) {
3 throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
5 }
6}
判断更新UI控件的时候是否在UI线程中进行,如果不是的话就会crash。也就是onCreate的时候这个判断并没有被执行到,所以onCreate中启动的Thread并不会导致程序的crash。
而在按钮点击下,这个判断执行到了。
虽然第一种情况不会出现crash异常,但建议大家不要这样做,这样很容易导致UI阻塞,如果非要更改,我们可以借助Activity.runOnUiThread,View.Post来实现,这也是google推荐的。
1 /*
2 * IMPLEMENTATION
3 */
4 private void checkThread() {
5 if (started && !isFxApplicationThread()) {
6 throw new IllegalStateException("Task must only be used from the FX Application Thread");
7 }
8}
1public class MainActivity extends AppCompatActivity implements View.OnClickListener {
2 private Button btn;
3 @Override
4 protected void onCreate(Bundle savedInstanceState) {
5super.onCreate(savedInstanceState);
6setContentView(R.layout.activity_main);
7initView();
8 }
9 private void initView() {;
10btn = (Button) findViewById(R.id.bt_start);
11btn.setOnClickListener(this);
12}
13@Override
14public void onClick(View view) {
15switch (view.getId()) {
16 case R.id.bt_start:
17 newThead();
18 break;
19 }
20 }
21 private void newThead() {
22 new Thread() {
23 @Override
24 public void run() {
25 super.run();
26 Looper.prepare();
27 addView();
28 Looper.loop();
29 }
30 }.start();
31}
32private void addView(){
33TextView tx = new TextView(MainActivity.this);
34tx.setText("update");
35tx.setBackgroundColor(ContextCompat.getColor(this,R.color.colorPrimary));
36tx.setGravity(Gravity.CENTER);
37WindowManager wm = MainActivity.this.getWindowManager();
38WindowManager.LayoutParams params = new WindowManager.LayoutParams(
39 200, 200, 200, 200, WindowManager.LayoutParams.FIRST_SUB_WINDOW,
40 WindowManager.LayoutParams.TYPE_TOAST, PixelFormat.OPAQUE);
41wm.addView(tx, params);
42 }
43}
每次刷新UI的时候,会用ViewRootImpl.checkThread()来检验线程是否是View的创建线程。
[END]
今天就分享到此,希望对你有帮助,感谢阅读,感谢有你。
阅读推荐:
从事程序员的8大好处,怪不得很多女孩子都喜欢嫁给程序员
这篇关于面试时,很多面试官都喜欢采用这种方法,来考察求职者的能力的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!