Android异步线程之AsyncTask

       AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后会把执行的进度和最终结果传递给主线程并更新UI。这里Android已经帮我们封装好了AsyncTask(AsyncTask是对Thread+Handlerde 的封装),即使你对异步消息处理机制完全不了解,也可以简单的从子线程切换到主线程。

       AsyncTask是一个抽象类。那么要使用AsyncTask,我们需要写一个类去继承它;在继承时我们可以为AsyncTask类指定3个泛型参数,分别为Params(开始时),Progress(执行时)和Result(结束后)。

Params:启动任务执行的输入参数,如HTTP请求的URL。

Progress:后台任务执行的百分比。后台任务执行时,如果需要对在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。

Result:后台执行任务最终返回的结果类型。

例如:如果AsyncTask不需要传递具体参数,那么这三个泛型参数可以使用Void代替

 

 
 
class MyAsyncTask extends AsyncTask<Params, Progress, Result>{...} 

 

我们来一个完整的简单例子:

 

 
//创建一个类继承AsyncTask并设置好泛型参数 class demoAsyncTask extends AsyncTask<Void,Intent,Boolean>{ /**  * 该方法在主线程中执行,将在execute(Params… params)被调用后  * 执行,一般用来做一些UI的准备工作,如在界面上显示一个进度条。  */  @Override  protected void onPreExecute() { progressDialog.show();//显示进度条对话框  } /**  * 抽象方法,必须实现,该方法在线程池中执行,用于执行异步任务,  * 将在onPreExecute方法执行后执行。其参数是一个可变类型,  * 表示异步任务的输入参数,在该方法中还可通过  * publishProgress(Progress… values)来更新实时的任务进度,  * 而publishProgress方法则会调用onProgressUpdate方法。  * 此外doInBackground方法会将计算的返回结果传递给  * onPostExecute方法。  * */  @Override  protected Boolean doInBackground(Void... params) { try { while (true){ int downloadPercent=doDownload();  publishProgress(downloadPercent);  if (downloadPercent>=100){ break;  } } }catch (Exception e){ return false;  } return null;  } /**  *在主线程中执行,该方法在publishProgress(Progress… values)  *方法被调用后执行,一般用于更新UI进度,如更新进度条的当前进度。  * @param values  */  @Override  protected void onProgressUpdate(Intent... values) { //在这里更新下载进度  progressDialog.setMessage("Download"+values[0]+"%");  } /**  *在doInBackground 执行完成后,onPostExecute 方法将被UI线程  *调用,doInBackground 方法的返回值将作为此方法的参数传递到  *UI线程中,并执行一些UI相关的操作,如提醒任务执行的结果,  *以及关闭进度条对话框等等。  * @param aBoolean  */  @Override  protected void onPostExecute(Boolean aBoolean) { progressDialog.dismiss();//关闭进度对话框  //在这里提示下载结果  if (aBoolean){ Toast.makeText(MyAsyncTask.this, "aBoolean",  Toast.LENGTH_SHORT).show();  }else { Toast.makeText(MyAsyncTask.this,"failed",  Toast.LENGTH_SHORT).show();  } } }

 

启动任务只需编写:new demoAsyncTask().execute();

 

//使用AsyncTask的诀窍:在doInBackground方法中执行具体的耗时操作,
//在onProgressUpdate方法中进行UI操作,
//在onPostExecute方法中执行一些任务的收尾工作

 

注意:老司机们提醒。为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
1) Task的实例必须在UI thread中创建
2) execute方法必须在UI thread中调用
3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法
4) 该task只能被执行一次,否则多次调用时将会出现异常
doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground 接受的参数,第二个为显示进度的参数,第三个为doInBackground返回和onPostExecute传入的参数。

 

         现在,我们来思考一些问题,Android异步线程AsyncTask我们学得差不多了,想想,AsyncTask+Okhttp(当然,AsyncTask还可以和其他网络技术合作,比如AsyncTask+HttpURLConnection等等,这里强烈推荐使用AsyncTask+OkHttp)实现异步下载图片。首先要考虑AsyncTask的三个泛型参数是什么数据类型?AsyncTask单独使用的时候需要传入url,Okhttp单独使用也是需要传入url,那么它俩合作,url作为参数传给谁去完成任务呢?还有具体的实现细节,它俩之间的数据通过什么方式方法来传递?先来个简单的例子:

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="AsyncTask+OkHttp网络下载图片"
        android:id="@+id/button" />

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/imageView" />

</LinearLayout>
Java代码:
public class MainActivity extends AppCompatActivity {
    private String url="http://photocdn.sohu.com/20110927/Img320705637.jpg";
    private Button mButton;
    private ImageView mImageView;
    private Bitmap bitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView=(ImageView) findViewById(R.id.imageView);
        mButton=(Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //在UI Thread当中实例化AsyncTask对象,并调用execute方法
                new MyAsyncTask().execute(url);
            }
        });
    }
    public class MyAsyncTask extends AsyncTask<String,Void,Bitmap>{
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                OkHttpClient client=new OkHttpClient();
                Request request=new Request.Builder()
                        .url(url).build();
                Response response=client.newCall(request).execute();
                InputStream is=response.body().byteStream();
                bitmap= BitmapFactory.decodeStream(is);
                is.close();
            }catch (Exception e){
                e.printStackTrace();
            }
            return bitmap;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            mImageView.setImageBitmap(bitmap);
        }
    }
}
效果图:

差点忘了说:记得申请权限

<!-- 授权手机能够访问网络 -->
<uses-permission android:name="android.permission.INTERNET" />

还有使用OkHttp需要下载它的两个依赖包:在build.gradle(Module:app) 中的dependencies添加{

 

compile 'com.squareup.okhttp3:okhttp:3.4.1'

 

}

 

栗子中我们只用到了两个方法,doInBackground和onPostExecute,还有两个方法没有用到,还得继续优化栗子,增加一个下载提示框提示用户图片正在下载中。布局不变,下面贴出Java代码:

 

public class MainActivity extends AppCompatActivity {
    private String url="http://photocdn.sohu.com/20110927/Img320705637.jpg";
    private Button mButton;
    private ImageView mImageView;
    private Bitmap bitmap;
    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView=(ImageView) findViewById(R.id.imageView);
        //弹出要给ProgressDialog
        progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setTitle("提示信息");
        progressDialog.setMessage("正在下载中,请稍后......");
        //设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失
        progressDialog.setCancelable(false);
        //设置ProgressDialog样式为圆圈的形式
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);

        mButton=(Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //在UI Thread当中实例化AsyncTask对象,并调用execute方法
                new MyAsyncTask().execute(url);
            }
        });
    }
    public class MyAsyncTask extends AsyncTask<String,Void,Bitmap>{
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //在onPreExecute()中我们让ProgressDialog显示出来
            progressDialog.show();
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                OkHttpClient client=new OkHttpClient();
                Request request=new Request.Builder()
                        .url(url).build();
                Response response=client.newCall(request).execute();
                InputStream is=response.body().byteStream();
                bitmap= BitmapFactory.decodeStream(is);
                is.close();
            }catch (Exception e){
                e.printStackTrace();
            }
            return bitmap;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //更新我们的ImageView控件
            mImageView.setImageBitmap(bitmap);
            //使ProgressDialog框消失
            progressDialog.dismiss();
        }
    }
}
效果图:

 

 

 

再继续优化栗子:这次我们用到更新UI的功能,一样的布局没变,下面贴Java代码:

 

public class MainActivity extends AppCompatActivity {
    private String url="http://photocdn.sohu.com/20110927/Img320705637.jpg";
    private Button mButton;
    private ImageView mImageView;
    private Bitmap bitmap;
    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView=(ImageView) findViewById(R.id.imageView);
        //弹出要给ProgressDialog
        progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setTitle("提示信息");
        progressDialog.setMessage("正在下载中,请稍后......");
        //设置setCancelable(false);表示我们不能取消这个弹出框,
        // 等下载完成之后再让弹出框消失
        progressDialog.setCancelable(false);
//        //设置ProgressDialog样式为圆圈的形式
//        progressDialog.setProgressStyle(ProgressDialog
//                .STYLE_SPINNER);
        //设置ProgressDialog样式为水平的样式
        progressDialog.setProgressStyle(ProgressDialog
                .STYLE_HORIZONTAL);
        
        mButton=(Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //在UI Thread当中实例化AsyncTask对象,并调用execute方法
                new MyAsyncTask().execute(url);
            }
        });
    }/**
      * 定义一个类,让其继承AsyncTask这个类
 * Params: String类型,表示传递给异步任务的参数类型是String,通常指定的是URL路径
 * Progress: Integer类型,进度条的单位通常都是Integer类型
 * Result:Bitmap类型,表示我们下载好的图片以Bitmap返回
 * @author xiaoluo
 *
 */
    public class MyAsyncTask extends AsyncTask<String,Integer
            ,Bitmap>{
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //在onPreExecute()中我们让ProgressDialog显示出来
            progressDialog.show();
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                OkHttpClient client=new OkHttpClient();
                Request request=new Request.Builder()
                        .url(url).build();
                Response response=client.newCall(request).execute();
                InputStream is=response.body().byteStream();
                bitmap= BitmapFactory.decodeStream(is);
                //每次读取后累加的长度
                  long file_length = 0;
                int length = 0;
                //每次读取1024个字节
                  byte[] data = new byte[1024];
                while (-1!=(length=is.read(data))){
                    //每读一次,就将file_length累加起来
                    file_length += length;
                    //得到当前图片下载的进度
                    int mData=((int) (file_length/is.read())*100);
                    publishProgress(mData);
                }
                is.close();
            }catch (Exception e){
                e.printStackTrace();
            }
            return bitmap;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            //更新ProgressDialog的进度条
            progressDialog.setProgress(values[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //更新我们的ImageView控件
            mImageView.setImageBitmap(bitmap);
            //使ProgressDialog框消失
            progressDialog.dismiss();
        }
    }
}
效果图:

 

 

 

在写这个例子的时候,还是学到了很多基础的知识,说实话,有时候你不动手打代码,你都不知道自己有些知识点依然懵懂。例子涉及的知识点,本人也去好好巩固,下面分享一些知识:

 

/**
 * 以字节为单位读取文件,常用于读二进制文件,如图片、声音、影像等文件。
 * 所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件
 * 的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件
 *(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列
*/
 
/**    * 把Bitmap转Byte    */ 
public static byte[] Bitmap2Bytes(Bitmap bm){    
        ByteArrayOutputStream baos = new ByteArrayOutputStream();    
        bm.compress(Bitmap.CompressFormat.PNG, 100, baos);    
        return baos.toByteArray();    
 }  
 
//android将图片内容解析成字节数组,将字节数组转换为 // ImageView可调用的Bitmap对象, //图片缩放,把字节数组保存为一个文件,把Bitmap转Byte 
 
/**    * @param 图片缩放    * @param bitmap 对象    * @param w 要缩放的宽度    * @param h 要缩放的高度    * @return newBmp 新 Bitmap对象   */
public static Bitmap zoomBitmap(Bitmap bitmap, int w, int h){    
        int width = bitmap.getWidth();    
        int height = bitmap.getHeight();    
        Matrix matrix = new Matrix();    
        float scaleWidth = ((float) w / width);    
        float scaleHeight = ((float) h / height);    
        matrix.postScale(scaleWidth, scaleHeight);    
        Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, width, height,    
                 matrix, true);    
             return newBmp;    
} 

 

/**
 * 将字节数组转换为ImageView可调用的Bitmap对象  
 */
public static Bitmap getPicFromBytes(byte[] bytes,BitmapFactory.Options opts) {    
      if (bytes != null)    
          if (opts != null)    
               return BitmapFactory.decodeByteArray(bytes, 0, bytes.length,opts);    
          else    
              return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);    
      return null;    
 }  
/**  
 * @param 将图片内容解析成字节数组  
 * @param inStream  
 * @return byte[]  
 * @throws Exception  
 */
public static byte[] readStream(InputStream inStream) throws Exception {    
            byte[] buffer = new byte[1024];    
            int len = -1;    
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();    
                while ((len = inStream.read(buffer)) != -1) {    
                    outStream.write(buffer, 0, len);
                }    
                byte[] data = outStream.toByteArray();
                outStream.close();
                inStream.close();
                return data;
} 
从资源中获取Bitmap
Java代码  
Resources res=getResources();  
Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic);  
 
Bitmap → byte[]
Java代码  
private byte[] Bitmap2Bytes(Bitmap bm){  
    ByteArrayOutputStream baos = new ByteArrayOutputStream();    
    bm.compress(Bitmap.CompressFormat.PNG, 100, baos);    
    return baos.toByteArray();  
   }  
 
byte[] → Bitmap
Java代码  
private Bitmap Bytes2Bimap(byte[] b){  
            if(b.length!=0){  
                return BitmapFactory.decodeByteArray(b, 0, b.length);  
            }  
            else {  
                return null;  
            }  
      }  
*/

希望这些知识点对大家有帮助!

如果大家对OkHttp还陌生的话,请看另一篇博客:Android网络技术之OkHttp框架

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值