Android基础之AsyncTask
来源:互联网 发布:淘宝推广店铺 编辑:程序博客网 时间:2024/05/11 20:07
背景
AsyncTask是Android中异步请求的基础类。由于在Android系统是单线程模型,即只能在主线程更新UI。之所以这样设计,主要是为了避免多个线程对UI同时操作造成的混乱。另一方面,Android是一个多线程的操作系统,我们不可能把所有操作都放在主线程中操作,如网络请求,读取存储的数据,等等。否则会抛出ANR异常。所以,有必要把耗时操作放在非UI线程中执行,而仅在UI线程更新UI。这样既保证了Android系统的单线程模型,又避免了程序的ANR。
AsyncTask是Android封装好的用于实现异步请求的类。利用这个类,可以很方便的在子线程中更新UI,同时简化异步操作。
AsyncTask基础
AsyncTask是一个抽象类,它包含了三个泛型参数。一般会继承该类。
AsyncTask<Params,Progress,Result>
其中:
泛型参数一:Params。它是启动任务时的输入参数类型;
泛型参数二:Progress。它是后台任务执行时返回的进度值的类型;
泛型参数三:Result。后台任务执行完成后返回的结果类型。
AsyncTask中必须重写的方法:
doInBackground():该方法必须重写,用于执行后台的异步任务。所有耗时操作都在该方法中执行。
onPreExecute():执行耗时操作前被调用。完成一些初始化操作。该方法在主线程中执行。
onPostExecute():在doInBackground()方法执行完毕后,该方法会被调用,并将返回的值传递至该方法中。该方法在主线程中执行;
onProgressUpdate():在 doInBackground()中调用publishProgress()方法,可将当前后台执行的进度发送到该方法中。该方法在主线程中执行。
新建一个MyAsyncTask类继承自AsyncTask,并重写上述方法,打印Log,各方法的调用顺序如下:
public class MyAsyncTask extends AsyncTask<Void, Void, Void> { public static final String TAG = "MyAsyncTask"; @Override protected Void doInBackground(Void... params) { Log.e(TAG, "doInBackground"); return null; } @Override protected void onPreExecute() { super.onPreExecute(); Log.e(TAG, "onPreExecute"); } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Log.e(TAG, "onPostExecute"); } @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); Log.e(TAG, "onProgressUpdate"); }}
调用顺序:
顺序为:onPreExecute()—>doInBackground()—>onPostExecute()
若在doInBackground()中加入方法 publishProgress()方法,再次运行程序,打印的Log为:
顺序为:onPreExecute()—>doInBackground()—>onProgressUpdate()—>onPostExecute()
demo1:使用AsyncTask加载一张网络图片
在使用AsyncTask加载完成之前,会实现一个progressBar,提示用户等待。加载完毕后,隐藏progressBar。
布局代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_image_load" 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.demo.lenovo.asynctasktest.ImageLoadActivity"> <ImageView android:id="@+id/iv_image" android:layout_width="match_parent" android:layout_height="match_parent" /> <ProgressBar android:id="@+id/pb_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:visibility="gone" /></RelativeLayout>
首先将ProgressBar设为Gone,即不显示。
接着是Activity:
public class ImageLoadActivity extends AppCompatActivity { private static final String URL = "http://img.my.csdn.net/uploads/201609/14/1473820894_3292.png"; private ImageView iv_image; private ProgressBar pb_progress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_image_load); iv_image = (ImageView) findViewById(R.id.iv_image); pb_progress = (ProgressBar) findViewById(R.id.pb_progress); new ImgLoadAsyncTask().execute(URL); } private class ImgLoadAsyncTask extends AsyncTask<String, Void, Bitmap> { @Override protected void onPreExecute() { super.onPreExecute(); pb_progress.setVisibility(View.VISIBLE); } @Override protected Bitmap doInBackground(String... params) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } String url = params[0]; URLConnection urlConnection = null; Bitmap bitmap = null; InputStream inputStream = null; BufferedInputStream bufferedInputStream = null; try { urlConnection = new URL(url).openConnection(); inputStream = urlConnection.getInputStream(); bufferedInputStream = new BufferedInputStream(inputStream); bitmap = BitmapFactory.decodeStream(bufferedInputStream); } catch (IOException e) { e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } if (bufferedInputStream != null) { bufferedInputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); iv_image.setImageBitmap(bitmap); pb_progress.setVisibility(View.GONE); } }}
在Activity中,将AsyncTask设为内部类,并将泛型参数分别设为String, Void, Bitmap。其中String表示加载的图片URL地址,Void表示不需要在加载过程中通知主线程更新进度,Bitmap表示加载完毕后返回一张Bitmap图。
在onPreExecute()方法中,首先将ProgressBar设为可见状态;在doInBackground()方法中从泛型数组String中获取url地址,通过URLConnection和InputStream网络请求基础类请求url,最终转化为Bitmap图像并返回;最后,在onPostExecute()方法中将Bitmap设置到ImageView上并将ProgressBar关闭。(在doInBackground()方法中加入一个Thread.sleep()以模拟网络请求的延迟。)
demo2:使用AsyncTask模拟进度条
在demo中,将模拟进度条的更新等待操作。
首先,布局就是一个横向的ProgressBar:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_progress_load" 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.demo.lenovo.asynctasktest.ProgressLoadActivity"> <ProgressBar android:id="@+id/pb_load_progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" /></RelativeLayout>
接着是Activity:
public class ProgressLoadActivity extends AppCompatActivity { private ProgressBar pb_load_progress; private ProgressLoadAsyncTask mProgressLoadAsyncTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_progress_load); pb_load_progress = (ProgressBar) findViewById(R.id.pb_load_progress); mProgressLoadAsyncTask = new ProgressLoadAsyncTask(); mProgressLoadAsyncTask.execute(); } private class ProgressLoadAsyncTask extends AsyncTask<Void, Integer, Void> { @Override protected Void doInBackground(Void... params) { for (int i = 0; i < 100; ++i) { //根据AsyncTask指定的泛型,该方法可传入一个Integer变长数组作为参数 publishProgress(i); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); pb_load_progress.setProgress(values[0]); } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); pb_load_progress.setVisibility(View.GONE); } }}
在Activitiy中使用AsyncTask模拟进度条的更新,由于不需要传入参数,所以泛型参数一是Void,而在doInBackground中,调用publishProgress(),传入的值将传给onProgressUpdate()方法,所以泛型参数二是Integer类型。该操作也不需要返回参数,所以泛型参数三也是Void类型。
最后,在onPostExecute()中隐藏ProgressBar。
取消Task
在运行过程中,会遇到一个问题:
- 当进度条未执行完毕时,退出该Activity,并立即再次启动,发现进度条并没有从上次退出的进度继续执行,也没有从头执行。
原因:
- 由于AsyncTask的底层实现是线程池,只有当一个线程执行完毕以后,下一个线程才会开始,所以,只有当doInBackground()方法的内容全部执行完毕以后,才能得到下一次执行。
解决方法:
- 使AsyncTask的生命周期与Activity的生命周期一致。
具体解决方式:
在Activity的onPause()中判断AsyncTask的状态并将其停止:
@Override protected void onPause() { super.onPause(); //当AsyncTask不为空且AsyncTask的状态为正在运行 if (mProgressLoadAsyncTask != null && mProgressLoadAsyncTask.getStatus() == AsyncTask.Status.RUNNING) { //只是将对应的AsyncTask标记为cancel状态,并没有真正取消该线程。 mProgressLoadAsyncTask.cancel(true); } }
需要注意的是cancel()方法只是将对应的AsyncTask标记为cancel状态,并没有真正取消该线程。下面是cancel()方法的文档:
其中第二段说:调用cancel()方法将会触发onCancelled(Object)在doInBackground()方法结束后调用(并且onCancelled(Object)是在UI线程中调用),同时您还要确保onPostExecute(Object) 不会被调用。最后还需要不断在doInBackground方法中检查isCancelled() 并及时手动停止该AsyncTask。
所以,除了在onPause中调用cancel方法外,还需要在doInBackground方法中调用isCancelled()判断状态,并将onPostExecute方法中的逻辑复制到onCancelled()方法中(如您需要在onCancelled()中直线自己的操作,如关闭进度条等,请不要调用super.onCancelled()):
在doInBackground和onProgressUpdate方法中,不断判断isCancelled()方法,若为true即终止任务:
@Override protected Void doInBackground(Void... params) { for (int i = 0; i < 100; ++i) { if (isCancelled()) { break; } //根据AsyncTask指定的泛型,该方法可传入一个Integer变长数组作为参数 publishProgress(i); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); if (isCancelled()) { return; } pb_load_progress.setProgress(values[0]); }
重新运行程序,当中途退出Activity,并再次进入后,可以进度条从头加载。
总结
必须在UI线程中创建AsyncTask实例;
必须在UI线程中调用AsyncTask.execute()方法;
不能手动调用AsyncTask的回调方法;
只能调用AsyncTask.execute()一次,不可重复调用。
只有doInBackground()方法运行在子线程中,其余回调方法均运行在主线程中。
- Android基础之AsyncTask
- android基础学习之AsyncTask
- Android基础之AsyncTask详解,巨细哟
- Android基础之AsyncTask源码解析
- 【Android基础】AsyncTask基础
- [Android基础]AsyncTask类
- android基础--AsyncTask
- Android AsyncTask基础
- android基础(AsyncTask)
- 史上最全系列之Android开发基础之AsyncTask
- Android学习笔记:Android异步任务之AsyncTask基础
- AsyncTask专题之二 AsyncTask基础
- Android基础之进程和线程 AsyncTask , Handler
- Android基础之AsyncTask的doInBackground方法参数详解
- 0913Android基础网络技术之下载(AsyncTask)
- Android基础之AsyncTask的doInBackground方法参数详解
- Android源码基础解析之异步任务AsyncTask
- Android基础之AsyncTask的doInBackground方法参数详解
- Android API 23+ 动态权限申请
- nodejs实现css,js和图片的压缩
- JavaWeb开发中alias拦截器的使用方法
- restclient下载地址
- 转载-mysql字符集
- Android基础之AsyncTask
- svg学习之svg动画
- maven:如何向本地仓库添加依赖
- TCP协议的连接和关闭精讲;三次握手、四次挥手剖析
- html标签中meta属性使用介绍
- Lucene6.1查询所有数据
- 10
- libevent学习笔记【使用篇】——DNS域名解析 evdns
- 3. Longest Substring Without Repeating Characters