在上一篇,详细介绍了AsynTask的基础知识。没有读过的朋友可以点击下面的链接:
http://www.cnblogs.com/fuly550871915/p/4892310.html
那么在这篇文章中,将编写实际的小例子,让大家看看,到底AsynTask是如何使用的。废话不多说了,例子很简单,请往下看。
一、一个异步加载网络图片的小例子
现在就跟着我一起来用AsynTask实现一个加载一张网络图片的小例子吧。首先我们看看我们要加载的这张图片是什么样的,如下:
这是我从网络张找的一张图片,它的地址是http://android-artworks.25pp.com/fs01/2015/01/05/4/110_416b936f41cb2b73dce64cbaf6bf1e16.png
下面我们就开始写代码吧。新建一个项目,别忘记给这个项目加上网络权限。然后修改它的activity_main.xml,代码如下:
<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" android:orientation="vertical" > <ImageView android:id="@+id/img_url" android:layout_width="200dp" android:layout_height="200dp" /> <ProgressBar android:id="@+id/progressbar" android:layout_width="70dp" android:layout_height="70dp" android:layout_gravity="center" /> </LinearLayout>
代码很简单,就是放置了一个ImageView用来放我们的这张网络图片的,然后再在中间放置一个进度条,用来显示进程。然后修改MainActivity的代码,如下:
1 package com.example.asynctasktest; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.net.HttpURLConnection; 6 import java.net.MalformedURLException; 7 import java.net.URL; 8 9 import org.apache.http.HttpConnection; 10 11 import android.os.AsyncTask; 12 import android.os.Bundle; 13 import android.view.View; 14 import android.widget.ImageView; 15 import android.widget.ProgressBar; 16 import android.app.Activity; 17 import android.graphics.Bitmap; 18 import android.graphics.BitmapFactory; 19 20 21 public class MainActivity extends Activity { 22 23 private ImageView img; 24 private ProgressBar progressBar; 25 private Bitmap bmp; 26 private static final String surl =" http://android-artworks.25pp.com/fs01/2015/01/05/4/110_416b936f41cb2b73dce64cbaf6bf1e16.png"; 27 protected void onCreate(Bundle savedInstanceState) { 28 super.onCreate(savedInstanceState); 29 setContentView(R.layout.activity_main); 30 31 img = (ImageView) findViewById(R.id.img_url); 32 progressBar = (ProgressBar) findViewById(R.id.progressbar); 33 MyAsyncTask mTask = new MyAsyncTask(); 34 mTask.execute(surl); 35 36 } 37 38 class MyAsyncTask extends AsyncTask<String, Void, Bitmap>{ 39 40 41 protected Bitmap doInBackground(String... params) { 42 43 String surl = params[0]; 44 HttpURLConnection connection = null; 45 46 try { 47 URL url = new URL(surl);//将字符串转化为网址对象 48 connection = (HttpURLConnection) url.openConnection();//创建网络接口 49 connection.setRequestMethod("GET"); 50 connection.setConnectTimeout(8000); 51 connection.setReadTimeout(8000); 52 InputStream in = connection.getInputStream();//获取返回的数据流 53 bmp = BitmapFactory.decodeStream(in);//获取图片 54 in.close(); 55 56 } catch (Exception e) { 57 58 e.printStackTrace(); 59 } 60 61 62 63 64 return bmp; 65 } 66 67 @Override 68 protected void onPreExecute() { 69 super.onPreExecute(); 70 //显示进度条 71 progressBar.setVisibility(View.VISIBLE); 72 } 73 74 @Override 75 protected void onPostExecute(Bitmap result) { 76 try { 77 Thread.sleep(5000);//为了观看效果,休眠5秒 78 } catch (InterruptedException e) { 79 // TODO Auto-generated catch block 80 e.printStackTrace(); 81 } 82 //关闭进度条和更新UI 83 progressBar.setVisibility(View.GONE); 84 img.setImageBitmap(bmp); 85 super.onPostExecute(result); 86 } 87 88 89 90 91 92 } 93 94 95 }
代码也很简单,在这里就用到了AsynTask来异步加载这张图片。doInBackground用来获取到这张图片并转化为Bitmap对象,然后再在onPostExecute方法中更新UI即可。好了,运行程序吧。效果图如下:
通过这个例子,是不是对怎么使用AsyncTask更加熟悉了呢?那么下面我们再来写一个例子吧,这次用来模拟一个进度条。
二、一个模拟进度条的小例子
使用的当然就是AsyncTask中的publishProgress和onProgressUpdate方法了。代码其实很简单,直接看吧。新建一个项目,修改它的activity_main.xml代码,如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 android:gravity="center"> 7 8 <Button 9 android:id="@+id/button" 10 android:layout_width="match_parent" 11 android:layout_height="wrap_content" 12 android:onClick="btnProgress" 13 android:text="按钮"/> 14 15 16 17 </LinearLayout>
先设置一个按钮是为了触发进度条的。然后再编写progressbar.xml,里面放置一个横向的进度条即可。代码如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 android:gravity="center"> 7 8 <ProgressBar 9 android:id="@+id/progressBar" 10 style="?android:attr/progressBarStyleHorizontal" 11 android:layout_width="match_parent" 12 android:layout_height="wrap_content" /> 13 14 15 16 </LinearLayout>
新建活动,用来显示这个进度条,在这个类里面我们就使用AsyncTask让进度条动起来。
1 package com.example.asynctasktest; 2 3 import android.app.Activity; 4 import android.os.AsyncTask; 5 import android.os.Bundle; 6 import android.widget.ProgressBar; 7 8 public class ProgressbarActivity extends Activity { 9 private ProgressBar progressBar; 10 11 protected void onCreate(Bundle savedInstanceState) { 12 super.onCreate(savedInstanceState); 13 setContentView(R.layout.progressbar); 14 progressBar = (ProgressBar) findViewById(R.id.progressBar); 15 MyAsyncTask mTask = new MyAsyncTask(); 16 mTask.execute(); 17 18 } 19 20 //Integer就是制定进度的衡量单位类型 21 class MyAsyncTask extends AsyncTask<Void, Integer, Void>{ 22 23 24 protected Void doInBackground(Void... params) { 25 for(int i=0;i<100;i++){ 26 //传递任务执行的当前值 27 publishProgress(i); 28 try { 29 Thread.sleep(300); 30 } catch (InterruptedException e) { 31 // TODO Auto-generated catch block 32 e.printStackTrace(); 33 } 34 } 35 return null; 36 } 37 38 39 40 //根据任务执行情况更新UI,其实就是更新进度条 41 protected void onProgressUpdate(Integer... values) { 42 super.onProgressUpdate(values); 43 progressBar.setProgress(values[0]); 44 } 45 46 } 47 }
在这个活动中,我们在doInBackground中建立一个循环,然后将循环的值通过publishProgress方法传递给onProgressUpdate方法,用来更新进度条。这样子就到了模拟进度条的效果。
不要忘记给这个活动注册哈。然后修改MainAcitivity中的代码,添加按钮的点击事件。如下:
1 package com.example.asynctasktest; 2 3 4 import android.os.AsyncTask; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.widget.ProgressBar; 8 import android.app.Activity; 9 import android.content.Intent; 10 11 12 public class MainActivity extends Activity { 13 14 15 16 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.activity_main); 20 } 21 22 public void btnProgress(View v){ 23 Intent intent = new Intent(this,ProgressbarActivity.class); 24 startActivity(intent); 25 } 26 27 28 }
下面我们运行程序,然后点击按钮,就会有下面的效果,如下:
三、AsyncTask的取消
我们以上面的进度条为例子,当进度更新到下面的情况时
我们返回到按钮界面,然后点击按钮,此时重新进入到进度条界面,如下:
而且等待了好久,才发现进度条才有开始启动,才又动起来。那这是为什么呢?这是因为我们之前的doInBackground里的线程还在进行,循环还在继续,所以AsyncTask还在执行。因此此刻重新进入进度条界面,就不可能立刻启动新的AsyncTask,而旧的呢又因为之前我们返回到按钮界面就没法重新绘制了。所以只有等旧的AsyncTask执行完毕,才会开启新的。因此你等了好久才,进度条才又重新运转起来。
其实解决这个问题,很简单,我们让AsyncTask的生命周期跟活动的生命周期一样就可以了,即在适当的时候取消AsyncTask即可。那么我们就修改一下代码,如下:
1 package com.example.asynctasktest; 2 3 import android.app.Activity; 4 import android.os.AsyncTask; 5 import android.os.Bundle; 6 import android.widget.ProgressBar; 7 8 public class ProgressbarActivity extends Activity { 9 private ProgressBar progressBar; 10 private MyAsyncTask mTask; 11 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.progressbar); 15 progressBar = (ProgressBar) findViewById(R.id.progressBar); 16 mTask = new MyAsyncTask(); 17 mTask.execute(); 18 19 } 20 21 //当跳到另外一个活动的时候调用该方法 22 protected void onPause() { 23 //如果mTask不为空,且正在运行 24 if(mTask !=null && mTask.getStatus() == AsyncTask.Status.RUNNING){ 25 mTask.cancel(true);//取消该任务 26 } 27 super.onPause(); 28 } 29 30 //Integer就是制定进度的衡量单位类型 31 class MyAsyncTask extends AsyncTask<Void, Integer, Void>{ 32 33 34 protected Void doInBackground(Void... params) { 35 for(int i=0;i<100;i++){ 36 //传递任务执行的当前值 37 publishProgress(i); 38 try { 39 Thread.sleep(300); 40 } catch (InterruptedException e) { 41 // TODO Auto-generated catch block 42 e.printStackTrace(); 43 } 44 } 45 return null; 46 } 47 48 49 50 //根据任务执行情况更新UI,其实就是更新进度条 51 protected void onProgressUpdate(Integer... values) { 52 super.onProgressUpdate(values); 53 progressBar.setProgress(values[0]); 54 } 55 56 } 57 }
红色部分是我修改的代码,当活动跳转的时候,就取消掉当前正在执行的AsyncTask。然后重新运行程序,然后当进度条启动之后几秒钟,返回到按钮界面,然后再点击按钮。按照我们的逻辑,此时应该是进度条重新启动,但是呢?我就不贴效果图了,跟上面的两张图一样。如果你做了这个实验,你就会发现,根本没什么改变。那是为什么啊??我们明明已经取消了该任务啊!!
其实原因就是因为AsyncTask的cancel方法,只是将一个AsyncTask对象标记为待取消状态,当它的进程执行完毕才会真的去取消它。所以我们在上面的代码中即使用了这个方法,也没有用。
因此要想立刻取消AsyncTask任务,必须手动取消。即在进程中,我们时刻检查AsyncTask的状态是否为取消的状态,如果是,立刻人为终止线程即可。再修改代码,如下:
1 package com.example.asynctasktest; 2 3 import android.app.Activity; 4 import android.os.AsyncTask; 5 import android.os.Bundle; 6 import android.widget.ProgressBar; 7 8 public class ProgressbarActivity extends Activity { 9 private ProgressBar progressBar; 10 private MyAsyncTask mTask; 11 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.progressbar); 15 progressBar = (ProgressBar) findViewById(R.id.progressBar); 16 mTask = new MyAsyncTask(); 17 mTask.execute(); 18 19 } 20 21 //当跳到另外一个活动的时候调用该方法 22 protected void onPause() { 23 //如果mTask不为空,且正在运行 24 if(mTask !=null && mTask.getStatus() == AsyncTask.Status.RUNNING){ 25 mTask.cancel(true);//取消该任务 26 } 27 super.onPause(); 28 } 29 30 //Integer就是制定进度的衡量单位类型 31 class MyAsyncTask extends AsyncTask<Void, Integer, Void>{ 32 33 34 protected Void doInBackground(Void... params) { 35 for(int i=0;i<100;i++){ 36 if(isCancelled()){//如果为取消状态,立刻break,跳出循环 37 break; 38 } 39 //传递任务执行的当前值 40 publishProgress(i); 41 42 try { 43 Thread.sleep(300); 44 } catch (InterruptedException e) { 45 // TODO Auto-generated catch block 46 e.printStackTrace(); 47 } 48 } 49 return null; 50 } 51 52 53 54 //根据任务执行情况更新UI,其实就是更新进度条 55 protected void onProgressUpdate(Integer... values) { 56 super.onProgressUpdate(values); 57 if(isCancelled()){ 58 return;//如果为 取消状态,就立刻跳出 59 } 60 progressBar.setProgress(values[0]); 61 } 62 63 } 64 }
红色地方是我修改的地方,在线程里面合适的地方我们判断当前的AsyncTask任务是不是取消状态,如果是就立刻跳出线程。然后再运行程序,按照上面的流程走一遍。我们发现一切正确了。无论什么情况,只要点击按钮进去,进度条就会立刻重新启动。效果图我就不贴了。
总结一下:
AsyncTask的cancel()方法并不是真的去立刻取消任务,只是将任务标记为取消状态,如果想立刻取消Async,必须手动根据这个状态去停止相应的进程。