前言:身为Android程序员,多思考才有自主的思考体系,才能逐渐成为架轻就熟的架构设计师。从今天开始,让高老师带您一起来动动脑,思考Android架构,探索许多程序员忽略的部分,却是晋升架构师必要的关键知识点。例如,本文的主题:Android UI<单线程程序>概念,就是许多程序员,没有深入思考过的议题,一直在脑海里模糊不清,就很难成为称职的架构师了,因为这是系统稳定的必要环节。


1. Android UI<单线程程序>概念

----单线程程序意谓着两个(或多个)线程不能共享对象或变量值。AndroidUI是单线程程序的环境。UI控件(Button)都是由UI线程所创建,内部攸关于UI显示的属性或变量都只有UI线程才能存取(Access)之,别的线程并不能去存取之。例如下图里的View类别体系,都只限于UI线程才能去执行它们的onDraw()函数,因为它会实际更动到UI的属性。


思考Android架構(一):What & Why《Android的UI控件是单线程》?-LMLPHP


例如,请看一个Android代码:


public class myActivity extends Activity


implements OnClickListener {

private Button ibtn;

@Override protected void onCreate(Bundle icicle) {

              super.onCreate(icicle);

              ibtn = new Button(this);

              //…………….

      }

     // 其它函数

   }


由于UI线程来执行onCreate()函数,诞生了Button对象,因而只限UI线程能去存取该对象里攸关UI的属性,其它线程不能去碰它们。


2. 单线程可避开线程安全问题


    线程安全问题就是如何避免不同线程之间,可能会相互干扰的问题。虽然两个线程几乎同时先后执行一个类别里的(可能不同)函数,只要不共享对象、或共享变量(例如AndroidUI单线程环境),就不会产生干扰现象,也就没有线程安全问题。换句话说,如果各自使用自己的对象或变量(即不共享对象或变量),就不会干扰到别线程执行的正确性了。例如下述范例:


class Task2{

    private int count;

    public void init(){  count =0;  }

    public void f1() {

           for(inti=0; i

                    count++;

           try {

               Thread.sleep(10);

             } catch(InterruptedException e) {e.printStackTrace();  }

                   System.out.println(Thread.currentThread().getName()+

                           "'s count: " + count);

   }}

}

class Task implements Runnable {

public void run() {

Task2 ta2 = new Task2();

 ta2.init();   ta2.f1();

}}


public class JMain {

public static voidmain(String[] args) {

   Task ta = new Task();

   Thread t1 = new Thread( ta, "A");

   Thread t2 = new Thread( ta, "B");

   t1.start();

   t2.start();

   System.out.println("Waiting...");

}}


这里,t1t2线程共享主线程所诞生的ta对象,但是各自诞生了Task2类别之对象。两者各自使用自己的对象(即不共享对象或变量),就不会干扰到别线程的数据。所以输出正确的结果:

Waiting...

           B's count: 1

           A's count: 1

           B's count: 2

           A's count: 2

           B's count: 3

           A's count: 3


3.  SurfaceView与非UI线程


View控件是由UI 线程(主线程)所执行。如果需要去迅速更新UI画面或者UI画图需要较长时间(避免阻塞主线程),就使用SurfaceView。它可以由背景线程(background thead)来执行,而View只能由UI()线程执行画面显示或更新。


思考Android架構(一):What & Why《Android的UI控件是单线程》?-LMLPHP


SurfaceView里,非UI线程可以去碰触UI显示,例如将图形绘制于Surface画布上。这SurfaceView内含高效率的rendering机制,能让背景线程快速更新surface的内容,适合演示动画(animation)

思考Android架構(一):What & Why《Android的UI控件是单线程》?-LMLPHP


4. 线程安全的化解之例

运用的单线程环境>概念。View是一个单线程的类;其意味着:此类的开发者心中意图只让有一个线程来执行这个类的代码(如函数调用)

思考Android架構(一):What & Why《Android的UI控件是单线程》?-LMLPHP

App开发者撰写子类时,覆写onDraw()时,是被UI线程调用的。UI线程执行子类的onDraw()时,可能会调用基类的函数而间接使用到基类的数据。为了确保满足基类开发者的预设前题(即单线程),子类的开发者,就不宜使用小线程去调用基类的函数了。

思考Android架構(一):What & Why《Android的UI控件是单线程》?-LMLPHP


然而,不宜使用小线程去调用基类的函数,并不意味着,不能用小线程去执行子类的函数。只要小线程不去调用基类函数就行了。例如:

思考Android架構(一):What & Why《Android的UI控件是单线程》?-LMLPHP


这是可以的,因为小线程并没有去调用基类函数,不会有共享基类数据之虞。反之,下图就违反单线程程序>的规则了:


思考Android架構(一):What & Why《Android的UI控件是单线程》?-LMLPHP


上图是小线程直接存取(Access)基类数据,有可能产生线程冲突。下图里,因调用基类函数而间接存取基类数据,也有可能产生线程冲突。所以都是违规的。

思考Android架構(一):What & Why《Android的UI控件是单线程》?-LMLPHP


因此,由于View类开发者心怀<单线程>,则View类的Client开发者就不宜让多线程去执行View的代码。


~ END ~

ee                                                                             ee

【思考Android技术】

請參考: Android从程序员到架构师之路课程(在线视频学习)

请进入:ADT架构师学苑


12-22 16:56