理解UI线程——swt, Android, 和Swing的UI机理线程在做GUI的时候, 无论是SWT, AWT, Swing 还是Android, 都需要面对UI线程的问题, UI线程往往会被单独的提出来单独对待, 试着问自己,当GUI启动的时候, 后台会运行几个线程? 比如1. SWT 从Main函数启动2. Swing 从Main函数启动3. Android 界面启动常常我们被告知, 主线程, UI线程, 因此这里很多会回答, 有两个线程, 一个线程是Main, 另外一个是UI. 如果答案是这样, 这篇文章就是写给你的。
OK, 我们以SWT为例, 设计以下方案寻找答案, 第一步, 我们看能否找到两个线程:1. 从Main中启动SWT的界面, 在启动界面前, 将Main所在的线程打印出来这里设计为Shell中嵌入一个Button2. 点击Button, 运行一个耗时很长的操作, 反复修改Button的文字, 在该线程中打印该线程的名称代码是这样的:1.publicstaticvoid main(String[] args) {2.final Display display = Display.getDefault();3.final Shell shell = new Shell();4. shell.setSize(500, 375);5. shell.setText("SWT Application");6. shell.setLayout(new FillLayout());7. btn = new Button(shell, SWT.NULL);8. btn.setText("shit");9. registerAction();10. shell.open();11. yout();12.while (!shell.isDisposed()) {13.if (!display.readAndDispatch())14. display.sleep();15. }16. shell.dispose();17. display.dispose();18.}19.privatestaticvoid registerAction() {20. btn.addMouseListener(new MouseListener() {21. @Override22.publicvoid mouseDoubleClick(MouseEvent e) {23. // TODO Auto-generated method stub24. }25. @Override26.publicvoid mouseDown(MouseEvent e) {27. methodA();28. }29. @Override30.publicvoid mouseUp(MouseEvent e) {31. }32. });33.}34./**35.* 持续的跑动, 打印线程的名称, 注意拖拽不动, 界面死掉, 直到跑完36.*/37.privatestaticvoid methodA() {38.for (int i = 0; i < count; i++) {39. haveArest(300);40. System.out.println("MethodA:" +Thread.currentThread().getName());41. btn.setText(i + "");42. }43.}haveArest方法在最后出现, 只是封装了一个让线程等待一段时间, 打印的结果都为main, 于是得到第一个重要的结论:UI所在的线程和Main所在的线程都是同一个线程。
再来推断一把:UI在哪个线程启动的, 则这个线程就是UI线程.1./**2.* @param args3.*/4.publicstaticvoid main(String[] args) {5. // TODO Auto-generated method stub6.7. Thread t = new Thread(new Runnable() {8. @Override9.publicvoid run() {10. createUI();11. }12. });13. t.start();14.}15.16.privatestaticvoid createUI()17.{18. System.out.println(Thread.currentThread().getName());19.final Display display = Display.getDefault();20.final Shell shell = new Shell();21. shell.setSize(500, 375);22. shell.setText("SWT Application");23. shell.setLayout(new FillLayout());24. Button btn = new Button(shell, SWT.NULL);25. btn.setText("shit");26. shell.open();27. yout();28.while (!shell.isDisposed()) {29.if (!display.readAndDispatch())30. display.sleep();31. }32. shell.dispose();33. display.dispose();34.}通过打印结果发现, 推论是正确的.根据铺天盖地参考书提示, 有这样一条定律:只可以存在一个UI线程验证一下, 我们的验证方式是创建两个UI线程:1./**2.* @param args3.*/4.publicstaticvoid main(String[] args) {5. // TODO Auto-generated method stub6.7. Thread t = new Thread(new Runnable() {8. @Override9.publicvoid run() {10. createUI();11. }12. });13. t.start();14.15. t = new Thread(new Runnable() {16. @Override17.publicvoid run() {18. createUI();19. }20. });21. t.start();22.23.24.}25.26.privatestaticvoid createUI()27.{28. System.out.println(Thread.currentThread().getName());29.final Display display = new Display();30.final Shell shell = new Shell();31. shell.setSize(500, 375);32. shell.setText("SWT Application");33. shell.setLayout(new FillLayout());34. Button btn = new Button(shell, SWT.NULL);35. btn.setText("shit");36. shell.open();37. yout();38.while (!shell.isDisposed()) {39.if (!display.readAndDispatch())40. display.sleep();41. }42. shell.dispose();43. display.dispose();44.}但这里确实创建了两个线程。
看来一个进程是可以创建两个线程的。
可以存在一个或者多个UI线程, 下次看到参考书这么写的时候, 可以BS它了。
之前犯了一个错误就是用Diplay display = Display.getDefault(); 这样得到的是前一个线程创建的Display,故不能创建. 造成只能创建一个UI线程的错觉当然我们的研究不能到此为止, 我们需要探究一下, 为什么总是被告知更新UI的动作要放在UI线程中?回到第一个例子中, 即:1.publicstaticvoid main(String[] args) {2.final Display display = Display.getDefault();3.final Shell shell = new Shell();4. shell.setSize(500, 375);5. shell.setText("SWT Application");6. shell.setLayout(new FillLayout());7. btn = new Button(shell, SWT.NULL);8. btn.setText("shit");9. registerAction();10. shell.open();11. yout();12.while (!shell.isDisposed()) {13.if (!display.readAndDispatch())14. display.sleep();15. }16. shell.dispose();17. display.dispose();18.}19.privatestaticvoid registerAction() {20. btn.addMouseListener(new MouseListener() {21. @Override22.publicvoid mouseDoubleClick(MouseEvent e) {23. // TODO Auto-generated method stub24. }25. @Override26.publicvoid mouseDown(MouseEvent e) {27. methodA();28. }29. @Override30.publicvoid mouseUp(MouseEvent e) {31. }32. });33.}34./**35.* 持续的跑动, 打印线程的名称, 注意拖拽不动, 界面死掉, 直到跑完36.*/37.privatestaticvoid methodA() {38.for (int i = 0; i < count; i++) {39. haveArest(300);40. System.out.println("MethodA:" +Thread.currentThread().getName());41. btn.setText(i + "");42. }43.}运行的时候拖动试试, 发现不动, 直到for循环中修改btn的操作完成.这里我们不难明白一个观点:同一个线程的情况下, 一个操作(拖动), 是需要等待另外一个操作(更新btn)完成后, 才可以进行的。