Thread 是一个并发的执行单元,在java中实现线程一是通过继承Thread类,二是实现Ruunable接口,当开始一个新线程的时候必须call start(),但是线程并不是马上就run起来,而是注册到JVM的线程调度器中准备run。
涉及对象:主线程和新启动的线程。
Handler的做要作用,主要接受子线程发送的数据,然后用这个数据去更新主线程的UI
解析:
每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程,由于android采用的是UI单线程模型,所以只能在UI主线程中来管理界面中的UI控件,进行时间分发,如点击一个Button的时候将分发一个事件响应函数来操作,这个过程将很短暂不是长时间的操作。但是如果是一些耗时的操作比如读取网页数据,不能在主线程中实现,不然的话就因为长时间不能响应而出现假死现象,如果5秒钟还没有完成的话,Android系统就将发出一个”强制关闭“的错误提示。
所以我们将耗时操作放在一个子线程中,因为子线程涉及到更新UI。Android 的主线程不是线程安全的,也就是说要实现更新UI只能在主线程中更新,在子线程中操作时危险的,所以Android中就定义了一个Handler类来处理该问题。
Handler对象定义在UI主线程中,子线程和主线程要通信就需要发送Message,然后处理Message中带有的数据,这个时候Handler就承担接受子线程传递过来的Meaasge对象,然后把message放入主线程队列中,来更新主线程的UI。
说的通俗一点就是Handler就是在各个线程之间的一个中转数据处理站的作用。
编程模型:
public class myActivity extends Activity {
//定义其他UI控件
private Handler myHandler = new Handler() {
public void handlerMessage(Message msg) {
//实现消息处理函数
}
};
//定义一个子线程, 在子线程中进行计算处理或者逻辑处理
//将处理的结果封装成Message实例然后传递给主线程,在handlerMessage()
函数中主要处理就是接受子线程发送过来的Message,然后进行功能处理。
}
过程详解:
非UI线程发送Message到UI线程分量两个步骤:
UI线程-----------------------------------------------------------------非UI线程
1)定义Handler对象----------------------------------------------------1)创建线程
2)实现handlerMessage()处理函数---------------------------------2)在run()方法中实现要实现的功能
......................------------------------------------------------------------3)处理结果封装成Message
3)handler对象获取Message,也就是将message发送到了handler的消息队列中了
Message:消息,包含消息ID,消息处理对象以及处理的数据等,有MessageQueue统一列队,然后最终每个Message消息有Handler来处理。
Handler:发送消息,接受消息,然后重写handleMessage(Message msg)消息来处理Message。
MessageQueue:消息队列,用来存放Handler发送过来的消息,将Message添加到队列的末尾,然后这些消息被Looper抽取出来,然后由Handler来处理。
Looper:不断的从MessageQueue中抽取出来Message来执行,所以一个MessageQueue需要有一个Looper。
Looper和MessageQueue是一一对应的关系,而多个Handler可以共享MessageQueue和Looper,当然这些Handler也就运行在同一个线程里面。
Looper.prepare():函数用来封装android线程中的消息循环,默认情况下一个线程是不存在消息循环的,需要调用Loop.prepare()来给线程创建一个消息循环,调用Looper.loop()来让MessageQueue中的消息循环起来,取出消息,交给handler处理。常用的方法是:
public class myThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
handler = new Handler() {
//消息处理函数
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
//获取消息然后处理
switch(msg.what) {
case MSG : System.out.println("the message is:" + msg.obj);
//Log.d("the recived message is :", (String) msg.obj);
}
}
};
Looper.loop();
}
Note: 在Looper.loop()后的代码不会被立即执行,当调用mHandler.getLooper().quit()方法后,loop才会中止,然后之后的代码才会运行。一个线程只能对应一个Looper。
AsyncTask
AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程和UI线程交互的辅助类。它的实现是通过一个内部的线程池,每个后台任务装载到线程池中的一个线程,然后在执行该线程。AsyncTask抽象出后台线程运行的五个状态:1,准备运行。2,正在后台运行。3,进度更新。4,完成后台任务。5,取消任务。
AsyncTask是一个一个泛型类,原型为:android.os.AsyncTask<Params, Progress, Result>
Params:启动任务执行的输入参数,比如Http请求的URL。
Progress:后台任务执行的百分比。
Result:后台执行任务的最终返回结果,不如String。
要实现的方法:
onPreExecute()------->准备阶段:该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
doInBackground(Params...)------>正在后台运行:该回调函数由后台线程在onPreExecute()方法执行结束后立即调用。通常在这里执行耗时的后台计算。计算的结果必须由该函数返回,并被传递到onPostExecute()中。在该函数内也可以使用publishProgress(Progress...)来发布一个或多个进度单位(unitsof progress)。这些值将会在onProgressUpdate(Progress...)中被发布到UI线程。
onProgressUpdate(Progress...)--------->进度更新:该函数由UI线程在publishProgress(Progress...)方法调用完后被调用。一般用于动态地显示一个进度条。
onPostExcute(Result)-------->完成后台任务:当后台计算结束后调用。后台计算的结果会被作为参数传递给这一函数。
onCancelled()-------------->取消任务:在调用AsyncTask的cancel()方法时调用
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
1) Task的实例必须在UI thread中创建
2) execute方法必须在UI thread中调用
3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法
4) 该task只能被执行一次,否则多次调用时将会出现异常
doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。
AsyncTask的本质是一个线程池,然后提交的异步任务将装载在一个线程中然后执行,当工作的线程需要更UI线程交互时,
总结:Handler和AsyncTask都是为了主线程不被阻塞UI线程,但是UI的更新只能在UI主线程这个完成这是因为Android的单线程模型,所以要异步的完成。