一、线程异步简介
基于移动客户端的软件特别强调实时性,Android程序更是如此,任何一个程序超过5s没有响应,都会被系统强制杀掉。而且Android也不允许在UI线程中进行任何网络操作,否则就会产生NetworkOnMainThreadException异常。因此,凡是耗时的操作,都不应该直接出现在UI线程中。今天,我通过最简单直观地示例总结下Android开发中最常用的两种处理耗时操作的方法:一个是Handler,另一个是AsyncTask,进行详细的讲解android中异步的方式。
二、Handler方法
Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。本篇将通过两种方式实现Handler消息传递,实现异步方式更新操作。
1.在主线程中操作
在Android Handler中提供了几种方法通过异步的方式访问UI线程
Handler.post():把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行
/**
* Post方法
* 把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行
*/
mBtnPost.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
/**
* 此处直接写setmText无用
* 无法在子线程中访问更新UI操作,必须在主线程中更新UI
* 使用post方法修改UI主键属性
*/
//mText.setText("Handler.post");
handler.post(new Runnable() {
@Override
public void run() {
mText.setText("Handler.post-->" + Thread.currentThread().getName());
}
});
}
}).start();
}
});
Handler.postDelayed():把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟后执行。
/**
* PostDelayed方法
* 把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟后执行。
*/
mBtnPostDelayTime.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
mText.setText("Handler.pistDelayed,3s-->" + Thread.currentThread().getName());
}
},3000);
}
}).start();
}
});
Handler.postAtTime():把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。uptimeMillis参数首先需要获取当前的时间才与计算。
/**
* PostAtTime
* 把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
* uptimeMillis参数首先需要获取当前的时间才与计算
* 事件:延迟3s显示Text
*/
mBtnAtTime.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
handler.postAtTime(new Runnable() {
@Override
public void run() {
for(int count = 1; count < 3; count++){
mText.setText("PostAtTime" + count);
}
}
},android.os.SystemClock.uptimeMillis() + 3000);
}
}).start();
}
});
Handler.removeCallbacks():removeCallbacks移除在队列中的消息
/**
* removeCallbacks移除在队列中的消息
* 参数为线程名
*/
mBtnRemoveCallbacks.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//handler.removeCallbacks(runnable);
}
});
Android的线程异步处理机制:Handler对象维护一个线程队列,有新的Runnable送来(post())的时候,把它放在队尾,而处理 Runnable的时候,从队头取出Runnable执行。当向队列发送一个Runnable后,立即就返回,并不理会Runnable是否被执行,执行是否成功等。而具体的执行则是当排队排到该Runnable后系统拿来执行的。这样,就实现了Android的异步处理机制。
注意:该方式并没有另开线程,而是在主线程中运行。因为该种方法并没有实现真正的异步,只是用post的方法实现了消息的传递。下面讲解另外开线程实现异步方法。
2.另开线程操作
使用new Thread()创建新线程,Handler和Looper方法进行消息传递及接收信息。
package example.com.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.widget.TextView;
/**
* author Jimmy.li
* Date: 2016-07-11
* Time: 11:11
* version V1.0
*/
public class HandlerTest extends Activity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handlertest);
mTextView = (TextView) this.findViewById(R.id.textview);
//开启另一个线程
HandlerThread mHandlerThread = new HandlerThread("HandlerThread");
mHandlerThread.start();
//进行消息传递
myHandlerTest myHandlerTest = new myHandlerTest(mHandlerThread.getLooper());
Message msg = myHandlerTest.obtainMessage();
msg.sendToTarget();
}
//Handler方法接收消息
class myHandlerTest extends Handler{
public myHandlerTest(){
}
public myHandlerTest(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mTextView.setText("线程名:" + Thread.currentThread().getName());
}
}
}
三、AsyncTask方法
AsyncTask是一个工具类,顾名思义异步执行任务。用于处理一些后台的比较耗时的任务,给用户带来良好用户体验,从编程的语法上显得优雅了许多,不再需要子线程和Handler就可以完成异步操作并且刷新用户界面。
AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,开发者需要实现一个或几个方法。在任务的执行过程中,这些方法被自动调用。
1.doInBackground(Params...),将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
protected Integer doInBackground(Void... params) {
int i = 0;
while (i<100){
SystemClock.sleep(250);
i++;
if(i % 5 == 0){
publishProgress(i);
}
}
return i;
}
2.onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
//完成更新UI操作
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
textView.setText(values[0] + "%已完成");
}
3.onPostExecute(Result),在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
//结束时调用
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
textView.setText(integer + "%,全部完成");
}
Good luck!
Write by Jimmy.li