Android中使用线程Thread的方法和Java SE相同。和大多数OS系统一样,Android中也有称为UI Thread的主线程。UI Thread 主要用来给相应的Widget分发消息,包括绘制(Drawing)事件。UI Thread 也是用来处理用户交互事件的线程。比如:如果你按下屏幕上某个按钮,UI 线程则将Touch 事件通知对应的控件(Widgets),Widget 则将其状态设置成“按下”,并把“重绘”(Invalidate)事件发到Event Queue中去。 UI线程从Event Queue中读取事件后通知Widgets重画自身。
如果你的应用设计不好的话, UI线程的这种单线程模式就会导致非常差的用户响应性能。特别是你将一些费时的操作如网络访问或数据库访问也放在UI线程中,这些操作会造成用户界面无反应,最糟糕的是,如果UI线程阻塞超过几秒(5秒),著名的ANR对话框就会出现:
所以在设计应用时,需要把一些费时的任务使用单独的工作线程来运行避免阻塞UI线程,但是如果在工作线程中想更新UI线程的话,不能直接在工作线程 中更新UI,这是因为UI线程不是“Thread Safe”。因此所有UI相关的操作一般必须在UI Thread中进行。
Android OS提供了多种方法可以用在非UI线程访问UI线程。
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
- Handler
Bezier 示例动态显示Bezier曲线,使用了Activity.runOnUiThread 来更新屏幕,完整代码如下:
- 1 public class Bezier extends Graphics2DActivity
- 2 implements OnClickListener,Runnable{
- 3
- 4 /**
- 5 * The animation thread.
- 6 */
- 7 private Thread thread;
- 8 private volatile boolean stopThread=false;
- 9 private boolean stopOrNot=false;
- 10 boolean drawn;
- 11 /**
- 12 * The random number generator.
- 13 */
- 14 static java.util.Random random = new java.util.Random();
- 15 /**
- 16 * The animated path
- 17 */
- 18 Path path = new Path();
- 19 /**
- 20 * Red brush used to fill the path.
- 21 */
- 22 SolidBrush brush = new SolidBrush(Color.RED);
- 23 private static final int NUMPTS = 6;
- 24 private int animpts[] = new int[NUMPTS * 2];
- 25 private int deltas[] = new int[NUMPTS * 2];
- 26 long startt, endt;
- 27
- 28 private Button btnOptions;
- 29 @Override
- 30 protected void drawImage() {
- 31 drawDemo(100, 100);
- 32
- 33 }
- 34
- 35 public void onCreate(Bundle savedInstanceState) {
- 36 super.onCreate(savedInstanceState);
- 37 setContentView(R.layout.beziers);
- 38 graphic2dView
- 39 = (GuidebeeGraphics2DView) findViewById(R.id.graphics2dview);
- 40 btnOptions = (Button) findViewById(R.id.btnStopStart);
- 41 btnOptions.setOnClickListener(this);
- 42 reset(100,100);
- 43 if (thread == null) {
- 44 thread = new Thread(this);
- 45 thread.start();
- 46 }
- 47
- 48 }
- 49
- 50 @Override
- 51 public void onClick(View view) {
- 52
- 53 if(!stopOrNot){
- 54 btnOptions.setText("Start");
- 55 stopThread=true;
- 56 }
- 57 else{
- 58 stopThread=false;
- 59 btnOptions.setText("Stop");
- 60 if (thread == null) {
- 61 thread = new Thread(this);
- 62 thread.start();
- 63 }
- 64 }
- 65 stopOrNot=!stopOrNot;
- 66
- 67 }
- 68 /**
- 69 * Generates new points for the path.
- 70 */
- 71 private void animate(int[] pts, int[] deltas,
- 72 int i, int limit) {
- 73 int newpt = pts[i] + deltas[i];
- 74 if (newpt <= 0) {
- 75 newpt = -newpt;
- 76 deltas[i] = (random.nextInt() & 0x00000003)
- 77 + 2;
- 78 } else if (newpt >= limit) {
- 79 newpt = 2 * limit - newpt;
- 80 deltas[i] = -((random.nextInt() & 0x00000003)
- 81 + 2);
- 82 }
- 83 pts[i] = newpt;
- 84 }
- 85
- 86 /**
- 87 * Resets the animation data.
- 88 */
- 89 private void reset(int w, int h) {
- 90 for (int i = 0; i < animpts.length; i += 2) {
- 91 animpts[i + 0]
- 92 = (random.nextInt() & 0x00000003)
- 93 * w / 2;
- 94 animpts[i + 1]
- 95 = (random.nextInt() & 0x00000003)
- 96 * h / 2;
- 97 deltas[i + 0]
- 98 = (random.nextInt() & 0x00000003)
- 99 * 6 + 4;
- 100 deltas[i + 1]
- 101 = (random.nextInt() & 0x00000003)
- 102 * 6 + 4;
- 103 if (animpts[i + 0] > w / 2) {
- 104 deltas[i + 0] = -deltas[i + 0];
- 105 }
- 106 if (animpts[i + 1] > h / 2) {
- 107 deltas[i + 1] = -deltas[i + 1];
- 108 }
- 109 }
- 110 }
- 111
- 112 final Runnable updateCanvas = new Runnable() {
- 113 public void run() {
- 114 int offsetX = (graphic2dView.getWidth() -
- 115 SharedGraphics2DInstance.CANVAS_WIDTH) / 2;
- 116 int offsetY = (graphic2dView.getHeight()
- 117 - SharedGraphics2DInstance.CANVAS_HEIGHT) / 2;
- 118 graphic2dView.invalidate(offsetX,offsetY,
- 119 offsetX+100,offsetY+100);
- 120 }
- 121 };
- 122 /**
- 123 * Sets the points of the path and draws and fills the path.
- 124 */
- 125 private void drawDemo(int w, int h) {
- 126 for (int i = 0; i < animpts.length; i += 2) {
- 127 animate(animpts, deltas, i + 0, w);
- 128 animate(animpts, deltas, i + 1, h);
- 129 }
- 130 //Generates the new pata data.
- 131 path.reset();
- 132 int[] ctrlpts = animpts;
- 133 int len = ctrlpts.length;
- 134 int prevx = ctrlpts[len - 2];
- 135 int prevy = ctrlpts[len - 1];
- 136 int curx = ctrlpts[0];
- 137 int cury = ctrlpts[1];
- 138 int midx = (curx + prevx) / 2;
- 139 int midy = (cury + prevy) / 2;
- 140 path.moveTo(midx, midy);
- 141 for (int i = 2; i <= ctrlpts.length; i += 2) {
- 142 int x1 = (curx + midx) / 2;
- 143 int y1 = (cury + midy) / 2;
- 144 prevx = curx;
- 145 prevy = cury;
- 146 if (i < ctrlpts.length) {
- 147 curx = ctrlpts[i + 0];
- 148 cury = ctrlpts[i + 1];
- 149 } else {
- 150 curx = ctrlpts[0];
- 151 cury = ctrlpts[1];
- 152 }
- 153 midx = (curx + prevx) / 2;
- 154 midy = (cury + prevy) / 2;
- 155 int x2 = (prevx + midx) / 2;
- 156 int y2 = (prevy + midy) / 2;
- 157 path.curveTo(x1, y1, x2, y2, midx, midy);
- 158 }
- 159 path.closePath();
- 160 // clear the clipRect area before production
- 161
- 162 graphics2D.clear(Color.WHITE);
- 163 graphics2D.fill(brush, path);
- 164
- 165 this.runOnUiThread(updateCanvas);
- 166 }
- 167
- 168
- 169
- 170 public void run() {
- 171 Thread me = Thread.currentThread();
- 172
- 173 if (!drawn) {
- 174 synchronized (this) {
- 175 graphics2D.clear(Color.WHITE);
- 176 graphics2D.fill(brush, path);
- 177 graphic2dView.refreshCanvas();
- 178 drawn = true;
- 179 }
- 180 }
- 181 while (thread == me && !stopThread) {
- 182 drawDemo(100,100);
- 183 }
- 184 thread = null;
- 185 }
- 186 }
除了上述的方法外,Android还提供了AsyncTask类以简化工作线程与UI线程之间的通信。这里不详述。此外,上面Bezier曲线动画在屏幕上显示时有闪烁的现象,这是动态显示图像的一个常见问题,后面将专门讨论。