- 浏览: 86718 次
- 性别:
- 来自: 长沙
文章分类
最新评论
-
wangyudong:
家里是拨号上网,不行的,找到了一个Holer工具,只需要配置一 ...
【如何通过广域网访问局域网的服务器?】 -
叮咚可乐名:
大数据就是这么任性第一季数据结构和算法http://pan.b ...
数据结构与算法-简介 -
戢_时光:
...
【Java集合之二】Set集合 -
lzg406:
HTTP默认方式下,使用持久连接。 这个值得推敲
【应用层】之一:HTTP协议与FTP协议 -
mute_:
特别腻害呢
正则表达式1:概念
本文围绕以下三个部分展开:
一、UI单线程原则
二、自定义线程
三、消息机制
案例一、非UI线程不能更新UI
案例二、通过runOnUiThread,在非UI线程中更新UI
案例三、通过Handler+Message--handlerMessage(消息机制)更新UI
一、UI单线程原则
(1)
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread)。Main Thread 也叫 UI Thread,也即 UI线程。
主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。
(2)
系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去。结果就是,响应系统回调的方法(比如响应用户动作的onKeyDown()和各种生命周期回调)永远都是在UI线程里运行。
(3)
当App做一些比较重(intensive)的工作的时候,除非合理地实现,否则单线程模型的performance会很poor。
特别的是,如果所有的工作都在UI线程,做一些比较耗时的工作比如访问网络或者数据库查询,都会阻塞UI线程,导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程blocked的时间太长(大约超过5秒),用户就会看到ANR(application not responding)的对话框。
(4)
Android UI Thread并不是线程安全的,所以不能从非UI线程来操纵UI组件。必须把所有的UI操作放在UI线程里。
(5)UI单线程原则(Android 单线程模型的两条原则):
1)不要阻塞UI线程。
2)不要在UI线程之外访问Android UI Toolkit(主要是这两个包中的组件:android.widget 和 android.view)
二、自定义线程
自定义线程的两种方法:
(1)继承 Thread 类
(2)实现 Runnable 接口
三、消息机制
1. Handler
(1)
Handler用于处理线程中的消息队列。当Looper对象从消息队列中获取消息后,会把消息派发给Handler对象。
一个线程中只能有一个Handler对象,可以通过该对象向所在线程发送消息。因此,只要拥有其它线程中Handler对象的引用,就可以向其发送消息;除了给别的线程发送消息外,还可以给本线程发送消息。
通过Handler你可以发布或者处理一个消息或者是一个Runnable的实例。每个Handler都会与唯一的一个线程以及该线程的消息队列管理绑定。
(2)Handler一般有两种用途:
1)实现一个定时任务
2)在线程间传递数据
2. Message
Message是一个描述消息的数据结构类。
Message包含很多成员变量和方法,但对于简单的消息处理,一般仅需了解3项,分别是:
(1)int what:这是用户自定义的一个整型值,用于区分消息类型。
(2)int arg1:这是额外消息参数。
(3)int arg2:同arg1。
3. Message Queue
Message Queue是一个消息队列,用来存放通过Handler发布的消息。
Android在第一次启动程序时会默认会为UI Thread创建一个关联的消息队列,可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,如activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI Thread通讯。
4. Looper
Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。
案例一、非UI线程不能更新UI
MainActivity。运行的时候,会直接退出。
案例二、通过runOnUiThread,在非UI线程中更新UI
效果描述:
刚进入界面是这样的:
当点击“启动线程”按钮的时候,文本框显示当前时间,并且秒数在时刻随时间变化。当点击“停止线程”按钮的时候,文本框显示的时间停止变化。
(1)strings.xml
(2)activity_main.xml。布局
(3)MainActivity。通过runOnUiThread,在非UI线程中更新UI
案例三、通过Handler+Message--handlerMessage(消息机制)更新UI
效果描述:
刚进入界面是这样的:
当点击按钮后,跳到另一个界面,然后数字和当前时间同时在每秒增加 1。
(1)strings.xml
(2)创建一个 Activity :UpdateUIActivity
(3)activity_main.xml。主界面按钮。
(4)MainActivity。主界面按钮的点击事件。
(5)activity_update_ui.xml。跳转后的界面上,写一个文本,用于显示 UI 变化。
(6)UpdateUIActivity。通过Handler+Message--handlerMessage(消息机制)更新UI
一、UI单线程原则
二、自定义线程
三、消息机制
案例一、非UI线程不能更新UI
案例二、通过runOnUiThread,在非UI线程中更新UI
案例三、通过Handler+Message--handlerMessage(消息机制)更新UI
一、UI单线程原则
(1)
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread)。Main Thread 也叫 UI Thread,也即 UI线程。
主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。
(2)
系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去。结果就是,响应系统回调的方法(比如响应用户动作的onKeyDown()和各种生命周期回调)永远都是在UI线程里运行。
(3)
当App做一些比较重(intensive)的工作的时候,除非合理地实现,否则单线程模型的performance会很poor。
特别的是,如果所有的工作都在UI线程,做一些比较耗时的工作比如访问网络或者数据库查询,都会阻塞UI线程,导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程blocked的时间太长(大约超过5秒),用户就会看到ANR(application not responding)的对话框。
(4)
Android UI Thread并不是线程安全的,所以不能从非UI线程来操纵UI组件。必须把所有的UI操作放在UI线程里。
(5)UI单线程原则(Android 单线程模型的两条原则):
1)不要阻塞UI线程。
2)不要在UI线程之外访问Android UI Toolkit(主要是这两个包中的组件:android.widget 和 android.view)
二、自定义线程
自定义线程的两种方法:
(1)继承 Thread 类
(2)实现 Runnable 接口
三、消息机制
1. Handler
(1)
Handler用于处理线程中的消息队列。当Looper对象从消息队列中获取消息后,会把消息派发给Handler对象。
一个线程中只能有一个Handler对象,可以通过该对象向所在线程发送消息。因此,只要拥有其它线程中Handler对象的引用,就可以向其发送消息;除了给别的线程发送消息外,还可以给本线程发送消息。
通过Handler你可以发布或者处理一个消息或者是一个Runnable的实例。每个Handler都会与唯一的一个线程以及该线程的消息队列管理绑定。
(2)Handler一般有两种用途:
1)实现一个定时任务
2)在线程间传递数据
2. Message
Message是一个描述消息的数据结构类。
Message包含很多成员变量和方法,但对于简单的消息处理,一般仅需了解3项,分别是:
(1)int what:这是用户自定义的一个整型值,用于区分消息类型。
(2)int arg1:这是额外消息参数。
(3)int arg2:同arg1。
3. Message Queue
Message Queue是一个消息队列,用来存放通过Handler发布的消息。
Android在第一次启动程序时会默认会为UI Thread创建一个关联的消息队列,可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,如activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI Thread通讯。
4. Looper
Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。
案例一、非UI线程不能更新UI
MainActivity。运行的时候,会直接退出。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /*当在非UI线程中操作UI组件的时候,会无法运行*/ // 启动匿名的线程类 (非UI线程不能更新UI) new Thread(){ @Override public void run() { Toast.makeText(getApplicationContext(), "在非UI线程中不能访问UI", Toast.LENGTH_LONG).show(); } }.start(); }
案例二、通过runOnUiThread,在非UI线程中更新UI
效果描述:
刚进入界面是这样的:
当点击“启动线程”按钮的时候,文本框显示当前时间,并且秒数在时刻随时间变化。当点击“停止线程”按钮的时候,文本框显示的时间停止变化。
(1)strings.xml
<resources> <string name="app_name">HandlerUI</string> <string name="action_settings">Settings</string> <string name="btn_start">启动线程</string> <string name="btn_stop">停止线程</string> </resources>
(2)activity_main.xml。布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <Button android:id="@+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="@string/btn_start" /> <Button android:id="@+id/btnStop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/btnStart" android:onClick="onClick" android:text="@string/btn_stop" /> <TextView android:id="@+id/tvState" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/btnStop" android:text="@string/app_name" android:textSize="30sp" /> </RelativeLayout> <!-- 2.两个 Button,一个 TextView-->
(3)MainActivity。通过runOnUiThread,在非UI线程中更新UI
package com.android.handlerui; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; import java.text.SimpleDateFormat; import java.util.Date; public class MainActivity extends Activity { private static final String TAG = "UIThread"; // 4 创建一个 Handler 对象 private Handler handler = new Handler(); // 5.1 将要执行的操作写在线程对象的 run 方法当中 // 使用匿名的 Runnable接口 private Runnable action = new Runnable() { @Override public void run() { // 模拟下载 Log.v(TAG, "下载中..."); // 5.4 在 run 方法内部,执行 postDelayed 方法: // 每隔1秒执行一次(日志输出 下载中...)。 handler.postDelayed(action, 1000); // 6. 在非 UI 线程中更新 UI // 调用 runOnUiThread:调用后,相当于又唤醒了 UI线程。 // 这样,外面的非 UI线程控制时间,UI线程同时在更新界面 /** * runOnUiThread : * * Runs the specified action on the UI thread. * If the current thread is the UI thread, * then the action is executed immediately. * If the current thread is not the UI thread, * the action is posted to the event queue of the UI thread. */ runOnUiThread(new Runnable() { private TextView tvState = (TextView) findViewById(R.id.tvState); @Override public void run() { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String text = sdf.format(date); tvState.setText(text); } }); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 3. public void onClick(View view) { switch (view.getId()) { case R.id.btnStart: // 5.2 调用 Handler 的 post 方法,将要执行的线程对象添加到消息队列当中, // 那么队列就会执行线程,调用 run() 方法 // post方法:线程只执行一次就结束了。 handler.post(action); break; case R.id.btnStop: // 5.3 从队列中移除线程对象 handler.removeCallbacks(action); break; } } // -------------------------------------------------------------- @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
案例三、通过Handler+Message--handlerMessage(消息机制)更新UI
效果描述:
刚进入界面是这样的:
当点击按钮后,跳到另一个界面,然后数字和当前时间同时在每秒增加 1。
(1)strings.xml
<string name="btn_update_ui">通过Handler更新UI</string>
(2)创建一个 Activity :UpdateUIActivity
(3)activity_main.xml。主界面按钮。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <Button android:id="@+id/btnUpdateUI" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="@string/title_activity_update_ui" /> </RelativeLayout>
(4)MainActivity。主界面按钮的点击事件。
public void onClick(View view) { startActivity(new Intent(this, UpdateUIActivity.class)); }
(5)activity_update_ui.xml。跳转后的界面上,写一个文本,用于显示 UI 变化。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.android.handlerui.UpdateUIActivity"> <TextView android:id="@+id/tvState" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_update_ui" android:textSize="30sp" /> </RelativeLayout>
(6)UpdateUIActivity。通过Handler+Message--handlerMessage(消息机制)更新UI
package com.android.handlerui; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; import java.text.SimpleDateFormat; import java.util.Date; public class UpdateUIActivity extends Activity { // 定义常量:消息代号 private static final int MSG_CODE = 100; // 1.3 private TextView tvState; // 1.2 private Handler handler; // 当写上 static后,count的生命周期就和 app的生命周期一样了。 // 这样的话,当返回主界面,再进入后,统计数字就不会是从0开始的了。 // private int count; -- 当返回主界面,再进入后,统计数字就是从0开始的。 private static int count; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_update_ui); // 1.3 tvState = (TextView) findViewById(R.id.tvState); // 2.1 调用更新界面的方法 updateUI(); } private void updateUI() { handler = new Handler() { /** * 子类只有重写此方法才能接收消息 * @param msg 接收的消息 */ @Override public void handleMessage(Message msg) { // 2.3 根据消息代码处理消息 // 用户指定的消息代码 switch (msg.what) { case MSG_CODE: // 2.4 当接收到消息后,handle自己再延时 1s发送消息 sendMessageDelayed(obtainMessage(MSG_CODE), 1000); // 处理消息 Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 显示count数字自动增长,以及时间 String text = String.format("%d\n%s", (count++), sdf.format(date)); tvState.setText(text); break; } } }; } /** * onResume():活动到前台,UI获得焦点,进入运行状态 * <p/> * 而 onStart():活动进入可见状态 */ @Override protected void onResume() { super.onResume(); // 2.2 handle & msg 不为空时,延迟 1s发送消息 // handler肯定不为空:上面已经 new 出来了:handler = new Handler(){} if (handler != null) { // 设置发送消息的代号为 MSG_CODE Message msg = handler.obtainMessage(MSG_CODE); if (msg != null) { handler.sendMessageDelayed(msg, 1000); } } } /** * 2.5 暂停的时候,移除消息 * <p/> * onPause():另一个活动即将到前台获得焦点(透明或没有完全覆盖屏幕),当前活动仍 * 可见但不可交互;当系统资源不足时,会被销毁。 */ @Override protected void onPause() { super.onPause(); if (handler != null) { // 移除消息 handler.removeMessages(MSG_CODE); } } // ------------------------------------------------------------ @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_update_ui, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
发表评论
-
【攻克Android (43)】WebView (网络视图)
2015-09-09 22:15 1193本文围绕以下两个部分展开: 一、WebView WebVi ... -
WebView (网络视图)
2015-09-09 22:12 0本文围绕以下两个部分展开: 一、WebView WebVi ... -
【攻克Android (41)】HttpURLConnection
2015-09-08 17:43 1289本文围绕以下三个部分展开: 一、HttpURLConnec ... -
HttpURLConnection
2015-09-08 17:42 0本文围绕以下三个部分展开: 一、HttpURLConnec ... -
【攻克Android (40)】JSON解析
2015-09-07 20:49 1221本文围绕以下三个部分 ... -
JSON解析
2015-09-07 20:47 0本文围绕以下三个部分 ... -
【攻克Android (39):XML解析之四】Simple方式解析XML
2015-08-27 18:38 1646本文围绕以下三个部分 ... -
【攻克Android (38):XML解析之三】Pull方式解析XML
2015-08-27 09:25 1678本文围绕以下两个部分展开: 一、Pull解析 案例一:P ... -
【攻克Android (37):XML解析之二】SAX方式解析XML
2015-08-26 15:15 832本文围绕以下两个部分展开: 一、SAX解析 案例一 ... -
【XML解析之一】SAX方式解析XML
2015-08-26 02:58 0本文围绕以下四个部分展开: 一、SAX解析 案例一 ... -
【攻克Android (36):XML解析之一】DOM方式解析XML
2015-08-26 00:27 875本文围绕以下三个部分展开: 一、XML概述 二、DOM解析 ... -
DOM方式解析XML案例
2015-08-26 00:26 0本文围绕以下四个部分展开: 一、DOM解析 案例一 ... -
【XML解析之四】Simple方式解析XML
2015-08-27 18:36 0本文围绕以下三个部分 ... -
【XML解析之一】Pull方式解析XML
2015-08-26 02:56 0本文围绕以下四个部分展开: 一、Pull解析 案例一:P ... -
【攻克Android (22)】View/Tween Animation 视图动画(补间动画)
2015-08-25 21:22 826本文围绕以下四个部分 ... -
【攻克Android (22)】View/Tween Animation 视图动画(补间动画)
2015-08-25 21:21 0本文围绕以下四个部分 ... -
【攻克Android (45)】四大组件之 ContentProvider
2015-08-18 16:24 990本文围绕以下两个部分展开: 一、ContentProvide ... -
【攻克Android (32)】Notification 通知
2015-08-17 19:57 875本文围绕以下两个部分展开: 一、通知 案例一 一 ... -
Notification 通知
2015-08-17 19:56 0本文围绕以下两个部分展开: 一、通知 案例一 一 ... -
BroadcastReceiver完整版
2015-08-17 13:15 0本文围绕以下四个部分展开: 一、广播 案例一:系统广播(属于 ...
相关推荐
攻克学习多线程时碰到的难题,希望可以帮到大家
24学时攻克c++_ 笔记
可以很好的去理解,java多线程.如果疑问可以发邮件给我,我会及时去回复。一起交流学习。
24小时攻克c++代码
24学时攻克c++,这本书不错,不过只上传了23页
很多人学习完JavaSE/JavaEE之后想往更深入的技术进行探索,比如对大数据、分布式、高并发类的专题进行攻克时,立即遇到针对java.lang包中线程类的学习,但线程类的学习并不像JDBC一样简单,学习曲线陡峭,多弯路与...
这是最精彩的源代码及练习,初学C++的朋友千万别错过哟!
24张图攻克border-image.doc
详尽解释 android 里的Activity的生命周期问题,攻克开发android程序中的难题
房价预测模型算法实现,包含了各种各样的Matlab双算法程序与,可以解决建模中的难题,攻克程序难关
大学生攻克Linux系统教程(又名天下没有难学的Linux)。一位在校大学生整理的教程,献给每一位要征服Linux的学习者-五星级的Linux教程。 本书汇集众多Linux学习者的心血与贡献,有很多作者在默默的为你呼喊,感谢...
8天攻克8000词汇,word版,方便学习,背单词的好东东
一天攻克平衡小车(含STC15及STM32) 一天攻克平衡小车(含STC15及STM32) 一天攻克平衡小车(含STC15及STM32) 一天攻克平衡小车(含STC15及STM32)
给初学者的教程, 内容比较基础.包括安装,熟悉Linux环境,VI基础,GDB基础. 编程基础. 内容与一些linux的基础手册类似.
大学生攻克Linux系统教程(又名天下没有难学的Linux)
迈向2035:攻克数字经济下的人才战.pdf
C++24学时教程PDF版个人感觉,适合初学者