Android UI线程和非UI线程

UI线程及Android的单线程模型原则

  当应用启动,系统会建立一个主线程(main thread)html

  这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components from the Android UI toolkit (components from the android.widget and android.view packages))发生交互。java

  因此main thread也叫UI thread也即UI线程android

 

  系统不会为每一个组件单首创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每个组件的调用都从UI线程分发出去数据库

  结果就是,响应系统回调的方法(好比响应用户动做的onKeyDown()和各类生命周期回调)永远都是在UI线程里运行。安全

 

  当App作一些比较重(intensive)的工做的时候,除非你合理地实现,不然单线程模型的performance会很poor。网络

  特别的是,若是全部的工做都在UI线程,作一些比较耗时的工做好比访问网络或者数据库查询,都会阻塞UI线程致使事件中止分发(包括绘制事件)。对于用户来讲,应用看起来像是卡住了,更坏的状况是,若是UI线程blocked的时间太长(大约超过5秒),用户就会看到ANRapplication not responding)的对话框。app

  另外,Andoid UI toolkit并非线程安全的,因此你不能从非UI线程来操纵UI组件。你必须把全部的UI操做放在UI线程里,因此Android的单线程模型有两条原则:ide

  1.不要阻塞UI线程。oop

  2.不要在UI线程以外访问Android UI toolkit(主要是这两个包中的组件:android.widget and android.view)。post

 

使用Worker线程

  根据单线程模型的两条原则,首先,要保证应用的响应性,不能阻塞UI线程,因此当你的操做不是即时的那种(not instantaneous),你应该把他们放进单另的线程中(叫作background或者叫worker线程)。

  好比点击按钮后,下载一个图片而后在ImageView中展现:

复制代码
public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}
复制代码

 

  这段代码用新的线程来处理网络操做,可是它违反了第二条原则:

  Do not access the Android UI toolkit from outside the UI thread.

  从非UI线程访问UI组件会致使未定义和不能预料的行为

 

  为了解决这个问题,Android提供了一些方法,从其余线程访问UI线程:

 

  好比,上面这段代码能够这么改:

复制代码
public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}
复制代码

 

  这么改以后就是线程安全的了。

  可是,当操做变得复杂的时候,这种代码会变得很是复杂,为了处理非UI线程和UI线程之间更加复杂的交互,能够考虑在worker线程中使用一个Handler,来处理UI线程中传来的消息。

  也能够继承这个类AsyncTask 。

 

Communicating with the UI Thread

  只有在UI线程中的对象才能操做UI线程中的对象,为了将非UI线程中的数据传送到UI线程,可使用一个 Handler运行在UI线程中。

  Handler是Android framework中管理线程的部分,一个Handler对象负责接收消息而后处理消息。

  你能够为一个新的线程建立一个Handler,也能够建立一个Handler而后将它和已有线程链接。

  若是你将一个Handler和你的UI线程链接,处理消息的代码就将会在UI线程中执行。

 

  能够在你建立线程池的类的构造方法中实例化Handler的对象,而后用全局变量存储这个对象。

  要和UI线程链接,实例化Handler的时候应该使用Handler(Looper) 这个构造方法。

  这个构造方法使用了一个 Looper 对象,这是Android系统中线程管理的framework的另外一个部分。

  当你用一个特定的 Looper实例来建立一个 Handler时,这个 Handler就运行在这个 Looper的线程中。

 

  在Handler中,要覆写handleMessage() 方法。Android系统会在Handler管理的相应线程收到新消息时调用这个方法

  一个特定线程的全部Handler对象都会收到一样的方法。(这是一个“一对多”的关系)。

 

参考资料

  官方Training: 与UI线程通讯:

  http://developer.android.com/training/multiple-threads/communicate-ui.html

  Guides: Processes and Threads

  http://developer.android.com/guide/components/processes-and-threads.html

 

  类参考:

  http://developer.android.com/reference/android/os/Looper.html

  http://developer.android.com/reference/android/os/Handler.html

  http://developer.android.com/reference/android/os/HandlerThread.html

 

  博客:

  Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等:

  http://www.cnblogs.com/playing/archive/2011/03/24/1993583.html