Android 异步更新UI----handler+thread
android应用是单线程模式的。
单线程模式需要记住两条:
一、防止UI线程阻塞
二、确保只在UI线程中访问Android UI工具包
在开发Android应用时必须遵守单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),主线程负责处理和ui相关的事件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。
开一个线程或者在后台线程中来执行耗时的操作,如下面的例子:
public void onClick( View v ) {
new Thread( new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork(); //从网络上下载图片
mImageView.setImageBitmap( b ); //把图片设置给ImageView
}
}).start()
}
上面的代码会报错,你可能会说逻辑很正确啊,但是它违背了Android单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.
例如: 如果在非UI线程直接对UI进行了操作,则会报错:
CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views
Android为我息循环们提供了消的机制,我们可以利用这个机制来实现线程间的通信。那么,我们就可以在非UI线程发送消息到UI线程,最终让Ui线程来进行ui的操作。
Andriod提供了几种在其他线程中访问UI线程的方法:
Activity.runOnUiThread( Runnable )
View.post( Runnable )
View.postDelayed( Runnable, long )
Hanlder
对于运算量较大的操作和IO操作,我们需要新开线程来处理这些繁重的工作,以免阻塞ui线程。
例子:下面我们以获取CSDN logo的例子,演示如何使用Thread+Handler的方式实现在非UI线程发送消息通知UI线程更新界面
ThradHandlerActivity.java:
- package com.example.thread;
- import org.apache.http.HttpResponse;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.impl.client.DefaultHttpClient;
- import com.example.test.R;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.ImageView;
- import android.widget.Toast;
- public class ThreadHandlerActivity extends Activity{
- private static final int MSG_SUCCESS = 0;
- private static final int MSG_FAILURE = 1;
- private ImageView mImageView;
- private Button mButton;
- private Thread mThread;
- @SuppressLint("HandlerLeak")
- private Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SUCCESS:
- mImageView.setImageBitmap((Bitmap)msg.obj);
- Toast.makeText(getApplication(), "成功获取图片", Toast.LENGTH_LONG).show();
- break;
- case MSG_FAILURE:
- Toast.makeText(getApplication(), "获取图片失败", Toast.LENGTH_LONG).show();
- break;
- }
- super.handleMessage(msg);
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.thread_layout);
- mImageView= (ImageView) findViewById(R.id.logo);//显示图片的ImageView
- mButton = (Button) findViewById(R.id.click);
- mButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mThread == null) {
- mThread = new Thread(runnable);
- mThread.start();
- }else {
- Toast.makeText(getApplication(), "线程已经运行", Toast.LENGTH_LONG).show();
- }
- }
- });
- }
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- HttpClient hc = new DefaultHttpClient();
- HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");
- final Bitmap bm;
- try {
- HttpResponse hr = hc.execute(hg);
- bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
- } catch (Exception e) {
- e.printStackTrace();
- mHandler.obtainMessage(MSG_FAILURE).sendToTarget();
- return;
- }
- mHandler.obtainMessage(MSG_SUCCESS, bm).sendToTarget();
- // mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素
- // mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。
- // @Override
- // public void run() {//run()方法会在ui线程执行
- // mImageView.setImageBitmap(bm);
- // }
- // });
- }
- };
- }
对于上面的方法,我们使用的是handler+Thread来实现更新UI,在里面也有一条注意的就是
- mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素</span>
其实我们上面提到一个方法Activity.runOnUiThread( Runnable ),将这个Runnable以UI线程的方式启动
- /**
- * 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.
- *
- * @param action the action to run on the UI thread
- */
- public final void runOnUiThread(Runnable action) {
- if (Thread.currentThread() != mUiThread) {
- mHandler.post(action);
- } else {
- action.run();
- }
- }
上面Activity的runOnUiThread(Runnable)方法实现。
利用Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 这样Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程。
使用示例:
- current_activity.this. runOnUiThread(new Runnable()
- @Override
- public void run() {
- // refresh ui 的操作代码
- }
- });
这里需要注意的是runOnUiThread是Activity中的方法,在线程中我们需要告诉系统是哪个activity调用,所以前面显示的指明了activity.
所以我们修改一下上面的代码:
- package com.example.thread;
- import org.apache.http.HttpResponse;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.impl.client.DefaultHttpClient;
- import com.example.test.R;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.ImageView;
- import android.widget.Toast;
- public class ThreadHandlerActivity extends Activity{
- private static final int MSG_SUCCESS = 0;
- private static final int MSG_FAILURE = 1;
- private ImageView mImageView;
- private Button mButton;
- @SuppressLint("HandlerLeak")
- private Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SUCCESS:
- mImageView.setImageBitmap((Bitmap)msg.obj);
- Toast.makeText(getApplication(), "成功获取图片", Toast.LENGTH_LONG).show();
- break;
- case MSG_FAILURE:
- Toast.makeText(getApplication(), "获取图片失败", Toast.LENGTH_LONG).show();
- break;
- }
- super.handleMessage(msg);
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.thread_layout);
- mImageView= (ImageView) findViewById(R.id.logo);//显示图片的ImageView
- mButton = (Button) findViewById(R.id.click);
- mButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- ThreadHandlerActivity.this.runOnUiThread(runnable);
- }
- });
- }
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- HttpClient hc = new DefaultHttpClient();
- HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");
- final Bitmap bm;
- try {
- HttpResponse hr = hc.execute(hg);
- bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
- } catch (Exception e) {
- e.printStackTrace();
- mHandler.obtainMessage(MSG_FAILURE).sendToTarget();
- return;
- }
- mImageView.setImageBitmap(bm);
- }
- };
- }
也可以在线程里面直接更新UI。
有人会说我传递一个当前的Activity到一个线程中,然后实现UI更新,那我就是调用的当前的Activity的内容,其实这个也是不对的也会提示
- android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Android 异步更新UI----handler+thread的更多相关文章
- Android异步更新UI的四种方式
Android异步更新UI的四种方式 2015-09-06 09:23 segmentfault 字号:T | T 大家都知道由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中 ...
- Android 通过广播来异步更新UI
之前的项目里要做一个异步更新UI的功能,可是结果出现了ANR,所以想写个demo来測试究竟是哪个地方出现了问题,结果发现原来的思路是没有问题,郁闷~~ 如今这个demo 就是模拟项目里面 的步骤 1. ...
- Android异步处理系列文章四篇之二 使用AsyncTask异步更新UI界面
Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ...
- Android异步处理二:使用AsyncTask异步更新UI界面
在<Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面>中,我们使用Thread+Handler的方式实现了异步更新UI界面,这一篇中,我们介绍一种更为简 ...
- Android 异步更新UI-线程池-Future-Handler实例分析
前言: 我们在开发Android过程中,在处理耗时任务和UI交互的过程中,都会将耗时任务放到子线程处理并刷新. 下面我提出的两个问题,相信大多数开发者都会碰到: 1. 数据经常需要读取更新,并且比较耗 ...
- 使用AsyncTask异步更新UI界面及原理分析
概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...
- Android多线程更新UI的方式
Android下,对于耗时的操作要放到子线程中,要不然会残生ANR,本次我们就来学习一下Android多线程更新UI的方式. 首先我们来认识一下anr: anr:application not rep ...
- Winform实现多线程异步更新UI(进度及状态信息)
引言 在进行Winform程序开发需要进行大量的数据的读写操作的时候,往往会需要一定的时间,然在这个时间段里面,界面ui得不到更新,导致在用户看来界面处于假死的状态,造成了不好的用户体验.所以在大量数 ...
- Flutter学习笔记(31)--异步更新UI
如需转载,请注明出处:Flutter学习笔记(31)--异步更新UI 大家都知道,子线程不能操作UI控件,在我们Android的日常开发中,经常会遇到网络请求数据通过线程间通信,将数据发送到UI线程中 ...
- handler机制和异步更新UI页面
Android 提供了Handler和Looper来满足线程之间的通行,Handler是先进先出原则,Looper类用来管理特定线程内对象之间的消息互换,也可以使用Runnable来完成页面异步更新 ...
随机推荐
- 前端项目课程7 banner设计注意事项
前端项目课程7 banner设计注意事项 一.总结 一句话总结: 1.每个部分的里面的部分可以用相同的名字么,如何修改样式呢? 可以, 用模块名 + 比如上中下(top middle bottom) ...
- thinkphp3.1课程 1-2 thinkphp中入口文件的实质是什么
thinkphp3.1课程 1-2 thinkphp中入口文件的实质是什么 一.总结 一句话总结:在thinkphp中,我们访问的始终是入口文件,并没有主动去访问任何一个其他文件,只不过在入口文件体内 ...
- [array] leetCode-26. Remove Duplicates from Sorted Array - Easy
26. Remove Duplicates from Sorted Array - Easy descrition Given a sorted array, remove the duplicate ...
- MacBook Touch Bar 使用技巧
MacBook Touch Bar 使用技巧 使用Clock Bar再Touch Bar上显示时间 在全屏显示的情况下无法看到时间,于是就想在Touch Bar上是否可以显示时间呢,系统好像没有相应的 ...
- HDU 1248 寒冰王座 完全背包
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1248 中文题,大意就不说了. 第一道完全背包题,跟着背包九讲做的. 和0-1背包的区别在于所不同的是每种 ...
- [机器学习] Coursera ML笔记 - 逻辑回归(Logistic Regression)
引言 机器学习栏目记录我在学习Machine Learning过程的一些心得笔记,涵盖线性回归.逻辑回归.Softmax回归.神经网络和SVM等等.主要学习资料来自Standford Andrew N ...
- POJ 3132 & ZOJ 2822 Sum of Different Primes(dp)
题目链接: POJ:id=3132">http://poj.org/problem?id=3132 ZOJ:http://acm.zju.edu.cn/onlinejudge/show ...
- UVALive - 3977 Summits (BFS染色)
题目大意:坑爹的题目.题意那么难理解. 讲的就是,假设该点是山顶的话(高度为h).那么以该点为中心,往外辐射.走高度大于h-d的点,到达不了还有一个比它高的点 这就提示了,高度要从大到小排序,依次以高 ...
- MRTG Monitoring with ESXi Hosted Guest Return ‘interface is commented * has no ifSpeed property’
MRTG Monitoring with ESXi Hosted Guest Return ‘interface is commented * has no ifSpeed property’ Rec ...
- CEPH OBJECTSTORE API介绍
Thomas是本人在Ceph中国社区翻译小组所用的笔名,该文首次公布在Ceph中国社区.现转载到本人博客,以供大家传阅 CEPH OBJECTSTORE API介绍 本文由 Ceph中国社区-Thom ...