`
jaisok
  • 浏览: 5276 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

UI与线程交互

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。

分享到:
评论

相关推荐

    无界面的UI线程交互

    "无界面的UI线程交互"这个主题探讨的是在没有直接用户界面的情况下,如何实现后台线程与前端UI线程的有效通信。 无界面的UI线程,即不直接显示图形界面的工作线程,通常被用于处理那些需要与UI线程交互但不需要用户...

    Android中UI线程与后台线程交互的探讨.pdf

    【Android UI线程与后台线程交互】 在Android应用开发中,UI线程(也称为主线程)负责处理用户界面的交互,而后台线程则用于执行耗时操作,如网络请求、数据库操作等,以避免阻塞UI,保证用户界面的流畅性。当后台...

    WinForm后台线程与UI线程通讯

    本文将详细探讨如何在WinForm应用中实现在后台线程与UI线程之间的通信,并确保UI更新的安全。 首先,理解线程的概念至关重要。在多线程编程中,主线程通常是创建UI并处理用户交互的线程,而后台线程则用于执行耗时...

    android UI线程和后台交互实例

    android UI线程和后台线程交互,包括多线程之AsyncTask等例子展示。。。。。。。。。。。

    UI 线程 和 工作线程 的实现

    UI线程是应用程序的主要线程,负责处理与用户交互相关的所有事件,如鼠标点击、键盘输入以及窗口的绘制等。而工作线程则用于执行耗时的任务,如大量计算、网络通信或读写文件,以避免阻塞UI线程,保持用户界面的响应...

    UI线程

    总之,UI线程是应用程序与用户交互的核心,理解其工作原理和优化策略对于开发高效、流畅的应用至关重要。开发者需要遵循最佳实践,确保在UI线程之外执行耗时操作,同时利用各种工具进行调试和性能优化。

    c#多线程中子线程动态改变ui控件

    在上面的代码中,我们首先获取UI线程的同步上下文,然后在子线程中使用Post方法异步更新Label控件的Text属性。 C#子线程更新UI控件有两种常用的方法:使用控件自身的Invoke/BeginInvoke方法和使用...

    C# 线程更新UI界面

    特别是在UI(用户界面)开发中,为了保证界面的流畅性,通常我们会将耗时操作放在后台线程进行,而更新UI则需要在主线程(也称为UI线程)中完成。这是因为UI控件不是线程安全的,多个线程并发访问可能导致数据不一致...

    工作线程 UI线程实例

    UI线程主要负责与用户交互,工作线程用来处理耗时任务。通过合理使用如AsyncTask等工具,我们可以确保应用的响应性和性能。在实际开发中,理解并熟练掌握这两者的关系对于构建高质量的应用至关重要。

    Qt中UI线程与子线程的交互

    UI线程与子线程的交互,主要看博主的目录下的 A.使用信号槽(线程安全的) B.使用 QMetaObject::invokeMethod C.使用 QApplication::postEvent ...

    MFC UI 线程的使用

    这样可以确保UI线程在处理用户交互时不会被其他线程打断。使用PostThreadMessage的例子如下: ```cpp UINT WM_CUSTOM_MSG = RegisterWindowMessage("MY_CUSTOM_MESSAGE"); // 在其他线程中 PostThreadMessage...

    WPF 使用线程更新UI

    只有通过Dispatcher,后台线程才能安全地与UI交互。 3. **异步编程模型**:在WPF中,可以使用Task、BackgroundWorker或者异步回调等方式启动后台任务。这些方式可以让程序在等待后台任务完成的同时,继续处理主线程...

    MFC创建UI线程

    在UI线程中,模态对话框(Modal Dialog)会阻止用户与对话框之外的任何其他UI元素进行交互,直到对话框被关闭。模式对话框(Modeless Dialog)则允许用户在显示对话框的同时操作主窗口。创建这两种对话框通常涉及...

    MFC用户界面线程与工作者线程

    因为用户界面的所有交互都需要在这个线程中进行,所以UI线程必须保持高度响应,避免被长时间运行的任务阻塞,以保证良好的用户体验。 工作者线程,又称为后台线程或辅助线程,是在应用程序运行过程中由开发者手动...

    VC 线程 创建 操作 同步 终止 UI线程等各种线程例子

    它们通常用于执行计算密集型任务或长时间运行的任务,以免阻塞UI线程。 8. **线程的挂起和恢复**: - `SuspendThread`函数可挂起一个线程,暂停其执行,而`ResumeThread`函数恢复已挂起线程的执行。 9. **线程的...

    UI界面多线程简单实现和线程捕获异常问题

    主线程通常负责处理用户交互和绘制UI,而后台线程则处理计算密集型任务。在Windows环境下,我们可以使用`System.Threading`命名空间中的`Thread`类创建新线程;在.NET框架中,`Task`类提供了更高级的异步编程模型;...

    WPF后台线程更新UI

    - **线程模型**:WPF应用的主线程是UI线程,负责处理用户交互和绘制UI。后台线程则用于执行非UI任务,如数据加载、计算等。 - **线程安全问题**:由于.NET Framework的UI控件不支持多线程访问,后台线程直接修改UI会...

    Java通信项目之客户端UI实现以及交互

    本文将深入探讨如何使用Java技术来设计和实现用户界面,并确保其与后端通信的有效交互。 首先,我们需要理解客户端UI的基础:Java Swing和JavaFX。Swing是Java标准库的一部分,提供了丰富的组件库,如JFrame、...

Global site tag (gtag.js) - Google Analytics