AsyncTask异步从网上下载图片
2016-08-03 00:15
369 查看
##AsyncTask异步从网上下载图片
本次讲解一个通过AsyncTask异步任务从网上下载图片,在安卓中,一般对于耗时操作,我们都不能再主线程中进行操作,如果在主线程操作耗时的程序,那么会产生ANR(Application Not Responding),那么此之前我们要先了解AsyncTask是什么
一、什么是AsyncTask
二、使用的优点:
1.简单,快捷
2.过程可控
三、使用的缺点:在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来
四、实现原理:
Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比
Result 后台执行任务最终返回的结果,比如Integer,String等
1.通常我们在继承AsyncTask后,首先必须要实现的一个方法就是doInBackground(Params…params)这个
方法是用于后台执行,也就是在其他线程执行,一般比较耗时的操作都可以放在这里。注意这里不能直接操
作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用
publicProgress(Progress…progress)来更新任务的进度。
通常我们用到AsyncTask至少还是要用到两个方法的,第一个是doInBackground(Params…params),第二
个就是onPostExecute(Result result) 此方法在主线程执行,doInBackground任务执行的结果作为此方法
的参数返回,
有必要的话你还得重写以下这三个方法,但不是必须的:
onProgressUpdate(Progress…) 这个方法是在doInBackground方法中调用publishProgress()方法,可以
使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
onPreExecute() 如果重写了这个方法,他会在doInBackground方法之前就会被调用,用户调用Excute
时的接口,该方法就被调用了,通常在这里可以初始化一些操作, 比如显示一些隐藏的控件,显示进度对话
框等。
onCancelled() 用户调用取消时,要做的操作
使用AsyncTask类,以下是几条必须遵守的准则:
Task的实例必须在UI thread中创建;
execute方法必须在UI thread中调用;
不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…),
onProgressUpdate(Progress…)这几个方法;
该task只能被执行一次,否则多次调用时将会出现异常,如果要多次执行,则需要创建多个AsyncTask实
例,当然有些东西实现起来比较复杂,本案例中我简单的使用了多个实例执行;
下面开始上带代码了
activity_main.xml布局代码
MainActiviy.java
Utils.java 在这个类中,是我们下载网络操作的代码实现,包括了接口的调用
好了,基本的功能都已经实现了,除了反射和注解拿到对应的ID代码没有贴上来,其他的功能都已经在这里了
本次讲解一个通过AsyncTask异步任务从网上下载图片,在安卓中,一般对于耗时操作,我们都不能再主线程中进行操作,如果在主线程操作耗时的程序,那么会产生ANR(Application Not Responding),那么此之前我们要先了解AsyncTask是什么
一、什么是AsyncTask
AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作 并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程
二、使用的优点:
1.简单,快捷
2.过程可控
三、使用的缺点:在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来
四、实现原理:
AsyncTask直接继承于Object类,位置为android.os.AsyncTask。要使用AsyncTask工作我们要提供三个 泛型参数并重载几个方法(至少重载一个)。 AsyncTask定义了三种泛型类型 Params,Progress和Result。
Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比
Result 后台执行任务最终返回的结果,比如Integer,String等
1.通常我们在继承AsyncTask后,首先必须要实现的一个方法就是doInBackground(Params…params)这个
方法是用于后台执行,也就是在其他线程执行,一般比较耗时的操作都可以放在这里。注意这里不能直接操
作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用
publicProgress(Progress…progress)来更新任务的进度。
通常我们用到AsyncTask至少还是要用到两个方法的,第一个是doInBackground(Params…params),第二
个就是onPostExecute(Result result) 此方法在主线程执行,doInBackground任务执行的结果作为此方法
的参数返回,
有必要的话你还得重写以下这三个方法,但不是必须的:
onProgressUpdate(Progress…) 这个方法是在doInBackground方法中调用publishProgress()方法,可以
使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
onPreExecute() 如果重写了这个方法,他会在doInBackground方法之前就会被调用,用户调用Excute
时的接口,该方法就被调用了,通常在这里可以初始化一些操作, 比如显示一些隐藏的控件,显示进度对话
框等。
onCancelled() 用户调用取消时,要做的操作
使用AsyncTask类,以下是几条必须遵守的准则:
Task的实例必须在UI thread中创建;
execute方法必须在UI thread中调用;
不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…),
onProgressUpdate(Progress…)这几个方法;
该task只能被执行一次,否则多次调用时将会出现异常,如果要多次执行,则需要创建多个AsyncTask实
例,当然有些东西实现起来比较复杂,本案例中我简单的使用了多个实例执行;
下面开始上带代码了
activity_main.xml布局代码
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" tools:context="com.company.annofindview.MainActivity"> //显示标题标签 <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:textSize="24sp" android:text="美女图片" /> //六个显示美女图片的控件 <ImageView android:id="@+id/img1" android:layout_width="85dp" android:layout_below="@+id/tv_title" android:layout_marginTop="15dp" android:layout_marginLeft="30dp" android:layout_height="100dp" /> <ImageView android:id="@+id/img2" android:layout_width="85dp" android:layout_below="@+id/tv_title" android:layout_marginLeft="30dp" android:layout_toRightOf="@+id/img1" android:layout_alignTop="@+id/img1" android:layout_height="100dp" /> <ImageView android:id="@+id/img3" android:layout_width="85dp" android:layout_below="@+id/tv_title" android:layout_marginLeft="30dp" android:layout_toRightOf="@+id/img2" android:layout_alignTop="@+id/img2" android:layout_height="100dp" /> <ImageView android:layout_marginTop="10dp" android:id="@+id/img4" android:layout_width="85dp" android:layout_below="@+id/img1" android:layout_alignLeft="@+id/img1" android:layout_height="100dp" /> <ImageView android:layout_marginTop="10dp" android:id="@+id/img5" android:layout_width="85dp" android:layout_below="@+id/img1" android:layout_marginLeft="30dp" android:layout_toRightOf ="@+id/img4" android:layout_height="100dp" /> <ImageView android:layout_marginTop="10dp" android:id="@+id/img6" android:layout_width="85dp" android:layout_below="@+id/img1" android:layout_marginLeft="30dp" android:layout_toRightOf ="@+id/img5" android:layout_height="100dp" /> //点击下载按钮 <Button android:id="@+id/btn_load" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下载" android:layout_below="@id/img6" android:layout_centerHorizontal="true" /> //用于显示每张图下载后的进度 <SeekBar android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/seekBar" android:max="100" android:visibility="gone" android:layout_below="@+id/btn_load" android:layout_centerHorizontal="true" android:layout_marginTop="49dp" /> </RelativeLayout>
MainActiviy.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Utils.Handler { //这里通过反射和注解拿到每个view对应的id,方便了我们每次手动的去使用findViewById去 //拿到控件ID,如果想了解这种实现方式可以查看我博客中的通过反射注解获取控件ID //img1 - img6是对应的六个图片控件 @FindIdAnno(R.id.img1) private ImageView img1; @FindIdAnno(R.id.img2) private ImageView img2; @FindIdAnno(R.id.img3) private ImageView img3; @FindIdAnno(R.id.img4) private ImageView img4; @FindIdAnno(R.id.img5) private ImageView img5; @FindIdAnno(R.id.img6) private ImageView img6; //下载按钮控件 @FindIdAnno(R.id.btn_load) private Button btn_load; //进度条控件 @FindIdAnno(R.id.seekBar) private ProgressBar prog; //保存6个图片控件的数组 private ImageView[] img; //当前从网络下载的实时字节长度 private int currentLength; //用于转换百分比的NumberFormat对象 NumberFormat format = NumberFormat.getNumberInstance(); //需要下载图片的地址 private String[] path = {"http://b.hiphotos.baidu.com/image/pic/item/908fa0ec08fa513d17b6a2ea386d55fbb2fbd9e2.jpg", "http://g.hiphotos.baidu.com/image/pic/item/e824b899a9014c0870b4e6910f7b02087bf4f473.jpg" , "http://d.hiphotos.baidu.com/image/pic/item/6159252dd42a28346729f83f5eb5c9ea15cebf73.jpg", "http://a.hiphotos.baidu.com/image/pic/item/9213b07eca80653864e86a2792dda144ad348280.jpg" , "http://e.hiphotos.baidu.com/image/pic/item/a8ec8a13632762d0dbb06a8ba5ec08fa513dc67c.jpg", "http://a.hiphotos.baidu.com/image/pic/item/e61190ef76c6a7efce7315cef9faaf51f2de6684.jpg"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //通过注解拿到每个控件的ID FindId.findById(MainActivity.this); //返回数的整数部分所允许的最大位数 format.setMaximumIntegerDigits(3); //返回数的小数部分所允许的最大位数 format.setMaximumFractionDigits(2); //初始化img数组 img = new ImageView[]{img1, img2, img3, img4, img5, img6}; //设置按钮点击事件 btn_load.setOnClickListener(this); } /** * 当点击下载按钮时,会循环6次,去下载对应的图片,这里就是实现了我们的 * 6次AsyncTask的执行 * @param v */ @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_load: for (int i = 0; i < path.length; i++) { new MyAsyncTask(i).execute(path[i]); } break; } } /** * 接口回调所实现的方法,此方法将拿到每次下载图片的总容量和当前下载进度容量 * @param total * @param currentLength */ @Override public void getPro(int total, int currentLength) { this.currentLength += currentLength;//每次将下载的字节相加 String format1 = format.format(this.currentLength * 1.0 / total);//转换成对应的 比例 int progress = (int) (Double.parseDouble(format1) * 100);//转换成百分百的进度 prog.setProgress(progress);//设置进度条 } /** * 内部类继承AsyncTask */ class MyAsyncTask extends AsyncTask<String, Integer, Bitmap> { //对应设置img的下标,0-5 int i; public MyAsyncTask(int i) { this.i = i; } @Override protected Bitmap doInBackground(String... params) {//先执行此线程,当线程有返回值的 //时候,相当于线程已经执行完,然后将值传给onPostExecute方法 currentLength = 0; return Utils.downloadPic(params[0], MainActivity.this); } /** * 下载前我们将SeekBar已经隐藏了,当点击下载时,就可以通过这个方法将进度条显示出来,次方法最先 * 执行 */ @Override protected void onPreExecute() { super.onPreExecute(); prog.setVisibility(View.VISIBLE); } /** * 此方法是属于主线程的,在这里面跟新UI,跟新进度条,以及跟新ImageView对应的图片 * @param bitmap */ @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); prog.setProgress(0); img[i].setImageBitmap(bitmap); } } }
Utils.java 在这个类中,是我们下载网络操作的代码实现,包括了接口的调用
public class Utils { /** * 自定义一个接口,用于接口回调 */ interface Handler{ public void getPro(int total, int currentLength); } /** * 下载网络的操作,采用的是HttpURLConnection GET方式获取网络请求 * @param path * @param handler * @return */ public static Bitmap downloadPic(String path,Handler handler){ try { //创建URL连接 URL url = new URL(path); //转换成HttpURLConnection对象 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); //设置连接超时5秒 connection.setConnectTimeout(5000); //设置读取超时5秒 connection.setReadTimeout(5000); //获取连接,相当于我们在地址栏输入网址敲回车,去获取服务器的连接 connection.connect(); //请求网络的响应码 int code = connection.getResponseCode(); //200表示请求成功 if(code == HttpURLConnection.HTTP_OK){ int total = connection.getContentLength(); InputStream is = connection.getInputStream(); BufferedInputStream bis = new BufferedInputStream(is); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len = -1; byte[] bytes = new byte[1024]; while(-1 != (len = bis.read(bytes))){ int currentLength = len; //这里使用了接口回调,每下载1024个字节就回调一次,将图片的总字节大小和当前下载的 //字节数传过去 handler.getPro(total,currentLength); bos.write(bytes,0, len); } byte[] bytes1 = bos.toByteArray(); bos.close(); //转换成图片返回 Bitmap bitmap = BitmapFactory.decodeByteArray(bytes1,0,bytes1.length); return bitmap; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }
好了,基本的功能都已经实现了,除了反射和注解拿到对应的ID代码没有贴上来,其他的功能都已经在这里了
注意:在安卓中使用网络一定要设置网络访问权限,不然无论你怎么运行都会蹦掉的。
运行结果如下:
首页
点击下载按钮后
六张图片都下载完成
相关文章推荐
- js笔记整理02
- 大数据IMF传奇行动绝密课程第20课:Top N彻底解秘
- [BZOJ4513] [SDOI2016] 储能表 - 数位DP
- 树的直径学习总结
- ace在linux下编译
- am命令启动Acitivity流程图
- 陶伯定理(Tauber theorem)
- python_django 返回和 通用视图
- Retrofit2 完全解析 探索与okhttp之间的关系
- 关于水平居中,垂直居中的三种办法
- 【一步一步学习VBA】VBA获取单元格数值并弹窗显示
- 网站从http过度到https需要注意的几个小问题
- iBATIS入门之安装配置与简单测试
- CodeForces 702E Analysis of Pathes in Functional Graph(倍增)
- [hdu 2068 RPG的错排] 错排公式
- BZOJ2243 (树链剖分+线段树)
- 2017年搜狐内推 面试题——谈谈面试中的算法
- 两种计算器的实现方式
- 马哥2016全新Linux+Python高端运维班第一周作业
- servlet