AsyncTask

作者: shakesbears | 来源:发表于2016-03-23 16:35 被阅读311次

    用于执行异步任务
    AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承
    需要指定以下三个泛型参数:

    Params:启动任务时输入参数的类型
    Progress:后台任务执行中返回进度值的类型
    Result:后台执行任务完成后返回结果的类型

    耗时操作:如加载网络图片等需要等待的操作,通常不允许放进主线程中

    回调方法:

    doInBackground:必须重写,异步执行后台线程将要完成的任务,所有耗时操作都将要在这个方法内执行
    onPreExecute:执行后台耗时操作前被调用,通常用于完成一些初始化操作
    onPostExecute:当doInBackground()完成后系统会自动调用onPostExecute()方法,并将doInBackground()的值传给该方法
    onProgressUpdate():在doInBackground()方法中调用publishProgress()方法更新任务执行进度后就会触发该方法

    回调方法调用顺序:
    onPreExecute()-->doInBackground()-->onProgressUpdate()-->onPostExecute()

    通过一个例子也加深了对AsyncTask的理解:

    加载一张网络图片

    作为一个不稳定的耗时操作,是严禁被放入主线程中的,所以需要在异步处理中下载图像,在UI线程中设置图像

    过程:
    1.创建一个布局文件,并创建一个imageView组件和一个progressBar组件
    image.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    
    android:padding="16dp"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent">    
    <ImageView        
    android:id="@+id/imageview"        
    android:layout_width="match_parent"        
    android:layout_height="match_parent" />    
    <ProgressBar       
    android:id="@+id/progressbar"        
    android:visibility="gone"        
    android:layout_centerInParent="true"        
    android:layout_width="wrap_content"        
    android:layout_height="wrap_content" />
    </RelativeLayout>
    

    注意progressBar 使用android:visibility="gone"设置其默认不显示出来
    android:layout_centerInParent="true"设置其居中显示

    2.创建ImageTest类,并创建内部类myAsyncTask

    public class ImageTest extends Activity {    
    private ImageView mImageView;    
    private ProgressBar mProgressBar;   
    private static  String URL="http://img1.gtimg.com/digi/pics/hv1/109/199/2039/132636829.jpg";    
    @Override    
    protected void onCreate(Bundle savedInstanceState) {        
    super.onCreate(savedInstanceState);        
    setContentView(R.layout.image);        
    mImageView=(ImageView)findViewById(R.id.imageview);        
    mProgressBar=(ProgressBar)findViewById(R.id.progressbar);       
     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) {            
    super.onPostExecute(bitmap);            //操作UI设置图像            
    mProgressBar.setVisibility(View.GONE);            
    mImageView.setImageBitmap(bitmap);       
     }        
    @Override        //传入可变长数组        
    protected Bitmap doInBackground(String... params) {           
     //获取传递进来的参数            
    String url=params[0];            
    Bitmap bitmap=null;            
    URLConnection connection;           
     InputStream is;           
     try {               
     connection=new URL(url).openConnection();                
    is=connection.getInputStream();//获取输入流                
    BufferedInputStream bis=new BufferedInputStream(is);                
    Thread.sleep(3000);                bitmap= 
    BitmapFactory.decodeStream(bis);//将输入流解析成bitmap                
    is.close();                
    bis.close();           
     } catch (IOException e) {                
    e.printStackTrace();            
    } catch (InterruptedException e) {                
    e.printStackTrace();           
     }            
    return bitmap;      
      }   
     }
    }
    
    

    其中注意:
    1.传入URL为String类型,表示需要加载的图片地址

    protected Bitmap doInBackground(String... params) {                    
    String url=params[0]; 
    

    doInBackground()方法传递进来的数组是一个可变长数组,也就是说可以传递不只一个参数,由于本例子只传入一个参数,所以只需要取出第一位即可,即params[0]

    connection=new URL(url).openConnection();
    

    这里获取网络连接对象

    is=connection.getInputStream();
    

    这里获取输入流

    BufferedInputStream bis=new BufferedInputStream(is);
    //将输入流包装
    bitmap= BitmapFactory.decodeStream(bis);
    

    将输入流解析成bitmap

    6.在线程开始之前需要先把progressBar设置成可见

    protected void onPreExecute() {    
    super.onPreExecute();    
    mProgressBar.setVisibility(View.VISIBLE);}
    
    protected void onPostExecute(Bitmap bitmap) {    super.onPostExecute(bitmap);      mProgressBar.setVisibility(View.GONE);    mImageView.setImageBitmap(bitmap);}
    

    doInBackground()方法执行后返回一个bitmap图像,获取到图像后就可以操作UI设置图像,同时将progressBar隐藏

    new myAsyncTask().execute(URL);
    

    设置传递进去的参数,首先调用onPreExecute()方法,接下来调用doInBackground()方法,最后调用onPostExecute()方法

    9.最后补充Mainfest文件,并添加activity和网络权限
    <uses-permission android:name="android.permission.INTERNET"/>
    再在主线程中添加按钮事件

    10.若觉得图片加载太快看不见progressBar可以添加Thread.sleep(3000)让其睡眠3秒再开始加载

    onPreExecute()加载进度条
    doInBackground()下载网络数据(耗时操作)
    onPostExecute()显示图片

    下面用ProgressBar来模拟进度条

    首先创建布局文件并设置progressBar样式
    progressbar.xml

    <RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"    
    android:orientation="vertical"    android:padding="16dp"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent">    
    <ProgressBar        
    android:id="@+id/pg"        
    style="?android:attr/progressBarStyleHorizontal"        
    android:layout_gravity="center_horizontal"        
    android:layout_width="match_parent"        
    android:layout_height="wrap_content"        
    android:layout_centerVertical="true"        
    android:layout_centerHorizontal="true"        
    android:layout_alignParentStart="true" />
    </RelativeLayout>
    
    

    然后创建ProgressBarTest类

    public class ProgressBarTest extends Activity {    
    ProgressBar mProgressBar;    
    private myAsyncTask mTask;    
    @Override    
    protected void onCreate(Bundle savedInstanceState) {        
    super.onCreate(savedInstanceState);        
    setContentView(R.layout.progressbar);        
    mProgressBar=(ProgressBar)findViewById(R.id.pg);        
    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(1000);                
    } 
    catch (InterruptedException e) {                    
    e.printStackTrace();               
     }           
     }          
      return null;     
       }       
     @Override     
       
    protected void onProgressUpdate(Integer... values) {            
    super.onProgressUpdate(values);            
    mProgressBar.setProgress(values[0]);       
     }   
     }
    }
    
    

    其中

    protected Void doInBackground(Void... params) {   
     for(int i=0;i<100;i++){       
     publishProgress(i);        
    try {            
    Thread.sleep(1000);      
      } 
    catch (InterruptedException e) {           
     e.printStackTrace();        }    }   
     return null;}
    

    publishProgress(i);用于调用更新进度的函数,所以将i传入

    protected void onProgressUpdate(Integer... values) {            
    super.onProgressUpdate(values);            
    mProgressBar.setProgress(values[0]);       
     }   
    将values即传入的i取出并设置给progressBar
    
    可是运行时,若退出再进入页面,progressBar会等一段时间再开始动,这是因为必须等到前面的task执行完成后才能执行后面的操作,所以要等到for循环执行完毕后才能执行下一次循环

    解决方法:使Activity的生命周期和AsyncTask的生命周期保持一致即可

    添加onPause()方法

    @Override
    //当mTask线程不为空而且状态是Running
    protected void onPause() {    
    super.onPause();    
    if(mTask!=null&&            
    mTask.getStatus() == AsyncTask.Status.RUNNING){        
    mTask.cancel(true);    
    }}
    
    

    判断当mTask线程不为空而且状态是Running时,取消线程
    可是运行后还是没解决,这是因为cancel只是将对应的AsyncTask标记为Cancel状态,并不是真正的取消线程的执行
    而且在java中也是不能直接停止线程的,必须等一个线程执行完毕后才能执行后面的操作

    所以应该在具体的异步线程中去检测状态值的改变,一旦当前AsyncTask的状态改为cancel,就跳出循环来结束掉操作,从而结束掉整个线程的逻辑

    在doInBackground()方法中加入判断

    protected Void doInBackground(Void... params) {    
    for(int i=0;i<100;i++){        
    if(isCancelled()){            
    break;        
    }       
     publishProgress(i);       
     try {           
     Thread.sleep(1000);        
    } catch (InterruptedException e) {           
     e.printStackTrace();        }    }   
     return null;}
    

    同理在更新操作中也要加入判断

    protected void onProgressUpdate(Integer... values) {        
    super.onProgressUpdate(values);        
    if (isCancelled()){           
     return;      
      }        
    mProgressBar.setProgress(values[0]);    }}
    

    总结

    AsyncTask使用注意事项:
    -必须在UI线程(onCreate())中创建AsyncTask实例
    -必须在UI线程(onCreate())中调用AsyncTask的execute()方法
    -重写的四个方法时系统自动调用的,不能手动调用
    -每个AsyncTask只能被执行一次,多次调用会引起异常
    -在AsyncTask中只有doInBackground()运行在其他线程,其他方法都运行在主线程,也就是说在其他方法中可以更新UI,而唯一在这个方法中需要做异步处理,不能再此方法中更新UI,安卓提供了onPreExecute()和onPostExecute()去承接异步处理中的操作去更新UI

    相关文章

      网友评论

          本文标题:AsyncTask

          本文链接:https://www.haomeiwen.com/subject/mmlllttx.html