AsyncTask基础
AsyncTask的基本构成
1-1 AsyncTask概述
为什么需要使用异步任务?
——Android单线程模式
——耗时操作放在非线程中执行
我们知道,Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直接操作UI的.这样的好处是保证了UI的稳定性和准确性,避免多个线程同时对UI进行操作而造成UI的混乱.但Android是一个多线程的操作系统,我们总不能把所有的任务都放在主线程中进行实现,比如网络操作,文件读取等耗时操作,如果全部放到主线程去执行,就可能会造成后面任务的阻塞.Android会去检测这种阻塞,当阻塞时间太长的时候,就会抛出Application Not Responsed(ANR)错误.所以我们需要将这些耗时操作放在非主线程中去执行.这样既避免了Android的单线程模型,又避免了ANR.
AsyncTask为何而生?
——子线程中更新UI
——封装、简化异步操作
提到异步任务,我们能想到用线程,线程池去实现.确实,Android给我们提供了主线程与其他线程通讯的机制.但同时,Android也给我们提供了一个封装好的组件--AsyncTask.利用AsyncTask,我们可以很方便的实现异步任务处理.AsyncTask可以在子线程中更新UI,也封装简化了异步操作.使用线程,线程池处理异步任务涉及到了线程的同步,管理等问题.而且当线程结束的时候还需要使用Handler去通知主线程来更新UI.而AsyncTask封装了这一切,使得我们可以很方便的在子线程中更新UI.
1-2 AsyncTask基本结构介绍
1. 用处:
将耗时操作放在非主线程中执行,既保证了Android单线程模型,也保证了程序的响应(不出现ANR)
AsyncTask在子线程中更新UI,封装、简化异步操作
2. AsyncTask<Params, Progress, Result>
是一个抽象类,通常用于被继承,继承AsyncTask需要指定如下三个泛型参数:
——Params:启动任务时输入参数的类型
——Progress:后台任务执行中返回进度值的类型
——Result:后台执行任务完成后返回结果的类型
注意:参数不是一定要使用, private class MyTask extends AsyncTask<Void, Void, Void> { ... }
3. 在继承AsyncTask的子类中需要重写的回调方法
onPreExecute()->doInBackground(Params...)->调用publishProgress(Progress...)->onProgressUpdate(Progress...)->doInBackground(Params...)执行结束->onPostExecute(Result)
MainActivity.java
package com.example.android3_asynctask; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyAsyncTask task = new MyAsyncTask(); task.execute(); } }
MyAsyncTask.java
package com.example.android3_asynctask; import android.os.AsyncTask; import android.util.Log; class MyAsyncTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... arg0) { // TODO Auto-generated method stub Log.i("yzx", "doInBackground"); publishProgress(); return null; } @Override protected void onPreExecute() { // TODO Auto-generated method stub Log.i("yzx", "onPreExecute"); super.onPreExecute(); } @Override protected void onPostExecute(Void result) { // TODO Auto-generated method stub Log.i("yzx", "onPostExecute"); super.onPostExecute(result); } @Override protected void onProgressUpdate(Void... values) { // TODO Auto-generated method stub Log.i("yzx", "onProgressUpdate"); super.onProgressUpdate(values); } }
在没有加publishProcess();
加publishProcess();
AsyncTask的使用示例
onPreExecute() 显示进度条
onPostExcute()隐藏进度条 都可以访问UI线程
mytask.execute(args)中传入的参数就是doInBackground中的参数
在onPreExecute()方法中
mProgressBar.setVisibility(View.VISIBLE);//显示进度条
在onPostExectute(Bitmap bitmap)方法中,参数是doInBackground()方法返回的参数
mProgressBar.setVisibility(View.GONE);//将进度条隐藏
mImageView.setImageBitmap(bitmap);//将图片设置为解析出来的网络图片
然后在onCreate方法中
new MyAsyncTask().execute(URL);//开启AsyncTask的异步线程操作,设置传递进去的
网络操作作为不稳定的废时操作,从android 4.0开始就被严禁放入主线程中
通常采用在异步线程处理→下载图像 在UI线程→设置图像
ProgressBar XML属性 visibility="gone"可设置为默认状态下为隐藏
Image.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:padding="16dp" android:layout_height="match_parent"> <ImageView android:id="@+id/image_a" android:layout_width="match_parent" android:layout_height="match_parent" /> <ProgressBar android:id="@+id/progress_a" android:visibility="gone" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
activity_main.xml
<?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" tools:context="com.example.yzx.android_asynctask.MainActivity"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Image Test" android:onClick="loadImage" /> </LinearLayout> Image.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:padding="16dp" android:layout_height="match_parent"> <ImageView android:id="@+id/image_a" android:layout_width="match_parent" android:layout_height="match_parent" /> <ProgressBar android:id="@+id/progress_a" android:visibility="gone" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
ImageTest
package com.example.yzx.android_asynctask; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; import java.io.BufferedInputStream; import java.io.IOException; import java.net.URL;//导入URL的包 import java.io.InputStream; import java.net.URLConnection; /** * Created by yangzixing on 2016/3/14. */ public class ImageTest extends Activity { private ImageView mImageView; private ProgressBar mProgressBar; private static String URL="//img-my.csdn.net/uploads/201504/12/1428806103_9476.png"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.image); mImageView=(ImageView)findViewById(R.id.image_a); mProgressBar= (ProgressBar) findViewById(R.id.progress_a); //设置传递进去的参数 new MyAsyncTask().execute(URL); } class MyAsyncTask extends AsyncTask<String,Void,Bitmap>{ @Override protected void onPreExecute() { super.onPreExecute(); mProgressBar.setVisibility(View.VISIBLE);//显示进度条 } @Override protected void onPostExecute(Bitmap bitmap) {//操作UI,设置图片 super.onPostExecute(bitmap); mProgressBar.setVisibility(View.GONE);//隐藏进度条 mImageView.setImageBitmap(bitmap); } /* String...params------可变长的数组,可以传递不止一个参数进来 */ @Override protected Bitmap doInBackground(String... params) { //获取传递进来的参数 String url=params[0];//取出对应url Bitmap bitmap=null; URLConnection connection;//定义网络连接对象 InputStream inputStream;//用于获取数据的输入流 try { //获取connection的对象。 connection=new URL(url).openConnection(); //获取输入流 inputStream=connection.getInputStream(); BufferedInputStream bis=new BufferedInputStream(inputStream); //通过decodeStream将输入流解析成Bitmap bitmap= BitmapFactory.decodeStream(bis); inputStream.close(); bis.close(); } catch (IOException e) { e.printStackTrace(); } //将bitmap作为返回值 return bitmap ; } } }
MainActivity
package com.example.yzx.android_asynctask; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyAsyncTask task=new MyAsyncTask(); task.execute(); } public void loadImage(View view){ startActivity(new Intent(this,ImageTest.class)); } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.yzx.android_asynctask"> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ImageTest"></activity> </application> </manifest>
AsyncTask模拟进度条
ProgressBarTest
package com.example.yzx.android_asynctask; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.widget.ProgressBar; /** * Created by yangzixing on 2016/3/14. */ public class ProgressBarTest extends Activity { private ProgressBar progressBar; private MyAsyncTask mTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.progressbar); progressBar= (ProgressBar) findViewById(R.id.pgb); mTask=new MyAsyncTask(); mTask.execute(); } class MyAsyncTask extends AsyncTask<Void,Integer,Void>{ @Override protected Void doInBackground(Void... params) { //模拟进度更新 for(int i=0;i<100;i++){ publishProgress(i); try {//增加睡眠时间,延缓更新的进度 Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); progressBar.setProgress(values[0]); } }
Progressbar.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:padding="16dp" android:layout_height="match_parent"> <ProgressBar style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pgb" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> </RelativeLayout>
activity_main.xml
<?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:orientation="vertical" android:layout_height="match_parent" tools:context="com.example.yzx.android_asynctask.MainActivity"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Image Test" android:onClick="loadImage" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="ProgressBar Test" android:onClick="loadProgress" /> </LinearLayout>
MainActivity
package com.example.yzx.android_asynctask; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyAsyncTask task=new MyAsyncTask(); task.execute(); } public void loadImage(View view){ startActivity(new Intent(this, ImageTest.class)); } public void loadProgress(View view){ startActivity(new Intent(this,ProgressBarTest.class)); } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.yzx.android_asynctask"> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ImageTest"></activity> <activity android:name=".ProgressBarTest"></activity> </application> </manifest>
如何取消AsyncTask
当在上一次进度条没有完全满的时候返回,再次点击Progress Test没有反应,因为上一次返回的进度条得满的时候才开始进度。
解决方法:让AsyncTask的生命周期与Activity的生命周期保持一致
AsyncTask.cancel(true)只是使该异步线程标识cancle,用户在任何地方都不能粗暴地直接结束一个线程,因此需要在线程的执行过程中去检测isCancled()标识
package com.example.yzx.android_asynctask; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.widget.ProgressBar; /** * Created by yangzixing on 2016/3/14. */ public class ProgressBarTest extends Activity { private ProgressBar progressBar; private MyAsyncTask mTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.progressbar); progressBar= (ProgressBar) findViewById(R.id.pgb); mTask=new MyAsyncTask(); mTask.execute(); } @Override protected void onPause() { super.onPause(); //mTask不为空且是running的状态,这仅是发送请求 if(mTask!=null && mTask.getStatus()==AsyncTask.Status.RUNNING){ //cancel方法只是将对应的AsyncTask标记为cancel状态,并不是真正的取消 mTask.cancel(true); } } class MyAsyncTask extends AsyncTask<Void,Integer,Void>{ @Override protected Void doInBackground(Void... params) { //模拟进度更新 for(int i=0;i<100;i++){ if(isCancelled()){ break; } publishProgress(i); try {//增加睡眠时间,延缓更新的进度 Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); if(isCancelled()){ return; } progressBar.setProgress(values[0]); } } }
总结
AsyncTask注意事项:
必须在UI线程中创建AsyncTask的实例。
必须在UI线程中调用AsyncTask的execute()方法
重写的四个方法是系统自动调用的,不应手动调用
每个AsyncTask只能被执行一次,多次调用将会引发异常
只有doInbackground的方法是在其他线程执行,不能执行UI操作,
其他方法可以对UI执行操作更新UI.