【Android学习笔记系列】AsyncTask的使用和介绍(获取网络图片与进度条实例)
2017-05-29 00:30
579 查看
什么是异步
首先我们要先知道什么是异步、什么是同步:同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程,同步是阻塞模式。
异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待,异步是非阻塞模式。
为什么要异步任务
然后我们开始介绍为什么我们需要异步任务这个功能,也就是AsyncTask
因为Android是单线程模型,只有UI线程也就是主线程才能对UI进行操作,而其他workerThread(工作线程)是不能直接操作UI的,这样做的好处是保证了UI的稳定性和准确性,避免多线程对UI进行操作而造成混乱。
但是同时Android也是一个多线程的操作系统,我们不可能把所有的事情都放到主线程中去做,比如一些网络操作,和读取文件这些耗时的操作。如果你都放到主线程去执行,这就会造成后面任务的阻塞,Android系统会去检测这样一个阻塞,当阻塞时间太长的时候,系统就会抛出Application Not Responding的无反应提示框(ANR)
所以我们要将耗时操作放到非主线程去执行,这样既保证了android的单线程模型,也避免了程序的一个ANR。
提到异步任务,我们最直接想到的是用线程和线程池去实现,Android给我们提供了一个主线程与其他线程的通讯机制,同时Android也给我们提供了一个封装好了组件AsyncTask,利用她我们可以很方便的实行异步任务
AsyncTask为何而生
总结下来就是两条,第一、它可以在子线程中去更新UI。第二、它封装简化了异步操作。
上边我们提到,我们要是想异步任务,通常我们可以通过线程和线程池去实现。但这里就涉及到了一个线程同步、和一个线程的管理,同时当线程结束的时候,我们还需要用handler去通知主线程去更新UI。而AsyncTask将这一切都封装了起来,我们可以非常方便的在子线程中去更新UI。
总之,什么是AsyncTask?
AsyncTask就是一个封装好的线程池,比起Thread+Handler的方法,AsyncTask在操作UI线程上更方便,因为onPreExecute()、onPostExecute()及更新UI方法onProgressUpdate()均运行在主线程中,这样就不用Handler发消息处理了。
AsyncTask的基本结构介绍
一、构建AsyncTask子类的参数
要传入的三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用Java.lang.Void类型代替。
二、构建AsyncTask子类的回调方法
doInBackground:必须重写的方法,所有耗时的操作都将在这个方法中进行,此方法将接收输入参数和返回计算结果。在执行过程中可以调publishProgress(Progress...values)来更新进度信息。
onPreExecute: 通常将一些初始化操作放在这里执行
onPostExecute: 当dolnBackground执行后,立即被调用,传入dolnBackground方法所返回的值,即展示我们处理完的结果,将结果显示到UI组件上。
onProgressUpdate: 当dolnBackground执行事,调用了publishProgress方法,自动触发该方法,直接将进度信息更新到UI组件上,通过这个方法,我们可以清楚的知道当前耗时操作的完成进度。
另外有个重点要说明的是,这四个函数中只有doInBackground是在其他线程执行的,需要异步处理,其他三个函数都是在主线程中调用的,也就是说只有doInBackground方法内部是不能出现更新UI的操作
除了这四个函数之外,当然我们还要一个方法也要说一下
Execute:执行一个异步任务,触发异步任务的执行。简而言之,这个函数就是执行的意思,就类似于Thread线程中的start方法,但这个方法只被允许在UI线程中调用。
执行顺序:
Execute->onPreExecute->dolnBackground->(onProgressUpdate)->onPostExecute
首先我们通过execute方法开启执行一个异步任务,然后我们通过onPreExecute方法去完成开始异步任务前的一些准备操作,比如初始化。然后再调用dolnBackground方法来执行真正的耗时操作,如果在该方法里执行了publishProgress方法,则接下里调用onProgressUpdate方法用于获取进度,更新进度条,然后再执行onPostExecute方法,更新UI
顺序验证:
新建一个MyAsyncTask.java,继承于AsyncTask,所有参数暂时使用Java.lang.Void类型代替,只有doInBackground方法是必须要重写的,同时我们在每个函数中都加入一个Log,方便我们知道他们之间执行的顺序。然后我们在虚拟机跑一跑,执行顺序就出来了,严格执行,我们上面所说的顺序
AsyncTask实际操作(网络获取图片)
网络操作式一个不稳定的耗时操作,从Android4.0开始就被严禁放进主线程中。所以在加载网络图片的操作中,我们需要在异步处理下载图片,并且在UI线程中去设置图像首先我们准备一张网络图片的链接,要是失效了就去百度搜索一张就行了
http://image.sinajs.cn/newchart/monthly/n/sh601003.gif
actvity_main.xml
<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" tools:context="${relativePackage}.${activityClass}" > <Button android:id="@+id/btn_loadImage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="46dp" android:layout_marginTop="80dp" android:text="Image Test" /> </RelativeLayout> [/b]
image.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ProgressBar android:id="@+id/progressBar1" style="?android:attr/progressBarStyleHorizontal" android:visibility="gone" android:layout_width="match_parent" android:layout_height="wrap_content" /> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /> </LinearLayout>
ImageTest.java
package bnuz.lwj.asynctaskteaching; import java.net.URLConnection; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Bundle; import android.os.PersistableBundle; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class ImageTest extends Activity{ private ImageView mImageView; private ProgressBar mProgressBar; private String URL = "http://image.sinajs.cn/newchart/monthly/n/sh601003.gif"; MyAsyncTask myAsyncTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.image); mImageView=(ImageView) findViewById(R.id.image); mProgressBar=(ProgressBar) findViewById(R.id.progressBar1); //设置传递进去的参数,是doInBackground获取的 MyAsyncTask myAsyncTask=new MyAsyncTask(mProgressBar,mImageView); myAsyncTask.execute(URL); } @Override protected void onPause() { //当一个Activity跳转到另一个activity是调用 super.onPause(); if(myAsyncTask!=null && myAsyncTask.getStatus()==AsyncTask.Status.RUNNING){ //这并不是真正的关闭这个异步任务,关闭线程,只是给线程标记了一个关闭的信号 //还需要在线程内部判断标记信号为什么是跳出线程 //目的是让这个异步任务和ImageTest的Activity的周期同步,一起存货,以免出现bug myAsyncTask.cancel(true); } } }MyAsyncTask.java
package bnuz.lwj.asynctaskteaching; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; //我们要传入一个URL,所以第一个参数我们定义为String类型 //第二个参数是进度,所以我们传进去一个integer,它就是publishProgress(value)的参数类型 //因为我们需要返回的一个参数类型是一张图片,所以设置为bitmap类型 //从这里我们可以知道,以后如果有返回复杂参数的时候,我们还需要定义javaBean类型 public class MyAsyncTask extends AsyncTask<String,Integer,Bitmap>{ private ProgressBar mProgressBar; private ImageView mImageView; public MyAsyncTask(ProgressBar mProgressBar, ImageView mImageView) { super(); this.mProgressBar = mProgressBar; this.mImageView = mImageView; } @Override protected void onPreExecute() { super.onPreExecute(); //将隐藏的mProgressBar显示出来 mProgressBar.setVisibility(View.VISIBLE); } //该方法传进来的是一个可变长的数组String... params //因为我们只传进来一个URL,所以只要取这个数组索引为0的值就行了 @Override protected Bitmap doInBackground(String... params) { String url=params[0]; Bitmap bitmap=null; URLConnection connection; InputStream is; //模拟更新进度 for(int i=0;i<100;i++){ //判断 if(isCancelled()) { //跳出线程 break; } publishProgress(i); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } //通过URL.openConnection方法获得一个URLConnection对象 try { URL myurl = new URL(url); connection=myurl.openConnection(); connection.connect(); is=connection.getInputStream(); //包装一下 BufferedInputStream bis=new BufferedInputStream(is); //将一个输入流解析为一个BitMap对象 bitmap=BitmapFactory.decodeStream(is); //关闭输入流 is.close(); bis.close(); } catch (MalformedURLException e) { Log.i("info", "MalformedURLException"); e.printStackTrace(); } catch (IOException e) { Log.i("info", "IOException:"+e.toString()); e.printStackTrace(); } return bitmap; } //操作UI,获取doInBackground从返回的bitmap图像 @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); /*mProgressBar.setVisibility(View.GONE);*/ mImageView.setImageBitmap(bitmap); this.cancel(true); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); if(isCancelled()){ return; } //从doInBackground的publishProgress方法获取到i值 //为什么是value[0],原理跟doInBackground获取URL原理一样 mProgressBar.setProgress(values[0]); } }MainActvity.java
package bnuz.lwj.asynctaskteaching; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.widget.ImageView; import android.widget.ProgressBar; public class ImageTest extends Activity{ private ImageView mImageView; private ProgressBar mProgressBar; private String URL = "http://image.sinajs.cn/newchart/monthly/n/sh601003.gif"; MyAsyncTask myAsyncTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.image); mImageView=(ImageView) findViewById(R.id.image); mProgressBar=(ProgressBar) findViewById(R.id.progressBar1); //实例一个异步任务 MyAsyncTask myAsyncTask=new MyAsyncTask(mProgressBar,mImageView); //设置传递进去的参数,是doInBackground获取的 myAsyncTask.execute(URL); } @Override protected void onPause() { //当一个Activity跳转到另一个activity是调用 super.onPause(); if(myAsyncTask!=null && myAsyncTask.getStatus()==AsyncTask.Status.RUNNING){ //这并不是真正的关闭这个异步任务,关闭线程,只是给线程标记了一个关闭的信号 //还需要在线程内部判断标记信号为什么是跳出线程 //目的是让这个异步任务和ImageTest的Activity的周期同步,一起存货,以免出现bug myAsyncTask.cancel(true); } } }
注意:最后别忘记了在AndroidManifest.xml加入网络权限和给新建的ImageTest这个Activity注册奥
在<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />下输入
<uses-permission android:name="android.permission.INTERNET" />在MainActivity的注册下面输入
<activity android:name=".ImageTest"></activity>
另外在写这个例子的时候,我因为网络问题报了IO异常,折腾了一几乎一天,大家可以注意一下
URLConnection解析URL的UnknownHostException异常解决办法
效果就是从MainActivity点击button跳转到ImageTest,然后进度条先执行到100,再显示图片
总结到此结束~
欢迎关注我的博客,一起学习讨论
要转载,请附上原文链接,作者:SnailMann
完整源码下载
传送门:【Android学习笔记系列】BaseAdapter适配器的介绍、使用及优化(详细)
大家也可以关注我的私人github: https://github.com/SnailMann,欢迎watch ,star, fork
关注我的私人GitHub
虽然现在暂时没有什么东西,但是总会有的
相关文章推荐
- android AsyncTask介绍 详解AsyncTask的使用 使用实例---加载网络图片
- 安卓多线程编程系列2:异步任务的使用之使用异步任务带有进度的横向滚动条下载网络图片
- [Android] AsyncTask使用实例---加载网络图片
- [Android] AsyncTask使用实例---加载网络图片
- [Android] AsyncTask使用实例---加载网络图片
- [Android] AsyncTask使用实例---加载网络图片
- 安卓控件使用系列17:ImageView获取网络图片
- 【Android学习笔记系列】AsyncTask、BaseAdapter整合异步加载用例(通过解析JSON格式数据加载网络图片内容)
- [置顶] 使用ImageLoader加载网络图片时,如何获取加载进度,如何设置进度条
- [Android] AsyncTask使用实例---加载网络图片
- AsyncTask使用实例---加载网络图片
- [Android] AsyncTask使用实例---加载网络图片
- [Android] AsyncTask使用实例---加载网络图片
- (原创)使用AsyncTask(带修改线程池方式)+自定义ImageLoader+LRU算法对图片三级缓存及其显示优化(只有在ListView滑动停止的时候才去网络请求获取图片数据)
- Android异步任务AsyncTask使用解析 获取网络图片
- [Android] AsyncTask使用实例---加载网络图片
- Android深入浅出系列之实例应用—程序运行进度提示条应用ProgressBar和Handler的使用
- Android学习笔记21:ImageView获取网络图片
- Android深入浅出系列之实例应用—简单的手指拖动图片,图片滑来滑去显示应用Gallery和BaseAdapter以及ImageView的使用
- [Python系列实用教程]一、Python如何使用urllib2获取网络资源