Android异步线程之AsyncTask

来源:互联网 发布:蜂窝数据qq打不开 编辑:程序博客网 时间:2024/05/17 07:14

       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类型 * ResultBitmap类型,表示我们下载好的图片以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)的储存,在磁盘上保留的并不是文件 * 的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件 *(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列*/
/**  BitmapByte   */
public static byte[] Bitmap2Bytes(Bitmap bm){            ByteArrayOutputStream baos = new ByteArrayOutputStream();            bm.compress(Bitmap.CompressFormat.PNG, 100, baos);            return baos.toByteArray();     }  
//android将图片内容解析成字节数组,将字节数组转换为// ImageView可调用的Bitmap对象,//图片缩放,把字节数组保存为一个文件,BitmapByte 
/**   * @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;
从资源中获取BitmapJava代码  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[] → BitmapJava代码  private Bitmap Bytes2Bimap(byte[] b){              if(b.length!=0){                  return BitmapFactory.decodeByteArray(b, 0, b.length);              }              else {                  return null;              }        }  */

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

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

0 0
原创粉丝点击