当应用程序启动,创建了一个叫“main”的线程,用于管理UI相关,又叫UI线程。其他线程叫工作线程(Work Thread)。

Single Thread Model

  • 一个组件的创建并不会新建一个线程,他们的创建都在UI线程中进行,包括他们的回调方法,如onKeyDown()。
  • 当在UI线程中进行某些耗时的操作时,将会阻塞UI线程,一般阻塞超过5秒就会显示一个ANR对话框。
  • UI线程是非线程安全的,所以,不能在工作线程中操作UI元素。

    两个原则

    • Do not block the UI thread (不要阻塞UI线程)
    • Do not access the Android UI toolkit from outside the UI thread (不要在工作线程中操作UI元素)

    在工作线程更新UI方法

    • Activity.runOnUiThread(Runnable)
    • Handler
      • sendMessage(Message)
      • post(Runnable)
    • AsyncTask

      • execute()
      • doInBackground()
      • onPostExecute()

    例子程序

    • HandlerActivity01

      • 在工作线程中进行UI操作。
    • HandlerActivity02

      • Handler的两个重要方法:sendMessage和post。
    • HandlerActivity03

      • 官方推荐最佳方法。

    HandlerActivity01主要代码:

    Java代码   
    1. btnEnd.setOnClickListener(new View.OnClickListener() {
    2. @Override
    3. public void onClick(View v) {
    4. new Thread(new Runnable() {
    5. @Override
    6. public void run()
    7. {
    8. //在新建的线程(工作线程)中改变Button的文字
    9. btnEnd.setText("Text Changed in Sub Thread");
    10. }
    11. }).start();
    12. }
    13. });

    这是一种错误的做法,运行程序,会报错误:

    Java代码   
    1. android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

    HandlerActivity02主要代码:

    Java代码   
    1. public class HandlerActivity02 extends Activity
    2. {
    3. private int title = 0;
    4. Button btnStart,btnEnd;
    5. private Handler mHandler = new Handler()
    6. {
    7. public void handleMessage(Message msg)
    8. {
    9. //更新UI
    10. switch (msg.what)
    11. {
    12. case 1:
    13. updateTitle();
    14. break;
    15. }
    16. };
    17. };
    18. public void onCreate(Bundle savedInstanceState)
    19. {
    20. super.onCreate(savedInstanceState);
    21. setContentView(R.layout.main);
    22. btnStart = (Button)findViewById(R.id.start);
    23. btnEnd = (Button)findViewById(R.id.end);
    24. //新启动一个线程,进行耗时操作
    25. Timer timer = new Timer();
    26. //每六秒执行一次MyTask的run方法
    27. timer.scheduleAtFixedRate(new MyTask(this), 1, 6000);
    28. }
    29. private class MyTask extends TimerTask
    30. {
    31. private Activity context;
    32. MyTask(Activity context)
    33. {
    34. this.context = context;
    35. }
    36. @Override
    37. public void run()
    38. {
    39. //耗时操作略....
    40. //更新UI方法  1
    41. Message message = new Message();
    42. message.what = 1;
    43. mHandler.sendMessage(message);
    44. //更新UI方法  2
    45. mHandler.post(updateThread);
    46. //更新UI方法  3
    47. context.runOnUiThread(updateThread);
    48. }
    49. }
    50. public void updateTitle()
    51. {
    52. setTitle("Welcome to Mr Wei's blog " + title);
    53. title++;
    54. }
    55. Runnable updateThread = new Runnable()
    56. {
    57. @Override
    58. public void run()
    59. {
    60. //更新UI
    61. btnStart.setText(String.valueOf(title));
    62. btnEnd.setText(String.valueOf(title));
    63. }
    64. };
    65. }

    这里有个容易出错的地方,在更新UI方法2和3中,我们传入的参数是一个Runnable对象,一般认为这就会启动一个新的线程,而且常有人在这个Runnable对象的run方法中进行耗时操作。看过这块的源码就会知道,其实,android只是调用了这个Runnable对象的run方法而已,并没有启动新的线程,而且我们不应该在run方法中进行耗时操作,因为这个run方法最终是在UI线程里面执行的。也就是说,run方法里面只应该放更新UI的代码,handleMessage方法也一样。

    如果你要看这部分源代码的话,相信这个图对你会有帮助:

    HandlerActivity03主要代码:

    Java代码   
    1. public class HandlerActivity03 extends Activity
    2. {
    3. Button btnStart;
    4. @Override
    5. protected void onCreate(Bundle savedInstanceState)
    6. {
    7. // TODO Auto-generated method stub
    8. super.onCreate(savedInstanceState);
    9. setContentView(R.layout.main);
    10. btnStart = (Button)findViewById(R.id.start);
    11. btnStart.setOnClickListener(new View.OnClickListener() {
    12. @Override
    13. public void onClick(View v) {
    14. //开始执行AsyncTask,并传入某些数据
    15. new LongTimeTask().execute("New Text");
    16. }
    17. });
    18. }
    19. private class LongTimeTask extends AsyncTask
    20. {
    21. @Override
    22. protected String doInBackground(String... params)
    23. {
    24. try
    25. {
    26. //线程睡眠5秒,模拟耗时操作,这里面的内容Android系统会自动为你启动一个新的线程执行
    27. Thread.sleep(5000);
    28. }
    29. catch (InterruptedException e)
    30. {
    31. e.printStackTrace();
    32. }
    33. return params[0];
    34. }
    35. @Override
    36. protected void onPostExecute(String result)
    37. {
    38. //更新UI的操作,这里面的内容是在UI线程里面执行的
    39. btnStart.setText(result);
    40. }
    41. }
    42. }

    这个方法确实挺好,因为它为你封装了许多操作,你只需要记住在doInBackground方法中写耗时操作的代码,在onPostExecute方法中写更新UI的方法就行了。

android中工作线程安全的更多相关文章

  1. android中的线程池学习笔记

    阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...

  2. Android多线程编程<一>Android中启动子线程的方法

          我们知道在Android中,要更新UI只能在UI主线程去更新,而不允许在子线程直接去操作UI,但是很多时候,很多耗时的工作都交给子线程去实现,当子线程执行完这些耗时的工作后,我们希望去修改 ...

  3. Android中后台线程如何与UI线程交互

    我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须 ...

  4. Android中UI线程与后台线程交互设计的6种方法

    在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须要重新开启 一个后台线程运行这些任务.然而,往往这些任务最终又会直 ...

  5. Android中UI线程与后台线程交互设计的5种方法

    我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一 些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必 ...

  6. Android中的线程池概述

    线程池 Android里面,耗时的网络操作,都会开子线程,在程序里面直接开过多的线程会消耗过多的资源,在众多的开源框架中也总能看到线程池的踪影,所以线程池是必须要会把握的一个知识点; 线程运行机制 开 ...

  7. android中开启线程

    其实Android启动线程和JAVA一样有两种方式,一种是直接Thread类的start方法,也就是一般写一个自己的类来继承Thread类.另外一种方式其实和这个差不多啊! 那就是Runnable接口 ...

  8. android中对线程池的理解与使用

    前段时间有幸接到腾讯上海分公司的 Android开发面试,虽然最后一轮被毙了.但还是得总结一下自己在android开发中的一些盲点,最让我尴尬的是面试官问我几个android中线程池的使用与理解..哎 ...

  9. Android中的线程池

    在Android中,主线程不能执行耗时的操作,否则可能会导致ANR.那么,耗时操作应该在其它线程中执行.线程的创建和销毁都会有性能开销,创建过多的线程也会由于互相抢占系统资源而导致阻塞的现象.这个时候 ...

随机推荐

  1. 【重装系统】线上Linux服务器(2TB)分区参考方案

    如果是线上服务器,假设它是 2TB 的 SATA 硬盘.8GB 内存,建议按如下方式进行分区: / 20480M(20G)(主分区) /boot 128M swap 10240M /data 2016 ...

  2. LINQ for XML简单示例

    LINQ,语言集成查询(Language Integrated Query)是一组用于c#和Visual Basic语言的扩展.它允许开发人员以与查询数据库相同的方式操作内存数据.从技术角度而言,LI ...

  3. jquery 页面加载时获取图片高度

    $(function () { $(window).load(function(){ alert($('img').height()); }); });

  4. cf595d

    题意:给出一个轮子,上面有一个随着它转动的传感器在圆周上,给出一个指定距离m,和轮子向前行进的速度v以及轮子的半径r.问让传感器通过该距离最少需要多少时间. 分析:首先我们列出传感器行进距离与时间的轮 ...

  5. centos6.3安装python2.7, pip2.7, mysql

    参考: https://github.com/h2oai/h2o-2/wiki/Installing-python-2.7-on-centos-6.3.-Follow-this-sequence-ex ...

  6. Android Studio在创建/导入项目的时候,一直处于building “XXX”gradle project info的解决办法

    Android Studio在新建项目或者导入项目的时候,可能会一直处于building “XXX”gradle project info的状态,而且还取消不了,无奈之下只能干掉进程... 还有一种情 ...

  7. C# 使用SqlBulkCopy类批量复制大数据

    用途说明: 前些日子,公司要求做一个数据导入程序,要求将Excel数据,大批量的导入到数据库中,尽量少的访问数据库,高性能的对数据库进行存储.于是在网上进行查找,发现了一个比较好的解决方案,就是采用S ...

  8. 【python】点分十进制ip与数字互转

    来源:http://www.cnblogs.com/vovlie/archive/2012/10/17/2727029.html 利用lambda表达式,非常简洁 int_to_ip = lambda ...

  9. Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds -----》myeclipse2015

    错误:Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds 错误提示就是我们限定了部署的时间导致的错 ...

  10. node的安装

    1.打开node官网  https://nodejs.org/en/  下载最新版node安装包 v6.2.2 版本自带npm包,比较方便使用.下载之后就正常的常规安装即可. 2.查看是否安装成功   ...