我想关于这个话题已经有很多前辈讨论过了。今天算是一次学习总结吧。

在android的设计思想中,为了确保用户顺滑的操作体验。一 些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务。因此我们必须要重新开启一个后台线程运行这些任务。然而,往往这些任务最终又会直接或者 间接的需要访问和控制UI控件。例如访问网络获取数据,然后需要将这些数据处理显示出来。就出现了上面所说的情况。原本这是在正常不过的现象了,但是 android规定除了UI线程外,其他线程都不可以对那些UI控件访问和操控。为了解决这个问题,于是就引出了我们今天的话题。Android中后台线 程如何与UI线程交互。

据我所知android提供了以下几种方法,用于实现后台线程与UI线程的交互。

1、handler

2、Activity.runOnUIThread(Runnable)

3、View.Post(Runnable)

4、View.PostDelayed(Runnabe,long)

5、AsyncTask

方法一:handler

handler是android中专门用来在线程之间传递信息类的工具。

要讲明handler的用法非常简单,但是我在这里会少许深入的讲一下handler的运行机制。

为了能够让handler在线程间传递消息,我们还需要用到几个类。他们是looper,messageQueue,message。

这 里说的looper可不是前段时间的好莱坞大片环形使者,他的主要功能是为特定单一线程运行一个消息环。一个线程对应一个looper。同样一个 looper对应一个线程。这就是所谓的特定单一。一般情况下,在一个线程创建时他本身是不会生产他特定单一的looper的(主线程是个特例)。因此我 们需要手动的把一个looper与线程相关联。其方法只需在需要关联的looper的线程中调用Looper.prepare。之后我们再调用 Looper.loop启动looper。

说了这么多looper的事情,到底这个looper有什么用哪。其实之前我们已经说到了,他是 为线程运行一个消息环。具体的说,在我们将特定单一looper与线程关联的时候,looper会同时生产一个messageQueue。他是一个消息队 列,looper会不停的从messageQuee中取出消息,也就是message。然后线程就会根据message中的内容进行相应的操作。

那 么messageQueue中的message是从哪里来的哪?那就要提到handler了。在我们创建handler的时候,我们需要与特定的 looper绑定。这样通过handler我们就可以把message传递给特定的looper,继而传递给特定的线程。在这里,looper和 handler并非一一对应的。一个looper可以对应多个handler,而一个handler只能对应一个looper(突然想起了一夫多妻制,呵 呵)。这里补充一下,handler和looper的绑定,是在构建handler的时候实现的,具体查询handler的构造函数。

在我 们创建handler并与相应looper绑定之后,我们就可以传递message了。我们只需要调用handler的sendMessage函数,将 message作为参数传递给相应线程。之后这个message就会被塞进looper的messageQueue。然后再被looper取出来交给线程 处理。

这 里要补充说一下message,虽然我们可以自己创建一个新的message,但是更加推荐的是调用handler的obtainMessage方法来获 取一个message。这个方法的作用是从系统的消息池中取出一个message,这样就可以避免message创建和销毁带来的资源浪费了(这也就是算 得上重复利用的绿色之举了吧)。

突然发现有一点很重要的地方没有讲到,那就是线程从looper收到message之后他是如何做出响应的 嘞。其实原来线程所需要做出何种响应需要我们在我们自定义的handler类中的handleMessage重构方法中编写。之后才是之前说的创建 handler并绑定looper。

好吧说的可能哟点乱,总结一下利用handler传递信息的方法。

假设A线程要传递信息给B线程,我们需要做的就是

1、在B线程中调用Looper.prepare和Looper.loop。(主线程不需要)

2、 编写Handler类,重写其中的handleMessage方法。

3、创建Handler类的实例,并绑定looper

4、调用handler的sentMessage方法发送消息。

到这里,我们想handler的运行机制我应该是阐述的差不多了吧,最后再附上一段代码,供大家参考。

 1 public class MyHandlerActivity extends Activity {
2 TextView textView;
3 MyHandler myHandler;
4
5 protected void onCreate(Bundle savedInstanceState) {
6 super.onCreate(savedInstanceState);
7 setContentView(R.layout.handlertest);
8
9 //实现创建handler并与looper绑定。这里没有涉及looper与
//线程的关联是因为主线程在创建之初就已有looper
10 myHandler=MyHandler(MyHandlerActivitythis.getMainLooper());
11 textView = (textView) findViewById(R.id.textView);
12
13 MyThread m = new MyThread();
14 new Thread(m).start();
15 }
16
17
18 class MyHandler extends Handler {
19 public MyHandler() {
20 }
21
22 public MyHandler(Looper L) {
23 super(L);
24 }
25
26 // 必须重写这个方法,用于处理message
27 @Override
28 public void handleMessage(Message msg) {
29 // 这里用于更新UI
30 Bundle b = msg.getData();
31 String color = b.getString("color");
32 MyHandlerActivity.this.textView.setText(color);
33 }
34 }
35
36 class MyThread implements Runnable {
37 public void run() {
38 //从消息池中取出一个message
39 Message msg = myHandler.obtainMessage();
40 //Bundle是message中的数据
41 Bundle b = new Bundle();
42 b.putString("color", "我的");
43 msg.setData(b);
44 //传递数据
45 myHandler.sendMessage(msg); // 向Handler发送消息,更新UI
46 }
47 }

方法二:Activity.runOnUIThread(Runnable)

这个方法相当简单,我们要做的只是以下几步

1、编写后台线程,这回你可以直接调用UI控件

2、创建后台线程的实例

3、调用UI线程对应的Activity的runOnUIThread方法,将后台线程实例作为参数传入其中。

注意:无需调用后台线程的start方法

方法三:View.Post(Runnable)

该方法和方法二基本相同,只是在后台线程中能操控的UI控件被限制了,只能是指定的UI控件View。方法如下

1、编写后台线程,这回你可以直接调用UI控件,但是该UI控件只能是View

2、创建后台线程的实例

3、调用UI控件View的post方法,将后台线程实例作为参数传入其中。

方法四:View.PostDelayed(Runnabe,long)

该方法是方法三的补充,long参数用于制定多少时间后运行后台进程

方法五:AsyncTask

AsyncTask是一个专门用来处理后台进程与UI线程的工具。通过AsyncTask,我们可以非常方便的进行后台线程和UI线程之间的交流。

那么AsyncTask是如何工作的哪。

AsyncTask拥有3个重要参数

1、Params

2、Progress

3、Result

Params是后台线程所需的参数。在后台线程进行作业的时候,他需要外界为其提供必要的参数,就好像是一个用于下载图片的后台进程,他需要的参数就是图片的下载地址。

Progress是后台线程处理作业的进度。依旧上面的例子说,就是下载图片这个任务完成了多少,是20%还是60%。这个数字是由Progress提供。

Result是后台线程运行的结果,也就是需要提交给UI线程的信息。按照上面的例子来说,就是下载完成的图片。

AsyncTask还拥有4个重要的回调方法。

1、onPreExecute

2、doInBackground

3、onProgressUpdate

4、onPostExecute

onPreExecute运行在UI线程,主要目的是为后台线程的运行做准备。当他运行完成后,他会调用doInBackground方法。

doInBackground 运行在后台线程,他用来负责运行任务。他拥有参数Params,并且返回Result。在后台线程的运行当中,为了能够更新作业完成的进度,需要在 doInbackground方法中调用PublishProgress方法。该方法拥有参数Progress。通过该方法可以更新Progress的数 据。然后当调用完PublishProgress方法,他会调用onProgressUpdate方法用于更新进度。

onProgressUpdate运行在UI线程,主要目的是用来更新UI线程中显示进度的UI控件。他拥有Progress参数。在doInBackground中调用PublishProgress之后,就会自动调onProgressUpdate方法

onPostExecute运行在UI线程,当doInBackground方法运行完后,他会调用onPostExecute方法,并传入Result。在onPostExecute方法中,就可以将Result更新到UI控件上。

明白了上面的3个参数和4个方法,你要做的就是

1、编写一个继承AsyncTask的类,并声明3个参数的类型,编写4个回调方法的内容。

2、然后在UI线程中创建该类(必须在UI线程中创建)。

3、最后调用AsyncTask的execute方法,传入Parmas参数(同样必须在UI线程中调用)。

这样就大功告成了。

另外值得注意的2点就是,千万不要直接调用那四个回调方法。还有就是一个AsyncTask实例只能执行一次,否则就出错哦。

以上是AsyncTask的基本用法,更加详细的内容请参考android官方文档。最后附上一段代码,供大家参考。

 1 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long>
2 //在这里声明了Params、Progress、Result参数的类型
3 {
4 //因为这里不需要使用onPreExecute回调方法,所以就没有加入该方法
5
6 //后台线程的目的是更具URL下载数据
7 protected Long doInBackground(URL... urls) {
8 int count = urls.length;//urls是数组,不止一个下载链接
9 long totalSize = 0;//下载的数据
10 for (int i = 0; i < count; i++) {
11 //Download是用于下载的一个类,和AsyncTask无关,大家可以忽略他的实现
12 totalSize += Downloader.downloadFile(urls[i]);
13 publishProgress((int) ((i / (float) count) * 100));//更新下载的进度
14 // Escape early if cancel() is called
15 if (isCancelled()) break;
16 }
17 return totalSize;
18 }
19
20 //更新下载进度
21 protected void onProgressUpdate(Integer... progress) {
22 setProgressPercent(progress[0]);
23 }
24
25 //将下载的数据更新到UI线程
26 protected void onPostExecute(Long result) {
27 showDialog("Downloaded " + result + " bytes");
28 }
29 }
30

有了上面的这个类,接下你要做的就是在UI线程中创建实例,并调用execute方法,传入URl参数就可以了。

这上面的5种方法各有优点。但是究其根本,其实后面四种方法都是基于handler方法的包装。在一般的情形下后面四种似乎更值得推荐。但是当情形比较复杂,还是推荐使用handler。

Android中UI线程与后台线程交互设计的5种方法的更多相关文章

  1. Android中UI线程与后台线程交互设计的6种方法

    在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须要重新开启 一个后台线程运行这些任务.然而,往往这些任务最终又会直 ...

  2. C#.Net前台线程与后台线程的区别

    本文来自:http://www.cnblogs.com/zfanlong1314/archive/2012/02/26/2390455.html .Net的公用语言运行时(Common Languag ...

  3. C#夯实基础之多线程二:主线程、前台线程与后台线程

    我们在<C#夯实基础之多线程一:初识多线程>一文中第二部分中指出,既然windows最终发展出了多线程模型,按理说,我们直接使用一个.NetFramework的线程类就可以直接撸代码了,但 ...

  4. java前台线程和后台线程

     main()函数即主函数,是一个前台线程,前台进程是程序中必须执行完成的,而后台线程则是java中所有前台结束后结束,不管有没有完成,后台线程主要用与内存分配等方面.                 ...

  5. C# 前台线程与后台线程的区别和联系

    c#前台线程与后台线程的区别和联系http://www.189works.com/article-13702-1.html 如何取消后台线程的执行http://www.cnblogs.com/shan ...

  6. C# 前台线程与后台线程区别

    using System; using System.Drawing; using System.Windows.Forms; using System.Threading; namespace Wi ...

  7. Android中UI设计的一些技巧!!!

    出处:http://blog.csdn.net/android_tutor/article/details/5995759 大家好,今天给大家分享的是Android中UI设计的一些技巧,本节内容主要有 ...

  8. C#多线程编程实战1.7前台线程和后台线程

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...

  9. [一点一滴.NET]前台线程和后台线程

    前台线程和后台线程就是通过线程实例的属性IsBackground=true or false来区分的. 新建一个线程是默认是后台线程. 前台线程全部执行完之后,才退出进程. 进程退出,所有的后台线程全 ...

随机推荐

  1. java spring一个类型split的方法

    /** * Take a String which is a delimited list and convert it to a String array. * <p>A single ...

  2. oracle10g前期准备

    上午在虚拟机安装了oracle10g,安装比较简单,只是前期工作比较多,如: 在Root用户下执行以下步骤: 1)修改用户的SHELL的限制,修改/etc/security/limits.conf文件 ...

  3. seaJs学习笔记之javascript的依赖问题

    之前分别为大家介绍了有关javascript中的冲突和性能问题,今天为大家介绍一下有关javascript中的依赖问题.我们将继续就之前javascript中性能问题继续介绍. 先来回顾一下性能问题的 ...

  4. sql执行计划解析案例(二)

    sql执行计划解析案例(二)   今天是2013-10-09,本来以前自己在专注oracle sga中buffer cache 以及shared pool知识点的研究.但是在研究cache buffe ...

  5. xcode6下使用autolayout+sizeclass实践

    历史车轮滚滚向前,将autolayout配合sizeclass做布局的方式推上了主流,虽然有点晚,但最终还是进行了一次完整的实践,特此记录一下: 因为网上已经有很多博客介绍了autolayout配合s ...

  6. TCP参数设置

    我们这里应用的是CentOS5.3,并内核使用的是2.6.18-128.el5PAE #1 SMP .修改部分TCP ,有的是为了提高性能与负载,但是存在降低稳定性的风险.有的则是安全方面的配置,则有 ...

  7. android开发的学习路线

    参考资料:千锋3G学院--课程大纲    http://www.mobiletrain.org 看了专业的培训机构的课程大纲,才知道,自己学习android的路途才刚刚开始!特此整理分享一下,希望能帮 ...

  8. spring core源码解读之ASM4用户手册翻译之一asm简介

    第一章:ASM介绍 1.1 ASM动机: 程序的分析,生成,转换技术可以应用到许多场景: 1.程序分析,从简单的语法解析到完整的语义分析,可以应用在程序中找到潜在的bug,发现无用的代码,工程代码的逆 ...

  9. PureMVC(JS版)源码解析(五):SimpleCommand类

          之前我们对PureMVC中涉及到观察者模式的三个基本类(Notification/Observer/Notifier)进行了分析,接下来将对PureMVC源码中的其他类进行分析,首先我们讲 ...

  10. android UI生成器

    可根据选择的效果生成资源 http://jgilfelt.github.io/android-actionbarstylegenerator/#name=example&compat=sher ...