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;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/**
* params 这个是异步任务执行需要的参数 Progress 异步任务执行的进度 Result 异步任务执行的返回值.
* .execute(); 通知异步任务去执行相应的操作
*/
new AsyncTask<Void, Void, Void>() {
/**
* 在异步的后台任务执行之前调用的方法
* 这个方法是运行在主线程里面的.
*/
@Override
protected void onPreExecute() {
pd = new ProgressDialog(DemoActivity.this);
pd.setMessage("正在执行....");
pd.show();
super.onPreExecute();
}
/**
* 在异步的后台任务执行之后调用的方法
* 这个方法是运行在主线程里面的.
*/
@Override
protected void onPostExecute(Void result) {
pd.dismiss();
super.onPostExecute(result);
}
/**
* 在后台执行的方法,方法是运行在子线程里面的
*/
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}.execute();
}
}