现在一直在做安卓的教学,一些人对于一些比较基本的东西还是比较模糊
所以最近只要有时间就会总结一些常用的知识
希望对读者有所帮助
下面有具体示例代码
简介
AsyncTask是 API 3提供的轻量级异步类(代码轻量一些,实际上与Hanler相比更加的消耗资源),效果相当于Thread+Handler。与之相比,AsyncTask在使用上更加简便快捷且过程可控,但当多个异步操作进行UI变更的时候,就变得比较复杂。
首先明确为什么会有AsyncTask或者handler的存在,其实很简单,就是程序在正常情况下只存在一个线程(主线程也叫UI线程),而当处理耗时操作如网络请求等等,就会阻塞主线程,造成UI界面的卡顿,这是不被允许的。所以才会出现异步处理(AsyncTask、Thread)。
这也就是为什么我们会使用AsyncTask或者Thread的原因。
AsyncTask是一个抽象类,如需使用它则必须显示的继承或者使用匿名内部类。
public abstract class AsyncTask<Params, Progress, Result>
其中实现子类或者匿名内部类的时候需要设置三个泛型参数
Params:执行后台任务所需要设置的参数,如:网络请求的URL
Progress:后台任务执行的进度信息,如:实现下载网络图片的进度,就需要通过它
Result:后台任务执行完毕最终返回的结果,如:下载网络图片最终返回的图片
AsyncTask提供的方法
该方法运行在UI线程,当AsyncTask执行execute()方法,onPreExecute()就会被执行。
可以再这个方法中做一些准备工作,如初始化进度条值等等
protected void onPreExecute() {
super.onPreExecute();
}
在onPreExecute()方法执行后马上执行,该方法运行在子线程中,负责执行耗时操作。
在执行耗时操作的过程中你可以不断地调用publishProgress()方法,这样可以使onProgressUpdate()不断地被调用
protected Result doInBackground(Params... params) {
return null;
}
调用publishProgress()方法后,就会导致该方法被执行,方法运行在UI线程,
可以更新进度条的显示等等关于进度的信息
protected void onProgressUpdate(Progress... values) {
super.onProgressUpdate(values);
}
这是最终的方法
doInBackground()方法执行后的返回结果会传给该方法,该方法运行在UI线程,在该方法中你可以显示处理结果。
protected void onPostExecute(Result s) {
super.onPostExecute(s);
}
用户取消操作执行的方法
protected void onCancelled() {
super.onCancelled();
}
这个方法不可以复写,多数在doInBackground()中调用,用来及时发布后台任务
publishProgress(Integer... Values)
下面是我写的一个下载图片的例子
MainActivity
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
private ProgressBar progressBar;
final String imageUrl = "http://img4.duitang.com/uploads/item/201502/01/20150201163209_NNruz.jpeg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.main_imageview);
progressBar = (ProgressBar) findViewById(R.id.main_progress);
}
// 通过按钮点击事件开启Asynctask任务
public void buttonAction(View view) {
new MyAsyncTask().execute(imageUrl);
}
// 使用匿名内部类的方式使用AsyncTask
// 三个泛型分别为1.传入的网址类型 2.不需要监听进度所以使用Void 3.返回的值类型
class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected void onPreExecute() {
super.onPreExecute();
// 使progressBar可见
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected Bitmap doInBackground(String... params) {
// 网络请求图片
String imageUrl = params[0];
Bitmap bitmap = null;
InputStream inputStream = null;
HttpURLConnection connection = null;
try {
URL url = new URL(imageUrl);
connection = (HttpURLConnection) url.openConnection();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
inputStream = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null)
inputStream.close();
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (bitmap != null) {
// 成功之后返回的数据
imageView.setImageBitmap(bitmap);
progressBar.setVisibility(View.GONE);
}
}
}
}
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.example.wjn.asynctaskdemo.MainActivity">
<ImageView
android:id="@+id/main_imageview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/main_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/main_imageview"
android:visibility="gone"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/main_progress"
android:onClick="buttonAction"
android:text="缓存图片"
/>
</RelativeLayout>
这样就完成了一个简单的AsyncTask的使用。能懂就好。
下面介绍下AsyncTask常见的问题:
最多可执行数量
在API11之前的版本,内部的线程池只能执行5个线程。超过的只能等待。也就是说AsyncTask的实例超过5个的话,那么之后的线程只能等待前五个执行完成之后才能执行。这就产生了一个比较大的限制。所以出现这种情况之后,只能自己创建线程池管理Thread。
但在之后Google意识到了AsyncTask的局限性,从Android 3.0开始对AsyncTask的API做出了一些调整:使用execute()提交的任务,增加了二个预定义的线程池SERIAL_EXECUTOR(默认为序列线程池,即一次只执行一个线程任务)和THREAD_POOL_EXECUTOR(API11之前使用的线程池)
他们两个最大的区别就是SERIAL_EXECUTOR为执行完一个在执行下一个,而THREAD_POOL_EXECUTOR和以前没有任何区别,一次崔铎执行5个,5个之后的只能等待。
所以有的时候会遇到网络图片需要等待很久才能出现,这就是因为有其他的AsyncTask还没有执行完毕。所以遇到这种问题解决很简单,要么直接使用Thread要么创建一个单独的线程池(Executors.newCachedThreadPool())
生命周期
其实对于AsyncTask很多人有这样一个误解,就是一个在Activity中的AsyncTask会随着Activity的销毁而销毁。然而事实并非如此。AsyncTask会一直执行doInBackground()方法直到方法执行结束
如果cancel(boolean)调用了,则执行onCancelled(Result)方法
如果cancel(boolean)没有调用,则执行onPostExecute(Result)方法
内存问题
在Activity中使用非静态匿名内部AsyncTask类,由于Java内部类的特点,AsyncTask内部类会持有外部类的隐式引用。但由于AsyncTask的生命周期可能比Activity的长,当Activity进行销毁AsyncTask还在执行时,所以AsyncTask还会持有Activity的引用,这样就会导致Activity对象无法回收,进而产生内存泄露。
有可能出现结果丢失
在屏幕旋转等造成Activity重新创建时AsyncTask数据丢失的问题。当Activity销毁并创新创建后,还在运行的AsyncTask会持有一个Activity的非法引用即之前的Activity实例。这样就导致了onPostExecute()没有任何作用。
最后给一些自己的建议
1.少用异步 - 原因很简单,异步任务开销大,难维护等等
2.出现异步的情况可以使用 - Loaders
3.大量线程执行任务的时候一定要使用线程池
4.对于没有与UI交互的线程就用Thread吧
希望有所帮助,感谢阅读!