Android中的好多应用,如UI更新,游戏开发,和耗时的操作都需要用到多线程的知识。而对Android中的线程机制好多人多觉得学习有困难。下面我们就一起来学习一下。

谈及Android中的线程我们会涉及到如下几个概念:

1. Handler 2. Looper 3. HandlerThread 4.Message 5. MessageQueue

下面我们由浅入深来探讨一下。我们用java多线程中很经典的例子计时器为例。我们要编写实现一个计时器,该如何编写呢?按照java中多线程的思路,我们很自然的会这样实现

代码如下:

[java] view plain copy
  1. packagexuan.blog.hander;
  2. importandroid.app.Activity;
  3. importandroid.os.Bundle;
  4. importandroid.util.Log;
  5. importandroid.view.View;
  6. importandroid.widget.Button;
  7. importandroid.widget.TextView;
  8. publicclassclockActivityextendsActivity{
  9. /**Calledwhentheactivityisfirstcreated.*/
  10. privateStringTAG="clockActivity";
  11. privateButtonendButton;
  12. privateTextViewtextView;
  13. privateThreadclockThread;
  14. privateinttimer=0;
  15. privatebooleanisRunning=true;
  16. @Override
  17. publicvoidonCreate(BundlesavedInstanceState){
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.main);
  20. endButton=(Button)findViewById(R.id.endBtn);
  21. textView=(TextView)findViewById(R.id.textview);
  22. endButton.setOnClickListener(newView.OnClickListener(){
  23. @Override
  24. publicvoidonClick(Viewv){
  25. //TODOAuto-generatedmethodstub
  26. isRunning=false;
  27. }
  28. });
  29. clockThread=newThread(newRunnable(){
  30. @Override
  31. publicvoidrun(){
  32. //TODOAuto-generatedmethodstub
  33. while(isRunning){
  34. try{
  35. Thread.currentThread().sleep(1000);
  36. timer++;
  37. textView.setText("走了"+timer+"秒");
  38. Log.d(TAG,"time:"+timer);
  39. }catch(InterruptedExceptione){
  40. e.printStackTrace();
  41. }
  42. }
  43. }});
  44. clockThread.start();
  45. }
  46. }

我们用Thread的构造方法创建了一个线程对象,并启动了该线程,在该线程中来计算并更新textView。

然而,在Android中运行我们会发现这样会抛出异常!!

这是因为Android系统中的视图组件并不是线程安全的,在Android中UI的更新必须由主线程来完成。而上面我们违反了这一规则。

我们应该如何解决这个问题呢?我们应该让主线程负责创建,显示和更新UI控件,启动子线程,停止子线程。让子线程完成计算并向主线程发出更新UI的消息让主线程来更新UI。

如何实现子线程和主线程之间的消息传递呢?Android中为我们提供了Handler机制。

Handler是android中多线程间传递消息和计划任务的“工具”类。Handler会在多个线程中发送Message和Runnable。

andler在多线程中有两方面的应用:

1.发送消息,在不同的线程间发送消息,使用方法sendXXX();

2.计算任务,在未来执行某任务,使用方法postXXX();

一个线程发出消息后,接受消息是通过重写Handler类中的handleMessage(Message)方法实现。

Handler也可以通过postXXX()方法提交计划任务,当取出一个任务Runnable是便会执行其中的run()方法。

经过分析,我们用handler发送消息的应用修改上述代码:

[java] view plain copy
  1. publicclassclockActivityextendsActivity{
  2. /**Calledwhentheactivityisfirstcreated.*/
  3. privateStringTAG="clockActivity";
  4. privateButtonendButton;
  5. privateTextViewtextView;
  6. privateThreadclockThread;
  7. privateinttimer=0;
  8. privatebooleanisRunning=true;
  9. privateHandlerhandler;
  10. @Override
  11. publicvoidonCreate(BundlesavedInstanceState){
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.main);
  14. endButton=(Button)findViewById(R.id.endBtn);
  15. textView=(TextView)findViewById(R.id.textview);
  16. endButton.setOnClickListener(newView.OnClickListener(){
  17. @Override
  18. publicvoidonClick(Viewv){
  19. //TODOAuto-generatedmethodstub
  20. isRunning=false;
  21. }
  22. });
  23. handler=newHandler(){
  24. @Override
  25. publicvoidhandleMessage(Messagemsg){
  26. //TODOAuto-generatedmethodstub
  27. super.handleMessage(msg);
  28. switch(msg.what){
  29. case0:textView.setText("走了"+timer+"秒");
  30. }
  31. }
  32. };
  33. clockThread=newThread(newRunnable(){
  34. @Override
  35. publicvoidrun(){
  36. //TODOAuto-generatedmethodstub
  37. while(isRunning){
  38. try{
  39. Thread.currentThread().sleep(1000);
  40. timer++;
  41. //textView.setText("走了"+timer+"秒");
  42. Messagemsg=newMessage();
  43. msg.obj=timer;
  44. msg.what=0;
  45. handler.sendMessage(msg);
  46. Log.d(TAG,"time:"+timer);
  47. }catch(InterruptedExceptione){
  48. e.printStackTrace();
  49. }
  50. }
  51. }});
  52. clockThread.start();
  53. }
  54. }

通过这种方式便实现了子线程和主线程之间的通信。当然我们也可以通过Handler的执行计划任务来完成这计时器的功能,代码如下:

[java] view plain copy
  1. publicclassclockActivityextendsActivity{
  2. /**Calledwhentheactivityisfirstcreated.*/
  3. privateStringTAG="clockActivity";
  4. privateButtonendButton;
  5. privateTextViewtextView;
  6. privateinttimer=0;
  7. privatebooleanisRunning=true;
  8. privateHandlerhandler;
  9. @Override
  10. publicvoidonCreate(BundlesavedInstanceState){
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.main);
  13. endButton=(Button)findViewById(R.id.endBtn);
  14. textView=(TextView)findViewById(R.id.textview);
  15. endButton.setOnClickListener(newView.OnClickListener(){
  16. @Override
  17. publicvoidonClick(Viewv){
  18. //TODOAuto-generatedmethodstub
  19. isRunning=false;
  20. }
  21. });
  22. handler=newHandler();
  23. Runnabler=newRunnable(){
  24. @Override
  25. publicvoidrun(){
  26. //TODOAuto-generatedmethodstub
  27. if(isRunning){
  28. textView.setText("走了"+timer+"秒");
  29. timer++;
  30. handler.postDelayed(this,1000);//提交任务r,延时1秒执行
  31. }
  32. }
  33. };
  34. handler.postDelayed(r,1000);
  35. }
  36. }

注意运行完handler.postDelayed(r,1000);只是提交了一次任务,因此为了反复执行要在run()方法中再次提交。注意上述方法中发送任务和执行任务都是在主线程中实现的,我们并没有令启动线程。

在下一篇文章中,我们在具体分析一下Android中的消息机制。

更多相关文章

  1. Android(安卓)知识体系
  2. Android的线程使用来更新UI----Thread、Handler、Looper、TimerT
  3. 无废话Android之smartimageview使用、android多线程下载、显式意
  4. Android的消息机制
  5. android,handler实现两个线程通信
  6. Android常见问题总结(一)
  7. Android(安卓)Binder入门指南之Binder服务的消息循环
  8. Android(安卓)老生常谈之消息机制
  9. Android异步处理三:Handler+Looper+MessageQueue深入详解

随机推荐

  1. 详解mysql中的静态变量的作用
  2. MySQL中浮点型转字符型可能会遇的问题详
  3. 详解数据库多表连接查询的实现方法
  4. Centos7.3下mysql5.7安装配置教程
  5. 详解MySQL导出指定表中的数据的实例
  6. MySQL无法启动的解决办法
  7. MySQL curdate()函数的实例详解
  8. 详解java调用ffmpeg转换视频格式为flv
  9. MySQL5.5.27安装图文教程
  10. 基于Mysql的Sequence实现方法