之前做过一个Android采集心电图数据的程序,那才是真正的多线程,之前写的小程序:比如下载个文件,从socket接受大一点的数据流然后在ui上更新进度,我都感觉这就叫做多线程了,其实这啥都不算,用个handler就解决问题了。而当你采集的时候情况就不同了,首先你要从硬件驱动中读取数据,另外数据需要缓存,缓存的同时还要将数据发送到远程服务器,另外还得将数据进行跳帧处理,以方便设备的屏幕上显示起来不那么卡,还要不断的更新ui界面上的绘图。起初的时候对这一连串的多线程真的是弄得手忙脚乱,后来才发现更新ui界面原来不只有handler一种方式,还有其他的,下面就总结如下:

1.利用Looper更新UI界面

这就是我们常用的handler方式

在Main主线程中新开一个线程,该线程负责数据的更新,然后将更新后的数据放在Message里面,然后通过Handler传递给相应的UI进行更新。

public class MainActivity extends Activity {
private Button mButton;
private TextView mText; @SuppressLint("HandlerLeak")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mButton = (Button)this.findViewById(R.id.button);
mText = (TextView)this.findViewById(R.id.text); final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
if(msg.what == 1){
mText.setText("更新后");
}
}
}; mText.setText("更新前");
final Thread thread = new Thread(new Runnable(){ @Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
} });
mButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
thread.start();
}
});
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} }

  

Handler的工作机制。

Handler的作用就是两个:在新启动的线程中发送消息和在主线程中获取和处理消息。像是上面例子中的Handler就包含了这两个方面:我们在新启动的线程thread中调用Handler的sendMessage()方法来发送消息。发送给谁呢?从代码中可以看到,就发送给主线程创建的Handler中的handleMessage()方法处理。这就是回调的方式:我们只要在创建Handler的时候覆写handleMessage()方法,然后在新启动的线程发送消息时自动调用该方法。

2.AsyncTask利用线程任务异步更新UI界面

AsyncTask的原理和Handler很接近,都是通过往主线程发送消息来更新主线程的UI,这种方式是异步的,所以就叫AsyncTask。使用AsyncTask的场合像是下载文件这种会严重阻塞主线程的任务就必须放在异步线程里面:

 public class MainActivity extends Activity {
private Button mButton;
private ImageView mImageView;
private ProgressBar mProgressBar; @SuppressLint("HandlerLeak")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mButton = (Button) this.findViewById(R.id.button);
mImageView = (ImageView) this.findViewById(R.id.image);
mProgressBar = (ProgressBar) this.findViewById(R.id.progressBar);
mButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
AsyncTaskThread thread = new AsyncTaskThread();
thread.execute("http://g.search2.alicdn.com/img/bao/uploaded/i4/"
+ "i4/12701024275153897/T1dahpFapbXXXXXXXX_!!0-item_pic.jpg_210x210.jpg");
}
});
} class AsyncTaskThread extends AsyncTask<String, Integer, Bitmap> { @Override
protected Bitmap doInBackground(String... params) {
publishProgress(0);
HttpClient client = new DefaultHttpClient();
publishProgress(30);
HttpGet get = new HttpGet(params[0]);
final Bitmap bitmap;
try {
HttpResponse response = client.execute(get);
bitmap = BitmapFactory.decodeStream(response.getEntity()
.getContent());
} catch (Exception e) {
return null;
}
publishProgress(100);
return bitmap;
} protected void onProgressUpdate(Integer... progress) {
mProgressBar.setProgress(progress[0]);
} protected void onPostExecute(Bitmap result) {
if (result != null) {
Toast.makeText(MainActivity.this, "成功获取图片", Toast.LENGTH_LONG)
.show();
mImageView.setImageBitmap(result);
} else {
Toast.makeText(MainActivity.this, "获取图片失败", Toast.LENGTH_LONG)
.show();
}
} protected void onPreExecute() {
mImageView.setImageBitmap(null);
mProgressBar.setProgress(0);
} protected void onCancelled() {
mProgressBar.setProgress(0);
}
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}

AsyncTask是为了方便编写后台线程与UI线程交互的辅助类,它的内部实现是一个线程池,每个后台任务会提交到线程池中的线程执行,然后通过向UI线程的Handler传递消息的方式调用相应的回调方法实现UI界面的更新。

AsyncTask的构造方法有三个模板参数:Params(传递给后台任务的参数类型),Progress(后台计算执行过程中,进度单位(progress units)的类型,也就是后台程序已经执行了百分之几)和Result(后台执行返回的结果的类型)。

 protected Bitmap doInBackground(String... params) {
publishProgress(0);
HttpClient client = new DefaultHttpClient();
publishProgress(30);
HttpGet get = new HttpGet(params[0]);
final Bitmap bitmap;
try {
HttpResponse response = client.execute(get);
bitmap = BitmapFactory.decodeStream(response.getEntity()
.getContent());
} catch (Exception e) {
return null;
}
publishProgress(100);
return bitmap;
}

params是一个可变参数列表,publishProgress()中的参数就是Progress,同样是一个可变参数列表,它用于向UI线程提交后台的进度,这里我们一开始设置为0,然后在30%的时候开始获取图片,一旦获取成功,就设置为100%。中间的代码用于下载和获取网上的图片资源

protected void onProgressUpdate(Integer... progress) {
mProgressBar.setProgress(progress[0]);
}

onProgressUpdate()方法用于更新进度条的进度。

 protected void onPostExecute(Bitmap result) {
if (result != null) {
Toast.makeText(MainActivity.this, "成功获取图片", Toast.LENGTH_LONG).show();
mImageView.setImageBitmap(result);
} else {
Toast.makeText(MainActivity.this, "获取图片失败", Toast.LENGTH_LONG).show();
}
}

onPostExecute()方法用于处理Result的显示,也就是UI的更新。

protected void onPreExecute() {
mImageView.setImageBitmap(null);
mProgressBar.setProgress(0);
} protected void onCancelled() {
mProgressBar.setProgress(0);
}

这两个方法主要用于在执行前和执行后清空图片和进度。
      最后我们只需要调用AsyncTask的execute()方法并将Params参数传递进来进行。完整的流程是这样的:

UI线程执行onPreExecute()方法把ImageView的图片和ProgressBar的进度清空,然后后台线程执行doInBackground()方法,千万不要在这个方法里面更新UI,因为此时是在另一条线程上,在使用publishProgress()方法的时候会调用onProgressUpdate()方法更新进度条,最后返回result---Bitmap,当后台任务执行完成后,会调用onPostExecute()方法来更新ImageView。

AsyncTask本质上是一个静态的线程池,由它派生出来的子类可以实现不同的异步任务,但这些任务都是提交到该静态线程池中执行,执行的时候通过调用doInBackground()方法执行异步任务,期间会通过Handler将相关的信息发送到UI线程中,但神奇的是,并不是调用UI线程中的回调方法,而是AsyncTask本身就有一个Handler的子类InternalHandler会响应这些消息并调用AsyncTask中相应的回调方法。从上面的代码中我们也可以看到,UI的ProgressBar的更新是在AsyncTask的onProgressUpdate(),而ImageView是在onPostExecute()方法里。这是因为InternalHandler其实是在UI线程里面创建的,所以它能够调用相应的回调方法来更新UI。

AsyncTask就是专门用来处理后台任务的,而且它针对后台任务的五种状态提供了五个相应的回调接口,使得我们处理后台任务变得非常方便。

如果只是普通的UI更新操作,像是不断更新TextView这种动态的操作,可以使用Handler,但如果是涉及到后台操作,像是下载任务,然后根据后台任务的进展来更新UI,就得使用AsyncTask,但如果前者我们就使用AsyncTask,那真的是太大材小用了!!

3.利用Runnable更新UI界面

剩下的方法都是围绕着Runnable对象来更新UI。

一些组件本身就有提供方法来更新自己,像是ProgressBar本身就有一个post()方法,只要我们传进一个Runnable对象,就能更新它的进度。只要是继承自View的组件,都可以利用post()方法,而且我们还可以使用postDelay()方法来延迟执行该Runnable对象。android的这种做法就真的是让人称道了,至少我不用为了一个ProgressBar的进度更新就写出一大堆难懂的代码出来。

还有另一种利用Runnable的方式:Activity.runOnUiThread()方法。这名字实在是太直白了!!使用该方法需要新启一个线程:

 class ProgressThread extends Thread {
@Override
public void run() {
super.run();
while (mProgress <= 100) {
runOnUiThread(new Runnable() { @Override
public void run() {
mProgressBar.setProgress(mProgress);
mProgress++;
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
}
}

4.总结

1.如果只是单纯的想要更新UI而不涉及到多线程的话,使用View.post()就可以了;

2.需要另开线程处理数据以免阻塞UI线程,像是IO操作或者是循环,可以使用Activity.runOnUiThread();

3.如果需要传递状态值等信息,像是蓝牙编程中的socket连接,就需要利用状态值来提示连接状态以及做相应的处理,就需要使用Handler + Thread的方式;

4.如果是后台任务,像是下载任务等,就需要使用AsyncTask。

 

Android更新UI的几种方式的更多相关文章

  1. Android 更新UI的几种方式

    1.Activity的 runOnUiThread textView = (TextView) findViewById( R.id.tv ); new Thread(new Runnable() { ...

  2. Android异步更新UI的四种方式

    Android异步更新UI的四种方式 2015-09-06 09:23 segmentfault 字号:T | T 大家都知道由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中 ...

  3. UI的线程问题:单线程原因及更新UI的四种方式

    1.UI线程为什么设计为单线程? UI控件的操作不是线程安全的,对于多线程并发访问的时候,如果使用加锁机制会导致: UI控件的操作变得很复杂. 加锁的操作必定会导致效率下降. 所以android系统在 ...

  4. 更新UI的几种方式

    在学习Handler的过程中牵涉到UI的更新,在这里就总结一下更新UI的四种方式吧,用法都比较简单,直接看代码就可以了. 一.使用Handler的post方法 新建项目,修改MainActivity代 ...

  5. Android通过子线程更新UI的几种方式

    一般情况下,UI的更新都少不了Handler,首先我们先了解一下Handler机制: Handler消息机制 定义 Message 线程间通信的数据单元,可通过message携带需要的数据创建对象:M ...

  6. Android开发更新UI的几种方式

    1.runOnUiThread 2.handler post 3.handler sendmessage 4.view post xml布局文件: <RelativeLayout xmlns:a ...

  7. Android:在子线程中更新UI的三种方式

    ①使用Activity中的runOnUiThread(Runnable) ②使用Handler中的post(Runnable) 在创建Handler对象时,必须先通过Context的getMainLo ...

  8. 转:探讨android更新UI的几种方法

    本文转自:http://www.cnblogs.com/wenjiang/p/3180324.html 作为IT新手,总以为只要有时间,有精力,什么东西都能做出来.这种念头我也有过,但很快就熄灭了,因 ...

  9. 【转】探讨android更新UI的几种方法----不错

    原文网址:http://www.cnblogs.com/wenjiang/p/3180324.html 作为IT新手,总以为只要有时间,有精力,什么东西都能做出来.这种念头我也有过,但很快就熄灭了,因 ...

随机推荐

  1. JQUERY1.9学习笔记 之可见性过滤器(一) 隐藏选择器

    描述:选择所有隐藏的元素. jQuery( ":hidden" ) 例:显示出所有隐藏的div元素,并对隐藏的input元素计数. <!doctype html>< ...

  2. WPF学习笔记-如何按ESC关闭窗口

    如何按ESC关闭窗口? 在InitializeComponent();下面增加KeyDown事件,如: public ModifyPrice() { InitializeComponent(); th ...

  3. javascript summary

    Client Javascript HTML5: http://www.html5rocks.com/en/ Libraray: JQuery, JQuery Mobile, Zepto, MoolT ...

  4. Unity3d场景合并

    Unity3d场景合并 一.Unity3d场景合并,有一次的情况是这样的,就是我们是每个人都在开发,每个人有不同的场景,那么合并的时候,有些会出问题,那么我有一个好的方案,就是首先弄一个公共的资源库, ...

  5. Labeling Balls

    poj3687:http://poj.org/problem?id=3687 题意:有N个重量1到N的点,把这N个点涂色,要求在一定的约束下颜色a必须比颜色b要轻,如果有多种选择则让重量最小的对应编号 ...

  6. H3C S5500上层接路由,VLAN IP作网站配置实例

    # version 5.20, Release 2208 # sysname S5500-1 # clock timezone #Web#8#01 add 08:00:00 # super passw ...

  7. 信号槽的被连接几次,就会执行几次(有空要仔细研究connect的各种用法)

    所以connect一定要做一次连接即可.否则点击一下按钮,会不断弹出多次窗口. 另外,也不用管这个对象有没有被实例化,connect都不会出错.

  8. 用Java实现非阻塞通信

    用ServerSocket和Socket来编写服务器程序和客户程序,是Java网络编程的最基本的方式.这些服务器程序或客户程序在运行过程中常常会阻塞.例如当一个线程执行ServerSocket的acc ...

  9. 使用CSS为图片添加边框的几种方法

    css的应用十分广泛,即便用在图片的效果中也是方法多样,本文下面就介绍五种为图片添加特殊效果边框的CSS写法阴影效果 通过使用带有一些padding之的背景图来添加阴影效果. HTML <img ...

  10. ZOJ-2112-Dynamic Rankings(线段树套splay树)

    题意: 完成两个操作: 1.询问一个区间里第k小的数: 2.修改数列中一个数的值. 分析: 线段树套平衡树,线段树中的每个节点都有一棵平衡树,维护线段树所记录的这个区间的元素.这样处理空间上是O(nl ...