之前做过一个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. PHP 获取目录

    取得当前文件名,当前目录,上层目录 文件名 test.php 路径 + 文件名 (要取得 /var/www/test/test.php)      echo __FILE__; 文件名 (要取得 te ...

  2. LINUX单网卡绑定多个IP

    在linux下,我们有时候需要给单网卡设置不同的IP地址,这样就涉及到单网卡绑定多个IP地址的情况.使用本方法可以方便的为单网卡绑定多个IP地址.笔者使用的环境是centos5.6,应该在fedora ...

  3. Java中swap解惑

    直接上代码…… public class Swap { public static void main(String[] args) { int a[] = new int[]{1,2}; Syste ...

  4. Kaggle Bike Sharing Demand Prediction – How I got in top 5 percentile of participants?

    Kaggle Bike Sharing Demand Prediction – How I got in top 5 percentile of participants? Introduction ...

  5. ExtJs3常用控件操作实例

    结合工作内容,不定期更新.这里面可能会讲到一些常用的组件的操作. json: { "total": 30, "data": [{ "funcAlign ...

  6. 总结iOS 8和Xcode 6的各种坑

    模拟器的路径从之前的~/Library/Application Support/iPhone Simulator移动到了~/Library/Developer/CoreSimulator/Device ...

  7. Webpack 傻瓜式指南(一)

    modules with dependencies   webpack   module bundler   static  assetss   .js .js .png Webpack傻瓜式指南 n ...

  8. Static Final用法

    一.final数据 在 java编程语言中,有时候需要告知编译器一段数据是不变的编译期常量.对于这种情况,编译器可以将此常量值带入需要用到它的计算式子当中,这种在编译时 执行计算式的方法减轻了运行时的 ...

  9. 14.5.3 Locks Set by Different SQL Statements in InnoDB

    14.5.3 Locks Set by Different SQL Statements in InnoDB 通过不同的SQL语句设置的锁 在InnoDB中 一个锁定读, 一个UPDATE 或者一个D ...

  10. CodeForce 2A Winner

    很多人玩一个游戏,每一轮有一个人得分或者扣分,最后分数最高的人夺冠:如果最后有多个人分数都是最高的,则这些人里面,在比赛过程中首先达到或者超过这个分数的人夺冠.现在给定最多1000轮每轮的情况,求最后 ...