Android UI线程和非UI线程

2016-07-16  本文已影响1012人  MrMagicWang

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

当应用启动,系统会创建一个主线程
  这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件发生交互。
  所以主线程也叫UI线程

系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都是从UI线程分发出去的。结果就是,响应系统回调的方法永远都是在UI线程里进行的

当App在做一些比较繁重的操作的时候,比如网络请求、数据库操作等相关操作。如果这些工作都在UI线程里进行,就会阻塞UI线程,导致时间停止分发。对于用户来说,应用看起来就像卡住了,更坏的情况是,如果UI线程阻塞时间过长,用户就会看到应用无响应提示。

另外,Android UI toolkit并不是线程安全的,所以你不能从非UI线程来操作UI组件。必须把所有的UI操作放在UI线程里。

所以android单线程模型有两条原则
  1. 不要阻塞UI线程。
  2. 不要再UI线程外操作UI组件。

使用worker线程

根据单线程模型的两条原则,首先,要保证应用的响应性,不能阻塞UI线程,所以当你的操作十分耗时,你应该把他们放进另外的单独线程中(叫做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();
}

这段代码用新的线程来处理网络操作,但是它违反了第二条原则:不能从非UI线程访问UI组件。

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

public final void runOnUiThread(Runnable action) {
    if(Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

利用Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程。

new Thread(new Runnable() {
      @Override
      public void run() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show();
                }
             });
 
      }
}).start();

在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。

由于不是在新的线程中使用,所以千万别做复杂的计算逻辑。
                              参考自清沁

public void onClick(View v) {
   new Thread(new Runnable() {
     public void run() {
       final Bitmap b = loadImageFromNetwork();
       mImageView.post(new Runnable() {
         public void run() {
           mImageView.setImageBitmap(b);
         }
       });
     }
   }).start();
}

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

上一篇 下一篇

猜你喜欢

热点阅读