UI线程与其他线程

当应用程序启动,创建了一个叫“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的两个重要方法:sendMessagepost
    • 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. });
            btnEnd.setOnClickListener(new View.OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    new Thread(new Runnable() {
    
                        @Override
                        public void run()
                        {
                            //在新建的线程(工作线程)中改变Button的文字
                            btnEnd.setText("Text Changed in Sub Thread");
                        }
                        
                    }).start();
                }
            });

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

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

    HandlerActivity02主要代码:

    Java代码 复制代码 收藏代码
    1. public class HandlerActivity02extends 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 MyTaskextends 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. }
    public class HandlerActivity02 extends Activity
    {
    
        private int title = 0;
        Button btnStart,btnEnd;
    
        private Handler mHandler = new Handler()
        {
            public void handleMessage(Message msg)
            {
                //更新UI
                switch (msg.what)
                {
                    case 1:
                        updateTitle();
                        break;
                }
            };
        };
        
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            btnStart = (Button)findViewById(R.id.start);
            btnEnd = (Button)findViewById(R.id.end);
            //新启动一个线程,进行耗时操作
            Timer timer = new Timer();
            //每六秒执行一次MyTask的run方法
            timer.scheduleAtFixedRate(new MyTask(this), 1, 6000);
        }
    
        private class MyTask extends TimerTask
        {
            private Activity context;
            MyTask(Activity context)
            {
                this.context = context;
            }
            
            @Override
            public void run()
            {
                //耗时操作略.... 
                
                //更新UI方法  1
                Message message = new Message();
                message.what = 1;
                mHandler.sendMessage(message);
                
                //更新UI方法  2
                mHandler.post(updateThread);
                
                //更新UI方法  3
                context.runOnUiThread(updateThread);
            }
        }
    
        public void updateTitle()
        {
            setTitle("Welcome to Mr Wei's blog " + title);
            title++;
        }
        
        Runnable updateThread = new Runnable() 
        {
    
            @Override
            public void run()
            {
                //更新UI
                btnStart.setText(String.valueOf(title));
                btnEnd.setText(String.valueOf(title));
            }
            
        };
    }

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

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

    HandlerActivity03主要代码:

    Java代码 复制代码 收藏代码
    1. public class HandlerActivity03extends 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 LongTimeTaskextends AsyncTask<String, Void , String>
    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. }
    public class HandlerActivity03 extends Activity
    {
        Button btnStart;
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            btnStart = (Button)findViewById(R.id.start);
            btnStart.setOnClickListener(new View.OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    //开始执行AsyncTask,并传入某些数据
                    new LongTimeTask().execute("New Text");
                }
            });
        }
        
        private class LongTimeTask extends AsyncTask<String, Void , String>
        {
    
            @Override
            protected String doInBackground(String... params)
            {
                try
                {
                    //线程睡眠5秒,模拟耗时操作,这里面的内容Android系统会自动为你启动一个新的线程执行
                    Thread.sleep(5000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                return params[0];
            }
    
            @Override
            protected void onPostExecute(String result)
            {
                //更新UI的操作,这里面的内容是在UI线程里面执行的
                btnStart.setText(result);
            }
            
        }
    
    }

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

  • Kaiwii注:

  • 因为AsyncTask的onPreExecute()和onPostExecute()方法实际上是在UI线程上运行的。而操作UI的代码逻辑必须要在UI线程上

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值