1 AsyncTask异步任务
1.1 ANR的概念
Application Not Responding,应用程序无响应
Android系统规定只有UI线程(主线程)能够修改UI界面,但是如果在UI线程中执行耗时操作,则会导致线程阻塞,影响用户体验,如果耗时操作导致阻塞时间过长,则有可能会引起系统ANR
产生的原因
用户在界面上的操作是由主线程处理,如果主线程被阻塞,那么就没有办法响应用户的操作,这时候可以看做是程序就“卡住了”,如果阻塞超过5秒,就会导致ANR。
如何避免ANR?
我们只需要将会造成主线程阻塞的操作(耗时操作)放在子线程中就可以了。
注意事项
1、超过5秒没有响应用户操作才会导致ANR,那么是不是5秒之内的耗时操作就可以在主线程中执行?
不是的,即使没有导致ANR也会影响用户的体验,一般200ms以上的操作都属于耗时操作应该放在子线程中执行
1、 ANR的产生不只是因为主线程被阻塞,根本的原因是程序没有响应用户的操作超过5秒,才会导致ANR。
但是,Android系统规定,和界面UI更新相关的操作必须在主线程中执行,如果在子线程中对UI界面进行更新操作,就会抛出异常,导致程序奔溃。
异常信息:android.view.ViewRootImpl$CalledFromWrongThreadException: Only theoriginal thread that created a view hierarchy can touch its views.
问题:执行耗时操作需要在子线程中完成,但是子线程不能更新UI,怎么办?
答:Android提供了消息机制可能解决这个问题,原理就是子线程中如果需要更新UI,就“通知”主线程更新UI
Android提供了以下方式解决上面的问题:
1、Handler消息机制(后面讲)
2、AsyncTask异步任务
1.2 Android系统中线程的概念
【
MainThread 主线程(UI线程):应用启动时创建,处理与UI相关事情,如点击事件、数据更新
WorkThread 子线程(工作线程):Android 4.0之后UI线程不能访问网络资源或执行耗时操作,必须开启子线程
】
1.3 AsyncTask类的基本使用
【
Android提供的异步任务类,主要作用是可以在子线程中执行耗时操作,并通知主线程更新UI
功能
在类中实现异步操作,并提供回调方法反馈当前异步执行的程度,最后将反馈的结果提供给UI主线程
回调方法
1.onPreExecute : 线程任务开始执行前的准备工作(运行在主线程中)
2.doInBackground :在子线程中执行耗时操作, 在线程中执行的代码(后台开启了线程并执行这部分代码)
3.onProgressUpdate : 在线程的执行中需要执行的界面操作(运行在主线程中)
4.onPostExecute :在子线程直线完毕之后调用(在主线程中执行)
其它回调方法:
5.onCancelled()是当调用cancel方法取消任务并doInBackground执行完之后才会调用
常用方法
execute:执行任务
cancel(boolean):取消任务
isCancelled():判断当前任务是否被取消
publishProgress(int progressValue):发布任务进度
三个泛型参数
第一个泛型参数:(定义)是传递给doInBackground方法的参数的类型(引用类型),由execute(…..)方法初传入
第二个泛型参数:(定义)是传递给onProgressUpdate方法的参数的类型,由publishProgress(。。。。。)方法传入
第三个泛型参数:是传递给onPostExecute方法的参数的类型,接收的是doInBackground方法的返回值
基本使用步骤
1、定义一个类,继承自AsyncTask,并指定三个泛型
2、重写回调方法
3、在UI线程中实例化AsyncTask对象
4、在UI线程中,执行AsyncTask对象的execute(..)方法,开始执行异步任务
5、在执行过程中,可以通过调用cancel(true)方法,停止异步任务
使用AsyncTask必须遵守的准则
三个泛型使用位置
第一个泛型:在doInBackground()方法中使用
第二个泛型:在onProgressUpdate()方法中使用
第三个泛型:在onPostExecute()方法中使用
AsyncTask的实例必须在UIthread中创建
execute方法必须在UI线程中被调用
一个AsyncTask实例只能被执行一次,否则多次调用时将会出现异常
】
1.4 Guithub开源框架简介
【
MarkdownPad --:用于看.md文件格式的文档工具。
Volley、XUtils、Universal-Image-Loader、okhttp、SlidingMenu、android-async-http、fresco
ButterKnife、EventBus
】
1.4.1 运用xutils框架下载图片
【
public class MainActivity extends Activity {
@ViewInject(R.id.iv)ImageView iv;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public voiddownloadImage(View v){
//下载图片
HttpUtilshttpUtils = new HttpUtils();
Stringurl = "http://api.k780.com:88/?app=weather.future&weaid=1&&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json";
httpUtils.send(HttpMethod.GET, url, newRequestCallBack<String>() {
@Override
public voidonFailure(HttpException arg0, String arg1) {
}
@Override
public voidonSuccess(ResponseInfo<String> arg0) {
Toast.makeText(MainActivity.this, arg0.result,0).show();
}
});
}
}
】
1.5 拓展
【
当有多个异步任务被同时启动时,那么是多个同时执行还是依次排队执行??
当有多个异步任务通过execute方法被同时启动时,是依次排队执行的,即
只有当第一个启动被启动的异步任务执行完毕后才会去执行下一个
如何实现让多条异步任务同时执行??
1.当多个异步对象通过execute方法启动时,
* 特点:不管异步任务对象是否属于同类别,使用都保持依次排队执行的特点
2. 如果需要让多个异步任务同时执行的话,可以选择使用
* executeOnExecutor方法启动异步任务
* 参数:
* 1: Executor线程池对象,可以通过此对象指定同时可以有多少个异步任务一起运行
* 2:作用与execute方法中的参数作用完全一致
* 注意:此处的数字不要添加的太多,否则影响速度
指定同时执行3条异步任务,代码如:
Executor exec = Executors.newFixedThreadPool(3);
new MyTask().executeOnExecutor(exec, pb);
new MyTask().executeOnExecutor(exec, pb2);
new MyTask().executeOnExecutor(exec, pb3);
】
1.5.1 示例多个异步同时执行
【
public class MainActivity extends Activity {
private ProgressBar pb,pb2,pb3;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
initView();
}
private voidinitView() {
pb = (ProgressBar) this.findViewById(R.id.progressBar1);
pb2 = (ProgressBar) this.findViewById(R.id.progressBar2);
pb3 = (ProgressBar) this.findViewById(R.id.progressBar3);
}
public voidbtnClick(View v){
Executor exec = Executors.newFixedThreadPool(3);//同时执行的线程数
new MyTask().executeOnExecutor(exec,pb);
new MyTask().executeOnExecutor(exec,pb2);
new MyTask().executeOnExecutor(exec,pb3);
}
//异步任务
class MyTask extendsAsyncTask<ProgressBar, Integer, Void>{
ProgressBarcurrentPb;
@Override
protected Void doInBackground(ProgressBar...params) {
// TODO Auto-generated method stub
currentPb = params[0];
for(inti=0;i<=100;i++){
try {
Thread.sleep(100);
publishProgress(i);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
@Override
protected voidonProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
currentPb.setProgress(values[0]);
}
}
}
】
1.5.2 同时异步多图片下载
【
public class TwoActivity extends Activity {
/*
* 实现多张图片同时下载并且在图片下载过程中时刻显示图片的下载进度
*/
ImageViewiv, iv2, iv3;
ProgressBarpb, pb2, pb3;
String[]urls = {
"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png",
"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png",
"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png"};
@Override
protected voidonCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.two);
initView();
}
private voidinitView() {
iv = (ImageView) findViewById(R.id.imageView1);
iv2 = (ImageView) findViewById(R.id.imageView2);
iv3 = (ImageView) findViewById(R.id.imageView3);
pb = (ProgressBar) findViewById(R.id.progressBar1);
pb2 = (ProgressBar) findViewById(R.id.progressBar2);
pb3 = (ProgressBar) findViewById(R.id.progressBar3);
}
public voidclick(View v) {
Executor exec = Executors.newFixedThreadPool(2);
new ImageTask().executeOnExecutor(exec, urls[0], iv, pb);
new ImageTask().executeOnExecutor(exec, urls[1], iv2, pb2);
new ImageTask().executeOnExecutor(exec, urls[2], iv3, pb3);
}
class ImageTask extendsAsyncTask<Object, Integer, Bitmap> {
ImageViewcurrentIv;
ProgressBarcurrentPb;
@Override
protected Bitmap doInBackground(Object...params) {
// TODO Auto-generated method stub
Stringurl = (String) params[0];
currentIv = (ImageView) params[1];
currentPb = (ProgressBar) params[2];
try {
HttpURLConnectionconn = (HttpURLConnection) newURL(url)
.openConnection();
conn.setRequestMethod("GET");
conn.connect();
if (conn.getResponseCode() == 200) {
InputStreamis = conn.getInputStream();
byte[] b = newbyte[1024];
int num = -1;
ByteArrayOutputStreambos = new ByteArrayOutputStream();
int current = 0;
int total = conn.getContentLength();
while ((num = is.read(b)) != -1) {
bos.write(b,0, num);
SystemClock.sleep(100);
current+= num;
publishProgress(total, current);
}
byte[]bitm = bos.toByteArray();
return BitmapFactory.decodeByteArray(bitm,0, bitm.length);
}
}catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
protected voidonProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
currentPb.setMax(values[0]);
currentPb.setProgress(values[1]);
}
@Override
protected voidonPostExecute(Bitmap result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if (result != null){
currentIv.setImageBitmap(result);
}
}
}
}
】
1.6 案例
1.6.1 案例:简易计时器
【
public class MainActivity extends Activity {
private TextView tv_count;
private intcount = 0;
private CountTask countTask;
@Override
protected voidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_count = (TextView) this.findViewById(R.id.tv_count);
}
// 开始计数
public voidstartCount(View v) {
if (countTask== null) {
countTask = newCountTask();
countTask.execute();
}
}
// 暂停
public voidpauseCount(View v) {
if (countTask!= null) {
countTask.cancel(true);
countTask = null;
}
}
// 重置
public voidreset(View v) {
pauseCount(null);
count = 0;
tv_count.setText("00:00:00");
}
// 开启异步任务
private classCountTask extends AsyncTask<Void,Integer, Void> {
@Override
protected Void doInBackground(Void... params){
while (!isCancelled()) {
try {
Thread.sleep(10);
// 通知主线程更新显示
publishProgress(++count);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
}
return null;
}
@Override
protected voidonProgressUpdate(Integer... values) {
if (isCancelled()) {
return;
}
int ss = values[0];
int mm = values[0] / 60;
int hh = values[0] / 60 / 60;
// tv_count.setText(values[0]+"");
tv_count.setText(hh + ":"+ mm + ":" + ss);
}
// 当子线程成功执行之后调用
@Override
protected voidonPostExecute(Void result) {
Log.i("mtag", "onPostExecute");
Toast.makeText(MainActivity.this, "onPostExecute",0).show();
}
// 当被中断取消时调用
@Override
protected voidonCancelled() {
Log.i("mtag", "onCancelled");
}
}
}
】
1.6.2 案例:下载一张图片
Android4.0开始,Android系统规定不能在UI线程中执行访问网络资源的操作,相关操作必须在子线程中完成
Android网络访问需要的权限:android.permission.INTERNET
【
/**
* 网络访问工具类
*
* @author EvanYu
* @date 2016.01.08
*/
public class HttpUtils {
/**
* 网络访问超时时间
*/
public static final int TIMEOUT = 10000;
/**
* 通过get方式实现网络请求
*
* @paramurl
* 访问的url地址
* @return请求的结果,null代表请求失败
*/
public static byte[] doGet(String url) {
HttpURLConnection conn = null;
try {
URL mUrl = newURL(url);
conn = (HttpURLConnection)mUrl.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(TIMEOUT);
conn.setReadTimeout(TIMEOUT);
conn.connect();
int code= conn.getResponseCode();
if (code== 200) {
return readStream(conn.getInputStream());
} else {
throw new RuntimeException("网络访问失败:" + code);
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (conn!= null) {
conn.disconnect();
conn = null;
}
}
}
public static byte[] doPost(String url, String params) {
HttpURLConnection conn = null;
try {
URL mUrl = newURL(url);
conn = (HttpURLConnection)mUrl.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(TIMEOUT);
conn.setReadTimeout(TIMEOUT);
// 设置请求属性
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", params.length() + "");
// Post请求必须要写以下两行代码
conn.setDoInput(true);
conn.setDoOutput(true);
// 将请求参数写到请求体中
conn.getOutputStream().write(params.getBytes());
;
conn.connect();
int code= conn.getResponseCode();
if (code== 200) {
return readStream(conn.getInputStream());
} else {
throw new RuntimeException("网络访问失败:" + code);
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (conn!= null) {
conn.disconnect();
conn = null;
}
}
}
private static byte[] readStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
return baos.toByteArray();
}
}
】
1.6.3 案例:下载文件时显示进度
使用ProgressBar控件显示下载进度
使用ProgressDialog显示下载进度
构造方法:ProgressDialog(Contextcontext)
设置进度条样式:setProgressStyle(intstyle)
设置标题:setTitle(CharSequence)
设置标题图标:setIcon(intresId)
设置显示内容:setMessage(CharSequencemessage)
设置进度值:setProgress(intvalue)
显示对话框:show()
关闭对话框:cancel()
【
public class MainActivity extends Activity {
private ImageView img_view;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
img_view = (ImageView) this.findViewById(R.id.img_view);
}
public voiddownloadImg(View v) {
// String url=
//"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png";
Stringurl = "http://img1.imgtn.bdimg.com/it/u=335355609,381250936&fm=21&gp=0.jpg";
new MyAsynTask().execute(url);
}
private classMyAsynTask extendsAsyncTask<String, Integer, Bitmap> {
ProgressDialog pDialog;
@Override
protected voidonPreExecute() {
// 开始下载之前先显示一个对话框
pDialog = newProgressDialog(MainActivity.this);
pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pDialog.setMax(100);
pDialog.setTitle("下载图片");
pDialog.setMessage("正在下载图片.....");
pDialog.show();
}
@Override
protected Bitmap doInBackground(String...params) {
/*
* String url = params[0]; byte[]data = HttpUtils.doGet(url);
* //将下载到的数据解析成一张图片 Bitmap bm =BitmapFactory.decodeByteArray(data,
* 0, data.length); SystemClock.sleep(2000);return bm;
*/
HttpURLConnectionconn = null;
try {
Stringurl = params[0];
URLmurl = new URL(url);
conn= (HttpURLConnection) murl.openConnection();
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
// 获取数据的总大小
int tatalSize = conn.getContentLength();
floatcurSize = 0; // 记录当前已经下载的大小
InputStreamis = conn.getInputStream();
ByteArrayOutputStreambaos = new ByteArrayOutputStream();
byte[] buf = newbyte[10];
int length = 0;
while ((length = is.read(buf)) != -1) {
baos.write(buf,0, length);
curSize+= length;
publishProgress((int) ((curSize / tatalSize) * 100));
// 模拟耗时操作
SystemClock.sleep(20);
}
// 将下载到的数据解析成一张图片
byte[]data = baos.toByteArray();
Bitmapbm = BitmapFactory.decodeByteArray(data, 0,
data.length);
return bm;
}else {
throw newRuntimeException("网路访问失败" + code);
}
}catch (Exception e) {
e.printStackTrace();
}finally {
if (conn != null){
conn.disconnect();
conn= null;
}
}
return null;
}
@Override
protected voidonProgressUpdate(Integer... values) {
pDialog.setProgress(values[0]);
}
@Override
protected voidonPostExecute(Bitmap result) {
img_view.setImageBitmap(result);
// 取消显示
// pDialog.dismiss();
pDialog.cancel();
}
}
}
】
1.6.4 案例:下载并显示天气信息
数据网:K780数据网、聚合数据等
K780测试账号
Appkey:15250
Secret:2bbebb3e480a850df6daca0c04a954e1
Sign:f88a5cecc3cbd37129bc090c0ae29943
网络访问工具类的封装
HttpUtils类
K780数据访问工具类的封装
K780Utils类
【
public class Weather {
private String days;
private String temperature;
public Weather() {
}
public Weather(String day, String temperature) {
this.days = day;
this.temperature = temperature;
}
public String getDay() {
return days;
}
public void setDay(String day) {
this.days = day;
}
public String getTemperature() {
return temperature;
}
public void setTemperature(String temperature) {
this.temperature = temperature;
}
@Override
public String toString() {
return "Weather[day=" + days+ ", temperature=" + temperature + "]";
}
}
引入网络访问工具类
public class MainActivity extends Activity {
private TextView tv_show;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_show = (TextView) findViewById(R.id.tv_show);
}
public voiddownload(View v) {
Stringurl = "http://api.k780.com:88/?app=weather.future&weaid=1&&appkey=15250&sign=f88a5cecc3cbd37129bc090c0ae29943&format=json";
new MyTask().execute(url);
}
private classMyTask extends AsyncTask<String,Void, List<Weather>> {
@Override
protected List<Weather>doInBackground(String... params) {
Stringdata;
try {
List<Weather>list = new ArrayList<Weather>();
data= new String(HttpUtils.doGet(params[0]));
JSONObjectjsonObject = new JSONObject(data);
JSONArrayresult = jsonObject.getJSONArray("result");
for (inti = 0; i < result.length(); i++) {
JSONObjecttemp = result.getJSONObject(i);
Stringdays = temp.getString("days");
Stringtemperature = temp.getString("temperature");
list.add(new Weather(days, temperature));
}
return list;
}catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
protected voidonPostExecute(List<Weather> result) {
// tv_show.setText(result);
for (Weather weather : result) {
tv_show.append(weather.toString() + "\n");
}
}
}
}
】