AsynTask 使用解析

       Android开发中往往要使用多线程操作,通常会进耗时操作放在单独的线程中执行,避免其占用主线程,但是子线程无法操作主线程,在子线程操作主线程会出错误。Android提供了Handle类来实现子线程更新UI线程,但是代码会显得臃肿,使用AsynTask一个轻量级的异步任务,任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦,会使代码清爽很多。
       如果每个任务都要创建一个线程,那么应用程序的效率要低很多,并且线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负,为了解决以上的问题,Android在1.5版本引入了AsyncTask。AsyncTask是使用java.util.concurrent框架来管理线程以及并发任务的执行,concurrent框架是一个非常成熟,高效的框架,经过了严格的测试,因此AsyncTask很好的解决了多线程的问题。

1、AsyncTask的执行步骤

AsyncTask的执行分为四个步骤,每一步都对应执行方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。在任务的执行过程中,这些方法被自动调用执行。
(1) onPreExecute() 当任务执行之前开始调用此方法,可以在这里显示进度对话框。
(2) onProgressUpdate(Progress… values) 当任务执行中调用,此方法在主线程执行,用于显示任务执行的进度。
(3) doInBackground(Params… params) 此方法在后台线程执行(子线程),完成任务的主要工作,通常是一个耗时操作。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
(4) onPostExecute(Result…result) 此方法在主线程执行(UI线程),任务执行的结果作为此方法的参数返回。

2、AsyncTask的泛型类型 Params,Progress和Result。

image
AsyncTask定义了三种泛型类型 Params,Progress和Result。
(1) Params 启动任务执行的输入可变参数,比如HTTP请求的URL,可以指定多个。
(2) Progress 后台任务执行的百分比。在doInBackground()中调用调用publicProgress(Progress…)设置的参数。
(3) Result 后台执行任务最终返回的结果,比如String、object…

3、AsyncTask使用实例

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

下面是从网上获取一个网页在TextViewh中展示源码的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class Activity extends AppCompatActivity {
private EditText url_edt;
private ProgressBar progress;
private TextView content_txt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
url_edt= (EditText) findViewById(R.id.addres_edt);
progress= (ProgressBar) findViewById(R.id.progress);
content_txt= (TextView) findViewById(R.id.content_txt);
findViewById(R.id.do_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getContent();
}
});
}
private void getContent() {
if (url_edt.getText().toString()!=null){
MyTask myTask=new MyTask();
//execute()在UI线程中执行
myTask.execute(url_edt.getText().toString().trim());
}else {
Toast.makeText(this,"请输入地址",Toast.LENGTH_SHORT).show();
}
}
class MyTask extends AsyncTask<String,Integer,Object>{
private static final String TAG = "MyTask";

//1、当任务执行之前开始调用此方法,可以在这里显示进度对话框。
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG,"onPreExecute");
progress.setVisibility(View.VISIBLE);
}
//2、当任务执行中调用,此方法在主线程执行,用于显示任务执行的进度。
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.d(TAG,"onProgressUpdate-------->"+values[0]);
progress.setProgress(values[0]);
}
//3、此方法在后台线程执行(子线程),完成任务的主要工作,通常是一个耗时操作。
// 在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
@Override
protected Object doInBackground(String... params) {
Log.d(TAG,"doInBackground");
//获取网络数据
Object s = getData(params[0]);
if (s != null){
return s;
}
return null;
}
//4、此方法在主线程执行(UI线程),任务执行的结果作为此方法的参数返回。
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
Log.d(TAG,"onPostExecute------>"+o.toString());
content_txt.setText(o.toString());
}
@Nullable
private Object getData(String param) {
try {
URL url=new URL(param);
HttpURLConnection httpUrlConnection= (HttpURLConnection) url.openConnection();
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setDoInput(true);
// 请求不使用缓存
httpUrlConnection.setUseCaches(false);
// 设定传送的内容类型是可序列化的java对象
// (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)
httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");

// 要求http请求不要gzip压缩,如果是gzip获取到响应长度是-1
httpUrlConnection .setRequestProperty("Accept-Encoding", "identity");
long length = httpUrlConnection.getContentLength();

// 设定请求的方法为"POST",默认是GET
httpUrlConnection.setRequestMethod("GET");
//连接,从上述第2条中url.openConnection()至此的配置必须要在connect之前完成,
httpUrlConnection.connect();
InputStream is=httpUrlConnection.getInputStream();
//判断是否正常响应数据
if (httpUrlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
System.out.println("网络错误异常!!!!");
return false;
}
Log.d(TAG,"getContentLength---------->"+length);
String s = null;
if(is != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int ch = -1;
int count = 0;
while((ch = is.read(buf)) != -1) {
baos.write(buf, 0, ch);
count += ch;
if(length > 0) {
// 如果知道响应的长度,调用publishProgress()更新进度
publishProgress((int) ((count / (float) length) * 100));
}
// 让线程休眠100ms
Thread.sleep(500);
}
s = new String(baos.toByteArray());
}
// 返回结果
return s;
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
}
}