AsyncTask

来源:互联网 发布:杭州淘宝美工学徒骗局 编辑:程序博客网 时间:2024/06/05 14:11

Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行(也就是主线程中完成)此时如果存在多个子线程同时操作主线程的情况,此时就有可能出现UI加载出现混乱。但是又不能总在主线程中执行耗时的方法,这样也会导致程序出现ANR的异常情况。那么如何解决问题呢?

Android中有消息一说,Message和Handler。

Handler:

1)按计划发送消息或执行某个Runnanble(使用POST方法);  

2)从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程)

在Android中有一种消息循环机制一说(Looper:主要用户循环子线程中向主线程中发消息的循环,遵循先进先出的队列机制),默认情况下,Handler接受的是当前线程下的消息循环实例在实例化Handler的时候,Looper可以是任意线程的,只要有Handler的指针,任何线程也都可以 sendMessage。Handler对于Message的处理是异步的。一个Looper 只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的(handleMessage()方法里不应该有耗时操作,可以将耗时操作放在 其他线程执行,操作完后发送Message(通过sendMessges方法),然后由handleMessage()更新UI)。

//Todo:handler和message是Android中非常重要的部分,后续中必须有详细的总结和说明。

今天看到了另外一个在Android开发中非常重要的处理线程的方法:AsyncTask

Android平台很多应用使用的都是AsyncTask,而并非Thread和Handler去更新UI,它是Android中常见的处理线程问题的重要一项。

首先:从Android 1.5开始系统将AsyncTask引入到android.os包中,过去在很早1.1和1.0 SDK时其实官方将其命名为UserTask,其内部是jdk 1.5开始新增的concurrent库Android UI的刷新google引入了Handler和Looper机制,它们均基于消息实现,有事可能消息队列阻塞或其他原因无法准确的使用(对应以上的部分),推荐大家使用AsyncTask代替Thread+Handler的方式,不仅调用上更为简单,经过实测更可靠一些,Google在Browser中大量使用了异步任务作为处理耗时的I/O操作,比如下载文件、读写数据库等等,它们在本质上都离不开消息,但是 AsyncTask相比Thread加Handler更为可靠,更易于维护。

但是但AsyncTask缺点也是有的比如一旦线程开启即 dobackground方法执行后无法给线程发送消息,仅能通过预先设置好的标记来控制逻辑,当然可以通过线程的挂起等待标志位的改变来通讯,对于某些应用Thread和Handler以及Looper可能更灵活。

所以综上所述:

1  AsyncTask实现的原理,和适用的优缺点

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.

使用的优点:

l  简单,快捷

l  过程可控

使用的缺点:

l  在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.

2 Handler异步实现的原理和适用的优缺点

在Handler 异步实现时,涉及到 Handler, Looper, Message,Thread四个对象,实现异步的流程是主线程启动Thread(子线程)àthread(子线程)运行并生成Message-àLooper获取Message并传递给HandleràHandler逐个获取Looper中的Message,并进行UI变更。

使用的优点:

l  结构清晰,功能定义明确

l  对于多个后台任务时,简单,清晰

AsyncTask与Handler相比,谁更轻量级?
答:通过看源码,发现AsyncTask实际上就是一个线程池,而网上的说法是AsyncTask比handler要轻量级,显然上不准确的,只能这样说,AsyncTask在代码上比handler要轻量级别,而实际上要比handler更耗资源,因为AsyncTask底层是一个线程池!而Handler仅仅就是发送了一个消息队列,连线程都没有开。
但是,如果异步任务的数据特别庞大,AsyncTask这种线程池结构的优势就体现出来了。


AsyncTask的用法:

在Android开发中必须遵循单线程模型的原则:也就是说Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。在单线程模型中始终要记住两条法则: 
1. 不要阻塞UI线程 
2. 确保只在UI线程中访问Android UI工具包

首先了解什么是主线程:当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。 
  Params 启动任务执行的输入参数,比如HTTP请求的URL。 
  Progress 后台任务执行的百分比。 
  Result 后台执行任务最终返回的结果,比如String。

params 这个是异步任务执行需要的参数 Progress 异步任务执行的进度 Result 异步任务执行的返回值.  在下面会有相关解释。

AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。

1) 子类化AsyncTask 
2) 实现AsyncTask中定义的下面一个或几个方法 
onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。

oInBackground(Params...), 将在onPreExecute 方法执行后马上执行(在子线程中执行),该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。

 onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。

onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.(可以在这个方法中执行一些擦屁股的工作,比如dismiss一个Dialog)

为了正确的使用AsyncTask类,以下是几条必须遵守的准则: 
  1) Task的实例必须在UI thread中创建 
  2) execute方法必须在UI thread中调用 
  3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法 
  4) 该task只能被执行一次,否则多次调用时将会出现异常

doInBackground方法返回类型和onPostExecute的参数类型必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数(是不是和 onProgressUpdate(Progress...),接受的参数对应呢),第三个为doInBackground返回和onPostExecute传入的参数。

.execute(); 通知异步任务去执行相应的操作


package cn.itcast.asynctask;import android.app.Activity;import android.app.ProgressDialog;import android.os.AsyncTask;import android.os.Bundle;public class DemoActivity extends Activity {/** Called when the activity is first created. */private ProgressDialog pd;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);/** * params 这个是异步任务执行需要的参数 Progress 异步任务执行的进度 Result 异步任务执行的返回值. * .execute(); 通知异步任务去执行相应的操作 */new AsyncTask<Void, Void, Void>() {/** * 在异步的后台任务执行之前调用的方法 * 这个方法是运行在主线程里面的. */@Overrideprotected void onPreExecute() {pd = new ProgressDialog(DemoActivity.this);pd.setMessage("正在执行....");pd.show();super.onPreExecute();}/** * 在异步的后台任务执行之后调用的方法 * 这个方法是运行在主线程里面的. */@Overrideprotected void onPostExecute(Void result) {pd.dismiss();super.onPostExecute(result);}/** * 在后台执行的方法,方法是运行在子线程里面的 */@Overrideprotected Void doInBackground(Void... params) {try {Thread.sleep(5000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}}.execute();}}


0 0
原创粉丝点击