Handler机制的原理

Android 的 Handler 机制(也有人叫消息机制)目的是为了跨线程通信,也就是多线程通信。之所以需
要跨线程通信是因为在 Android 中主线程通常只负责 UI 的创建和修改,子线程负责网络访问和耗时操作,
因此,主线程和子线程需要经常配合使用才能完成整个 Android 功能。
       Handler 机制可以近似用图 1 展示。MainThread 代表主线程,newThread 代表子线程。
       MainThread 是 Android 系统创建并维护的,创建的时候系统执行了 Looper.prepare();方法,该方法内部
创建了 MessageQueue 消息队列(也叫消息池),该消息队列是 Message 消息的容器,用于存储通过 handler
发送过来的 Message。MessageQueue 是 Looper 对象的成员变量,Looper 对象通过 ThreadLocal 绑定在
MainThread 中。因此我们可以简单的这么认为:MainThread 拥有唯一的一个 Looper 对象,该 Looper 对象
有用唯一的 MessageQueue 对象,MessageQueue 对象可以存储多个 Message。
       MainThread 中需要程序员手动创建 Handler 对象,并覆写 Handler 中的 handleMessage(Message msg)
方法,该方法将来会在主线程中被调用,在该方法里一般会写与 UI 修改相关的代码。
       MainThread 创建好之后,系统自动执行了 Looper.loop();方法,该方法内部开启了一个“死循环”不断
的去之前创建好的 MessageQueue 中取 Message。如果一有消息进入 MessageQueue,那么马上会被
Looper.loop();取出来,取出来之后就会调用之前创建好的 handler 对象的 handleMessage(Message)方法。
newThread 线程是我们程序员自定 new 出来的子线程。在该子线程中处理完我们的“耗时”或者网络
访问任务后,调用主线程中的 handler 对象的 sendMessage(msg)方法,该方法一被执行,内部将就 msg
添加到了主线程中的 MessageQueue 队列中,这样就成为了 Looper.loop()的盘中餐了,等待着被消费。这是

一个很复杂的过程,但是
Android 显然已经将这种模式给封装起来了,就叫 Handler 机制。我们使用时只需要在主线程中创建 Handler,并覆写
handler 中的handleMessage 方法,然后在子线程中调用 handler 的 sendMessage(msg)方法即可。

图1 Handler原理图

案例

网页源码查看器:

activity_layout.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:paddingBottom="@dimen/activity_vertical_margin"
  6. android:paddingLeft="@dimen/activity_horizontal_margin"
  7. android:paddingRight="@dimen/activity_horizontal_margin"
  8. android:paddingTop="@dimen/activity_vertical_margin"
  9. tools:context="com.seachal.htmlviewer.MainActivity"
  10. >
  11. <LinearLayout
  12. android:id="@+id/llay_top"
  13. android:layout_width="match_parent"
  14. android:layout_height="wrap_content"
  15. android:orientation="horizontal" >
  16. <EditText
  17. android:id="@+id/et_url"
  18. android:layout_width="0dp"
  19. android:layout_height="wrap_content"
  20. android:layout_weight="1"
  21. android:hint="请输入网络地址"
  22. android:text="http://www.baidu.com" />
  23. <Button
  24. android:layout_width="wrap_content"
  25. android:layout_height="wrap_content"
  26. android:onClick="load"
  27. android:text="确定" />
  28. </LinearLayout>
  29. <ScrollView
  30. android:layout_width="match_parent"
  31. android:layout_height="match_parent"
  32. android:layout_below="@id/llay_top"
  33. >
  34. <TextView
  35. android:id="@+id/tv_content"
  36. android:layout_width="wrap_content"
  37. android:layout_height="wrap_content"
  38. android:text="@string/hello_world" />
  39. </ScrollView>
  40. </RelativeLayout>

工具类将字节流转化为字符串 StreamUtls.java:

  1. public class StreamUtils {
  2. /**
  3. * 将字节流转化为字符串,使用android 默认编码
  4. *
  5. * @author ZhangSeachal
  6. * @date 2016年8月6日下午4:20:43
  7. * @version 1.0
  8. * @param inputStream
  9. * @return
  10. * @throws IOException
  11. */
  12. public static String inputStream2String(InputStream inputStream)
  13. throws IOException {
  14. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  15. int len = -1;
  16. byte[] buffer = new byte[1024];
  17. while ((len = inputStream.read(buffer)) != -1) {
  18. baos.write(buffer, 0, len);
  19. }
  20. inputStream.close();
  21. return new String(baos.toByteArray());
  22. }
  23. <span style="font-size:18px;"><strong>}</strong></span>

MainActivity.java

  1. /**
  2. * 网络源码查看器
  3. *
  4. * @author ZhangSeachal
  5. * @date 2016年8月5日 下午10:07:34
  6. * @version 1.0
  7. * @since
  8. */
  9. public class MainActivity extends Activity {
  10. private TextView tv_content;
  11. private EditText et_url;
  12. /** 创建一个Handler对象, 覆写类体、方法体 */
  13. private Handler handler = new Handler() {
  14. /**
  15. * 覆写handleMessage方法,在该方法中完成我们想做的工作, 该方法是在主线程中 被 调用的,因此可以再这里面修改UI。
  16. */
  17. public void handleMessage(Message msg) {
  18. // 判断Message 的类型,根据msg的what属性去获取期类型
  19. switch (msg.what) {
  20. // 如果成功
  21. case RESULT_OK:
  22. // 从msg的obj属性中获取数据,然后显示在TextView 上。
  23. tv_content.setText(msg.obj.toString());
  24. break;
  25. // 如果失败
  26. case RESULT_CANCELED:
  27. // 弹 吐司,给用户提示
  28. Toast.makeText(MainActivity.this, "访问网页失败", Toast.LENGTH_LONG)
  29. .show();
  30. default:
  31. break;
  32. }
  33. }
  34. };
  35. @Override
  36. protected void onCreate(Bundle savedInstanceState) {
  37. super.onCreate(savedInstanceState);
  38. setContentView(R.layout.activity_main);
  39. // 初始化控件
  40. et_url = (EditText) findViewById(R.id.et_url);
  41. tv_content = (TextView) findViewById(R.id.tv_content);
  42. }
  43. /**
  44. * 加载 网页源码
  45. *
  46. * @author ZhangSeachal
  47. * @date 2016年8月5日下午10:29:12
  48. * @version 1.0
  49. * @param view
  50. */
  51. public void load(View view) {
  52. // 获取用户输入的数据
  53. final String path = et_url.getText().toString().trim();
  54. /*
  55. * 网络访问必须在子线程中进行
  56. */
  57. new Thread(new Runnable() {
  58. @Override
  59. public void run() {
  60. // TODO Auto-generated method stub
  61. try {
  62. // 1.创建一个 URl对象,需要传入url
  63. URL url = new URL(path);
  64. /*
  65. * 2.使用url对象打开一个HttpURLConnection,
  66. * 由于其返回的是HttpURLConnection的父类,
  67. */
  68. HttpURLConnection connection = (HttpURLConnection) url
  69. .openConnection();
  70. /*
  71. * 3.配置connection 连接参数
  72. */
  73. // 设置联网超时时长,单位毫秒
  74. connection.setConnectTimeout(5000);
  75. /*
  76. * 设置数据读取超时 注意: 不是指读取数据总耗时超时, 而是能够读取到数据流等待时长
  77. */
  78. connection.setReadTimeout(5000);
  79. /**
  80. * 设置请求方式,默认是GET,但是为了增加代码易读性, 建议显示只是为GET
  81. */
  82. connection.setRequestMethod("GET");
  83. // 4. 开始连接网络
  84. connection.connect();
  85. // 5.以字节 输入流 的形式获取服务端发来的数据
  86. InputStream inputStream = connection.getInputStream();
  87. // 6.将字节流转化为字符串 (使用自定义的StreamUtils工具类)
  88. final String data = StreamUtils
  89. .inputStream2String(inputStream);
  90. /*
  91. * 7.将获取的数据封装到Message对象,然后发送给handler
  92. */
  93. Message msg = new Message();
  94. /*
  95. * 给Message 对象 的what属性设置一个int类型的值。 因为消息可能会有多个,因此为了区分这些不同的消息。
  96. * 需要给消息设置What属性. RESULT_OK 是Activity的常量值为-1,
  97. * 当然也可以自定义一个int类型的值。
  98. */
  99. msg.what = RESULT_OK;
  100. // msg.what = RESULT_CANCELED;
  101. /**
  102. * 给Message队形的obj属性设置一个object类型的属性。 该值正是我们需要在
  103. * Meaage对象上绑定的数据,这里绑定的 从网络上获取到的网页编码字符串。
  104. */
  105. msg.obj = data;
  106. /*
  107. * 给主线程发送消息。 发送后,系统会调用handler对象的handlerMessage(Message) 方法。
  108. * 该方法正是 我们自己实现的,而且该方法是在主线程中执行的。 从而就实现了从子线程中
  109. * 访问网络数据(耗时操作),然后交给主线程, 让主线程修改UI(修改UI只能在主线程中做)。
  110. */
  111. handler.sendMessage(msg);
  112. } catch (Exception e) {
  113. // TODO: handle exception
  114. e.printStackTrace();
  115. Log.d("tag", "遇到异常" + e, e);
  116. /**
  117. * 如果遇到异常,最好让主线程也知道子线程遇到异常了。 因此使用handler 发动一个空消息,
  118. * 所谓的空消息是指,该消息没有obj值, 只有一个what属性。 这列的RESULT_CANCELED
  119. * 就是一个int型的常量, 当然我们可以自定义,这里只不过是直接使用了Activity类的 一个常量而已。
  120. * 该消息发送后,系统依然会调用handler对象 的handlerMessage(Message)方法。
  121. */
  122. handler.sendEmptyMessage(RESULT_CANCELED);
  123. }
  124. }
  125. }).start();
  126. }
  127. @Override
  128. public boolean onCreateOptionsMenu(Menu menu) {
  129. // Inflate the menu; this adds items to the action bar if it is present.
  130. getMenuInflater().inflate(R.menu.main, menu);
  131. return true;
  132. }
  133. @Override
  134. public boolean onOptionsItemSelected(MenuItem item) {
  135. // Handle action bar item clicks here. The action bar will
  136. // automatically handle clicks on the Home/Up button, so long
  137. // as you specify a parent activity in AndroidManifest.xml.
  138. int id = item.getItemId();
  139. if (id == R.id.action_settings) {
  140. return true;
  141. }
  142. return super.onOptionsItemSelected(item);
  143. }
  144. }


最后在AndroidManifest.xml 中添加网络访问的权限

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.seachal.htmlviewer"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="16"
  8. android:targetSdkVersion="19" />
  9. <uses-permission  android:name="android.permission.INTERNET"/>
  10. <application
  11. android:allowBackup="true"
  12. android:icon="@drawable/ic_launcher"
  13. android:label="@string/app_name"
  14. android:theme="@style/AppTheme" >
  15. <activity
  16. android:name=".MainActivity"
  17. android:label="@string/app_name" >
  18. <intent-filter>
  19. <action android:name="android.intent.action.MAIN" />
  20. <category android:name="android.intent.category.LAUNCHER" />
  21. </intent-filter>
  22. </activity>
  23. </application>
  24. </manifest>

然后就大功告成了,运行一下去看看效果吧。如果有用就收藏一下吧!

Android的Handler机制的更多相关文章

  1. 10分钟了解Android的Handler机制

    Handler机制是Android中相当经典的异步消息机制,在Android发展的历史长河中扮演着很重要的角色,无论是我们直接面对的应用层还是FrameWork层,使用的场景还是相当的多.分析源码一探 ...

  2. android之handler机制深入解析

    一.android中需要另开线程处理耗时.网络的任务,但是有必须要在UI线程中修改组件.这样做是为了: ①只能在UI线程中修改组件,避免了多线程造成组件显示混乱 ②不使用加锁策略是为了提高性能,因为a ...

  3. 【转载】Android 的 Handler 机制实现原理分析

    handler在安卓开发中是必须掌握的技术,但是很多人都是停留在使用阶段.使用起来很简单,就两个步骤,在主线程重写handler的handleMessage( )方法,在工作线程发送消息.但是,有没有 ...

  4. android之Handler机制

    简单例子开头: 网络http请求网站源码数据并显示 注意点:访问网络需要加Internet权限: android.permission.INTERNET 简单的步骤: 使用UrlConnection请 ...

  5. [转]Android中handler机制的原理

    Andriod提供了Handler 和 Looper 来满足线程间的通信.Handler先进先出原则.Looper类用来管理特定线程内对象之间的消息交换(MessageExchange). 1)Loo ...

  6. 涛涛的小马甲 Android之Handler机制

    首先需要了解一个基本的概念ANR:Application not response 即应用程序无响应,也就是俗话说的死机. 出现Anr的原因是: 主线程需要做很多重要的事情,响应点击事件,更新UI如果 ...

  7. Android Handler机制剖析

    android的handler机制是android的线程通信的核心机制 Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃. Android中的实现了 接收消息的& ...

  8. Android多线程通信机制

    掌握Android的多线程通信机制,我们首先应该掌握Android中进程与线程是什么. 1. 进程 在Android中,一个应用程序就是一个独立的进程(应用运行在一个独立的环境中,可以避免其他应用程序 ...

  9. android的消息处理机制——Looper,Handler,Message

    在开始讨论android的消息处理机制前,先来谈谈一些基本相关的术语. 通信的同步(Synchronous):指向客户端发送请求后,必须要在服务端有回应后客户端才继续发送其它的请求,所以这时所有请求将 ...

随机推荐

  1. SFTP 命令列表以备查询

    Available commands: ascii Set transfer mode to ASCII binary Set transfer mode to binary cd path Chan ...

  2. DotLiquid模板引擎简介

    DotLiquid是一个在.Net Framework上运行的模板引擎,采用Ruby的Liquid语法,这个语法广泛的用在Ruby on rails和Django等网页框架中. DotLiquid相比 ...

  3. SQL Server Management Studio 无法修改表,超时时间已到 在操作完成之前超时时

    在修改表时,保存的时候显示:无法修改表,超时时间已到 在操作完成之前超时时间已过或服务器未响应 这是执行时间设置过短的原因,可以修改一下设置便能把执行时间加长,以便有足够的时间执行完修改动作. 在 S ...

  4. IBatisNet -- 保护你的配置文件及映射文件信息

    通常情况下我们在使用IBatisNet的时候,配置文件和映射文件都是暴露在外的,如果能进入到服务器,那么你的程序的操作数据库的SQL语句,数据库连接字符串等信息都将很轻松的被看到,这样是很危险的.然而 ...

  5. [翻译+山寨]Hangfire Highlighter Tutorial

    前言 Hangfire是一个开源且商业免费使用的工具函数库.可以让你非常容易地在ASP.NET应用(也可以不在ASP.NET应用)中执行多种类型的后台任务,而无需自行定制开发和管理基于Windows ...

  6. python中的TypeError错误解决办法

    新手在学习python时候,会遇到很多的坑,下面来具体说说其中一个. 在使用python编写面向对象的程序时,新手可能遇到TypeError: this constructor takes no ar ...

  7. 认识Git

    ---恢复内容开始--- Git是一款免费.开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. Git作为当下最潮流的版本控制工具也是有他独特的不同,最大的不同就在于他有分布式版本管理的 ...

  8. Array&String总结

    每一部分总结后有实例代码,代码中黄色框方法不改变原数组.PS:所有实例结果均一一运行所得. 符号说明: array和string共享    参数 Array --普通方法 栈:   pop()   p ...

  9. css_03之文本、浮动

    1.字体属性:①字体格式:font-family:取值:“microsoft yahei”/Arial……:②字体大小:font-size:取值:pt/px:③字体加粗:font-weight:取值: ...

  10. GreenDao2.2升级GreenDao3.0的适配之路

    前言.为什么要升级到Greendao3.0? 1. 多人开发 以往的数据库建表建Dao等操作要新开一个module,在统一的地方管理数据库建表,现在可以直接写Entity.多人开发时自己管自己的Ent ...