AsyncTask 异步加载网络图片 并使用进度条显示进度
这里我还使用了Thread方式进行加载 作为比较
如果app要进行联网操作请在清单文件(AndroidManifest.xml)中设置权限 在application节点下面就行了
<uses-permission android:name="android.permission.INTERNET"/>
- 为什么使用AsyncTask
- AsyncTask定义
- AsyncTask执行原理
- AsyncTask方法执行步骤
- AsyncTask使用时注意事项
- AsyncTask取消正在执行的任务
- Example 示例
为什么使用AsyncTask
异步任务
由于Android规定,主线程要进行UI绘制和事件响应,UI绘制和事件响应必须在主线程进行 一些耗时的操作不能在主线程中进行,一旦时间超过5秒 就会出现 ANR(无响应)问题,每次耗时操作都要另开线程进行操作,但是呢有些操作我们还涉及到一些UI控件的操作,由于一切关于UI的操作必须在主线程进行 这样 来回切换显得特别麻烦,所以AsyncTask的作用就显现出来了
AsyncTask定义
public abstract class AsyncTask<Params,Progress,Result>
- Params 第一个参数:输入参数 是doInbackground()方法的参数类型
- Progress 第二个参数:进度值 onProgressUpdate()方法的参数类型 ;主要用来反映进度值 ,如果不需要 可设置为Void
- Result 第三个参数:结果类型 doInbackground()方法的的返回值类型 也是 doPostExecute()的参数类型
AsyncTask执行原理
知道了 为什么使用 AsyncTask就好理解了,它的主要任务就是为了在 主线程和子线程中来回切换比较方便,所以它的执行 就分为主线程执行和后台执行;一张图简单明了
AsyncTask 方法执行步骤
其实上面一张图已经能看出大概了,这里再详细的记一下
- execute() 在主线程调用 用来启动 异步任务 ,一定要在主线程调用哦
- onPreExecute() 在execute()执行后立即执行此方法 一般在执行后台任务前对一些UI进行标记 对后台数据进行处理
- doInbackground() 在 onPreExecute()执行后 立即执行此方法 参数是 execute()方法 的参数 会传入到这里,主要在里进行一些耗时的操作,可以使用publishProgress()来跟新进度 返回值就是后台任务的返回结果,
- onProgressUpdate() 在 doInbackground 中调用publishProgress才会执行 ,不调用就不会执行 是传入进度值 在界面显示进度
- onPostExecute() 在doInbackground()执行完毕后调用 参数时 doInbackground的返回结果 ,在这里对结果进行处理显示到UI控件中
- cancel() 取消任务 ,这个手动调用哦
注意事项
- execute() 必须在UI主线程中调用
- 不能在 doInbackground()中进行有关于 UI的操作
- 除了 execute方法可以手动调用 ,其他方法都不能手动调用
- 一个AsyncTask 任务实例只能执行一次 ,第二次就会报错
关于取消任务 cancel(true)
在取消任务时 如果任务正在执行 (doInbackground()方法正在运行) 时取消 不会影响 doInbackground方法的执行
只是不会调用doPostExecute()方法而已 ,而且就算调用了publishProgress方法 onProgressUpdate也不会执行了,
所以 不是真正的取消操作 ,只是取消了 在UI主线程的操作,不调用onPostExecute()和onProgressUpdate()方法;
亲测日志:
这是在doInbackground()中的日志信息 可以看出 任务已经在取消状态,但是还是在运行
正确取消姿势:在doInbackground中加判断代码
if (isCancelled()){ //如果取消了任务 就不执行
return null;
}
取消任务的代码:
if (myAsync!=null && myAsync.getStatus() == AsyncTask.Status.RUNNING){
myAsync.cancel(true);
}
加载网络图片 并显示进度
开始贴代码咯
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.skymxc.demo.downloadimage.MainActivity">
<EditText
android:id="@+id/url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入图片下载URL"
android:singleLine="true"/>
<Button
android:id="@+id/down_thread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="结合Thread 下载"
android:onClick="click"
android:textAllCaps="false"/>
<ImageView
android:id="@+id/img1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="center"
android:maxWidth="100dp"
android:maxHeight="100dp"
android:scaleType="fitCenter"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/down_async"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="使用 AsyncTask 下载"
android:onClick="click"
android:textAllCaps="false"/>
<Button
android:id="@+id/cancel_async"
android:layout_width="0dp"
android:layout_weight="0.5"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="停止任务"
android:onClick="click"
android:enabled="false"
android:textAllCaps="false"/>
</LinearLayout>
<ProgressBar
android:id="@+id/pb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:max="100"
android:maxHeight="4dp"
android:minHeight="4dp"
style="?android:attr/progressBarStyleHorizontal"
/>
<!--android:progressDrawable="@drawable/progress_bg"-->
<ImageView
android:id="@+id/img2"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="center"
android:maxWidth="100dp"
android:maxHeight="100dp"
android:scaleType="fitCenter"/>
</LinearLayout>
完整java源码
package com.skymxc.demo.downloadimage;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private EditText edit ;
private ImageView img1 ;
private ImageView img2;
private ProgressBar pb;
private String urlStr ;
private Button btstart;
private Button btCanel;
private MyAsync myAsync;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.url);
img1 = (ImageView) findViewById(R.id.img1);
img2 = (ImageView)findViewById(R.id.img2);
pb = (ProgressBar) findViewById(R.id.pb);
btCanel = (Button) findViewById(R.id.cancel_async);
btstart = (Button) findViewById(R.id.down_async);
}
public void click(View v){
switch (v.getId()){
case R.id.down_thread:
Toast.makeText(MainActivity.this,"开始下载",Toast.LENGTH_SHORT).show();
loadImage();
break;
case R.id.down_async:
String url = edit.getText().toString();
myAsync = new MyAsync();
pb.setProgress(0);
myAsync.execute(url);
pb.setProgress(100);
break;
case R.id.cancel_async:
if (myAsync!=null && myAsync.getStatus() == AsyncTask.Status.RUNNING){
myAsync.cancel(true);
btCanel.setEnabled(false);
btstart.setEnabled(true);
}else{
btCanel.setEnabled(true);
btstart.setEnabled(false);
}
break;
}
}
/**
* 启动新线程下载图片
*/
private void loadImage(){
new Thread(){
@Override
public void run() {
urlStr = edit.getText().toString(); //获取下载的地址
Log.e("Tag","下载地址:"+urlStr);
HttpURLConnection connection = null; //url连接
try {
URL url = new URL(urlStr);
connection= (HttpURLConnection) url.openConnection(); //打开连接
connection.setRequestMethod("GET"); //设置访问方式 默认是GET 必须为大写
connection.setDoInput(true); //从网络读取数据 默认是true
// connection.setDoOutput(false); //上传数据 但是请求方法必须是post 默认是true
connection.setReadTimeout(10000); //设置读取超时时间 毫秒单位
connection.setConnectTimeout(10000); //设置连接超时时间 毫秒单位
// connection.setRequestProperty("Content-Type","text/plain;charset=utf-8");//上传数据时使用,可以对增加多个请求参数
int code = connection.getResponseCode(); //获取网络请求响应吗 常用:200,404 500
Log.e("Tag","========网络请求响应吗:"+code);
if (code==200){
InputStream is = connection.getInputStream(); //获取到输入流
final Bitmap bmp= BitmapFactory.decodeStream(is); //通过位图工厂将输入流转换为位图
runOnUiThread(new Runnable() {
@Override
public void run() {
img1.setImageBitmap(bmp);
}
});
}else{
//关于UI的操作只能在 主线程进行 runOnUiThread :将操作寄送到主线程运行
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"网络请求错误",Toast.LENGTH_SHORT).show();
}
});
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}
finally {
if (connection!=null){
connection.disconnect(); //断开连接
}
}
}
}.start();
}
/**
* 使用 异步任务下载图片 并显示进度
* 参数1 String 就是 doInbackground() 的参数类型 我们的代码就在这里写 系统默认调用
* 参数2 Integer onProgressUpdate() 的参数类型 系统不会自动调用此方法 手动调用:publishProgress()
* 参数3 Bitmap doInbackground() 的返回值类型 也是 onPostExecute() 的参数类型
*/
class MyAsync extends AsyncTask<String ,Integer,Bitmap>{
/**
* 在 doInbackground() 执行前,系统自动调用 在主线程运行
*/
@Override
protected void onPreExecute() {
Toast.makeText(MainActivity.this,"异步任务开始执行下载",Toast.LENGTH_SHORT).show();
// super.onPreExecute();
btstart.setEnabled(false);
btCanel.setEnabled(true);
}
/**
* 不在主线程 执行
* @param strings url
* @return 位图
*/
@Override
protected Bitmap doInBackground(String... strings) {
HttpURLConnection connection =null;
try {
URL url = new URL(strings[0]);
connection= (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setConnectTimeout(20000);
int code = connection.getResponseCode();
if (code==200){
//为了显示进度条这里使用 字节数组输出流
InputStream is = connection.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int length =-1 ;
int progress =0; //进度
int count = connection.getContentLength(); //获取内容产固定
byte[] bs = new byte[5];
while ((length=is.read(bs))!=-1){
progress+=length; //进度累加
if (count ==0){
publishProgress(-1);
}else{
//进度值改变通知
publishProgress((int)((float)progress/count*100));
}
Log.e("Tag","=任务是否取消:"+isCancelled()+"=======任务进度:"+(int)((float)progress/count*100)+"%");
if (isCancelled()){//如果取消了任务 就不执行
return null;
}
//由于我这网速太快,为了看到进度条就睡眠一会吧
// Thread.sleep(10);
bos.write(bs,0,length);
}
Log.e("Tag","=========任务完成");
return BitmapFactory.decodeByteArray(bos.toByteArray(),0,bos.size());
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (connection!=null){
connection.disconnect();
}
}
return null;
}
/**
* 在 doInbackground() 执行后 系统自动调用 在主线程运行
* @param bitmap 位图
*/
@Override
protected void onPostExecute(Bitmap bitmap) {
Log.e("Tag","===============任务是否取消:"+isCancelled());
Toast.makeText(MainActivity.this, "AsyncTask 下载完成,进行UI绘制", Toast.LENGTH_SHORT).show();
img2.setImageBitmap(bitmap); //设置位图
// super.onPostExecute(bitmap);
btstart.setEnabled(true);
btCanel.setEnabled(false);
}
/**
* 系统不会自动调用 使用 publishProgress() 调用
* 在主线程执行
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
int progress = values[0]; //进度值
if (progress!=-1) {
pb.setProgress(progress);
}
}
}
}
来张效果图吧
github地址:
https://github.com/sky-mxc/AndroidDemo/tree/master/downloadimage