文档章节

安卓AsyncTack详解

htq
 htq
发布于 2016/07/26 09:40
字数 1894
阅读 9
收藏 0

我们知道安卓中的UI线程不是线程安全的,即不能在UI线程中进行耗时操作,所以我们通常的做法是开启一个子线程来进行耗时操作,然后将处理后的结果运用Handler机制传递给UI线程,在UI线程中根据处理后的结果更新界面。如从网络上获取一张图片显示到界面上的一个ImageView控件上,我们会开启一个子线程来进行网络请求获取图片,然后运用Handler告诉主线程图片已经获取到,可以刷新界面显示图片。即运用Handler+Thread来处理这种请求,事实上这种情况在安卓开发中使用非常频繁,因此谷歌也为了简化开发步骤,提供了AsyncTask这个类,即异步任务类,它是为了在子线程中更新UI界面而存在的。

一AsyncTask基本用法

我们首先来看一下其类的定义:

public abstract class AsyncTask<Params, Progress, Result>
可以看到AsyncTask是一个抽象类,这说明它至少存在一种抽象方法,这个抽象方法就是我们必须重写的doInBackground(Params... params),另外它包含三个泛型参数Params, Progress, Result。

Params: 顾名思义,就是参数的意思,这个泛型指定的是我们传递给异步任务执行时的参数的类型
Progress:顾名思义,就是进度的意思,这个泛型指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型
Result: 顾名思义,就是结果的意思,这个泛型指定的异步任务执行完后返回给UI线程的结果的类型

下面我们来看一下AsyncTask中的重要方法:

protected void onPreExecute() { }
protected abstract Result doInBackground(Params... params);
protected void onProgressUpdate(Progress... values) { }
protected void onPostExecute(Result result) { }
可以看到在这四个最重要的方法中doInBackground(Params... params)是唯一一个抽象方法。下面我们一一介绍:

1onPreExecute(): 这个方法是在执行异步任务之前的时候执行,是在UI Thread当中执行的,通常我们在这个方法里做一些UI控件的初始化的操作,如弹出ProgressDialog

2doInBackground(Params... params):在onPreExecute()方法执行完之后,会马上执行这个方法,这个方法就是来处理异步任务的方法,Android操作系统会在后台的线程池当中开启一个worker thread来执行我们的这个方法,所以这个方法是在worker thread当中执行的,我们应该在此处理耗时操作,但是注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。

3onProgressUpdate(Progess... values): 这个方法也是在UI Thread当中执行的,我们在异步任务执行的时候,可能需要将执行的进度返回给我们的UI界面,例如下载一张网络图片,我们可能需要时刻显示其下载的进度,就可以使用这个方法来更新我们的进度。当我们在在 doInBackground 方法中调用 publishProgress(Progress) 的方法来后, onProgressUpdate 方法将会被调用。

4onPostExecute(Result... result): 当我们的异步任务执行完之后通过return语句进行返回时,就会将结果作为参数传递到此方法中,这个方法也是在UI Thread当中调用的,我们可以在此方法中将返回的结果显示在UI控件上。

了解了上述四个重要的方法后,我们就可以实现自己的AsyncTask,主要逻辑就是重写上述四个方法,在这些方法中根据业务逻辑进行相应的操作,如一个从网络上获取图片的异步任务代码如下:

public class MainActivity extends Activity
{
    private Button button;
    private ImageView imageView;
    private ProgressDialog progressDialog;
    private final String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        button = (Button)findViewById(R.id.button);
        imageView = (ImageView)findViewById(R.id.imageView);
      
        progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setTitle("提示信息");
        progressDialog.setMessage("正在下载中,请稍后......");
        //    设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失
        progressDialog.setCancelable(false);
        //    设置ProgressDialog样式为水平的样式
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                new MyAsyncTask().execute(IMAGE_PATH);
            }
        });
    }
    
    /**
     * 定义一个类,让其继承AsyncTask这个类
     * Params: String类型,表示传递给异步任务的参数类型是String,因为此异步任务是从网络上获取图片,所以通常指定的是URL路径
     * Progress: Integer类型,进度条的单位通常都是Integer类型
     * Result:byte[]类型,因为我们要存储从网络上获取的图片,然后将其以字节数组形式返回
     *
     */
    public class MyAsyncTask extends AsyncTask<String, Integer, byte[]>
    {
        @Override
        protected void onPreExecute()
        {
            super.onPreExecute();
            //    在onPreExecute()中我们让ProgressDialog显示出来
            progressDialog.show();
        }
        @Override
        protected byte[] doInBackground(String... params)
        {
            //    通过Apache的HttpClient来访问请求网络中的一张图片
            HttpClient httpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(params[0]);
            byte[] image = new byte[]{};
            try
            {
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                InputStream inputStream = null;
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                if(httpEntity != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
                {
                    //    得到文件的总长度
                    long file_length = httpEntity.getContentLength();
                    //    每次读取后累加的长度
                    long total_length = 0;
                    int length = 0;
                    //    每次读取1024个字节
                    byte[] data = new byte[1024];
                    inputStream = httpEntity.getContent();
                    while(-1 != (length = inputStream.read(data)))
                    {
                        //    每读一次,就将total_length累加起来
                        total_length += length;
                      
                        byteArrayOutputStream.write(data, 0, length);
                        //    得到当前图片下载的进度
                        int progress = ((int)(total_length/(float)file_length) * 100);
                        //    调用<span style="font-family: Arial, Helvetica, sans-serif;">publishProgress(progress);</span>将当前进度传给给onProgressUpdate方法
                        publishProgress(progress);
                    }
                }
                image = byteArrayOutputStream.toByteArray();
                inputStream.close();
                byteArrayOutputStream.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                httpClient.getConnectionManager().shutdown();
            }
            return image;
        }
        @Override
        protected void onProgressUpdate(Integer... values)
        {
            super.onProgressUpdate(values);
            //    更新ProgressDialog的进度条
            progressDialog.setProgress(values[0]);
        }
        @Override
        protected void onPostExecute(byte[] result)
        {
            super.onPostExecute(result);
            //    将doInBackground方法返回的byte[]解码成要给Bitmap
            Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
            //    更新我们的ImageView控件
            imageView.setImageBitmap(bitmap);
            //    使ProgressDialog框消失
            progressDialog.dismiss();
        }
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}


二总结

首先我们来梳理一下AsyncTask的处理过程,然后讲一下在使用AsyncTask时应该注意的地方

当我们在UI线程中调用execute(Params... params)开启异步任务后,execute方法会调用onPreExecute()方法,在该方法中doInBackground(Params... params)将被调用,且execute(Params... params)中的params参数将会被传递给doInBackground(Params... params)中的params参数,在该方法调用完成后,会自动调用onPostExecute(Result result)方法,且会将在doInBackground中return返回的结果传递给onPostExecute(Result result)中的result参数。


如果在doInBackground(Params... params)中

调用了publishProgress(Progress... values)方法,则在该方法中会调用onProgressUpdate(Progress... values)方法将被调用且会将publishProgress(Progress... values)中的

Progress... values参数传递给onProgressUpdate(Progress... values)中的values。

上述文字叙述可能不太直观,下面是它们运行调用的顺序直观表示:


使用AsyncTask时应该注意的地方:

1AsyncTask的对象必须在UI Thread当中实例化
2execute方法必须在UI Thread当中调用

3不能在doInBackground(Params... params)中更改UI组件,UI的更新必须在onProgressUpdate中完成。
4不要手动的去调用AsyncTask的onPreExecute, doInBackground, publishProgress, onProgressUpdate, onPostExecute方法,这些都是由Android系统自动调用的
5AsyncTask任务只能被执行一次,如果执行第二次将会抛出异常。


好了以上就是本人理解的关于AsyncTask的相关知识,看官如果觉得不错请不要吝啬点击一下下方的“顶”按钮给我一点鼓励哦!微笑

本文转载自:http://blog.csdn.net/htq__/article/details/51232659

共有 人打赏支持
htq

htq

粉丝 19
博文 67
码字总数 1007
作品 3
武汉
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
08/08
0
0
requestWindowFeature(featrueId)简述

我们在开发程序是经常会需要软件全屏显示、自定义标题(使用按钮等控件)和其他的需求,今天这一讲就是如何控制Android应用程序的窗体显示.   首先介绍一个重要方法那就是requestWindowFe...

聂磊
2013/05/26
0
0
android白盒测试所需其他安卓开发内容链接整理

Android Intent实现Activity之间跳转并传值:http://www.linuxidc.com/Linux/2011-04/34228.htm android Intent机制详解:http://www.oschina.net/question/56506567909 Android开发中Inten......

智能小松鼠
2014/12/18
0
0
preferenceActivity详解

首先从需求说起 即:现有某Activity专门用于手机属性设置 那么应该如何做呢? 根据已学知识 很快一个念头闪过 即:Activity + Preference 组合 前者用于界面构建 后者用于设置数据存放 其实 ...

月在青天
2013/12/29
0
0
LayoutInflater详解

 分析时候遇到的,记录一个 作用: 1、对于一个没有被载入或者想要动态载入的界面, 都需要使用inflate来载入. 2、对于一个已经载入的Activity, 就可以使用实现了这个Activiyt的的findViewByI...

骑牛找牛
2014/01/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

React 服务器渲染原理解析与实践

网盘下载地址 React 服务器渲染原理解析与实践 本套课程,讲解了React中SSR技术的整个搭建思路及流程,完整的从原理上讲清楚了SSR的概念,重点在于讲解编写SSR框架遇到的各种知识点,以及细节...

qq__2304636824
27分钟前
0
0
sourcetree 离线免注册登录安装教程

Sourcetree是一个优秀的git可视化管理工具,深受开发者喜爱Sourcetree官网,但是在安装时需要谷歌账户登录,需要翻qiang才可以,此一点一直被人们所诟病。今天本教程就为大家提供离线免登陆安...

QQZZFT
56分钟前
1
0
使用 PostgreSQL 解决一个实际的统计分析问题

使用 PostgreSQL 解决一个实际的统计分析问题作者:老农民(刘启华)Email: 46715422@qq.com 之前有个朋友扔给我一个奇葩需求,他们公司之前做了一批问卷调查,全部都是统一格式的excel...

新疆老农民
59分钟前
8
0
TypeScript基础入门之高级类型的映射类型

转发 TypeScript基础入门之高级类型的映射类型 高级类型 映射类型 一个常见的任务是将一个已知的类型每个属性都变为可选的: interface PersonPartial {    name?: string;    age?...

durban
今天
1
0
Dubbo源码分析(6):Dubbo内核实现之基于SPI思想Dubbo内核实现

SPI接口定义 定义了@SPI注解 package com.alibaba.dubbo.common.extension; import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.an......

郑加威
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部