Android UI线程和非UI线程

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线程:

  • Activity.runOnUiThread(Runnable)
    这个函数是Activity的成员函数,它的源码为:
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();
  • View.post(Runnable)

在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();
}
  • View.postDelayed(Runnable, long)

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,845评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,930评论 25 708
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,742评论 18 399
  • 这个周末大部分时间在路上,昨天凌晨三点回到湛江,今天深夜刚刚回到深圳,上演了一部现实版“人在旅途”。 虽然是一段短...
    小牛妈妈日记星球阅读 212评论 0 2
  • 无意当中打开了手机上的收音机功能,还想着或许能够听到FM101.1江苏交通广播网。 吭哧吭哧在手机上捯饬半天。才突...
    東門聖手阅读 321评论 1 3