AsyncTask解析(上)——原理分析与超简单demo实现

最近因为在做项目的过程中经常需要进行网络传输,所以打算把几个常用的网络通信框架和GitHub上面的开源框架梳理一遍,本文简单介绍了AsyncTask工作原理以及一个十分简单的应用demo。

当然,了解一个组件,最好是先从Android API文档入手。

那么首先我们来看一下AsyncTask的继承结构:

可以看到,AsyncTask跟Handler一样,是直接从Object类继承的,属于安卓系统包里的基本组件。

再来看看文档中对AsyncTask给出的描述:

从中我们可以得到3个比较重要的信息点:

1、AsyncTask与handler一样,都是为了防止线程阻塞而用于执行简单的异步处理的类。

2、比起Handler实现异步的过程:需要使用到Handler, Looper, Message,Thread四个对象,并需要通过主线程启动Thread(子线程)运行并生成Message-àLooper获取Message并传递给HandleràHandler逐个获取Looper中的Message,并进行UI变更。AsyncTask的实现过程更为简单,只有4个步骤,分别为onpreexecute,doInBackground,onProgressUpdate和onpostexecute。

3、AsyncTask是被设计成处理异步操作的一个辅助类而不是一个通用的线程框架,应该被用于一些简单的通信异步处理的情况中(最多几秒钟),如果要保持长时间的通信线程运行(比如文件传输之类的),最好不要使用AsyncTask!

AsyncTask的实现步骤:

这里参考了:http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html的博文,归纳得十分透彻精辟,值得牢记。

1、首先我们可以通过实现一个类继承AsyncTask来实现异步加载数据,如下所示:

public class TestAsyncTask  extends AsyncTask<String, Integer, String>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">       其中,AsyncTask定义了三种泛型类型:Params,Progress和Result。Params :启动任务执行的输入参数,比如HTTP请求的URL。Progress :后台任务执行的百分比。Result 后台执行任务最终返回的结果,这里用到的是String。</span>

2、实现这个异步加载类必须要有以下两个方法:

doInBackground(Params…) :在后台执行,比较耗时的操作都可以放在这里。但是不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。

onPostExecute(Result):  相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回。

3、有必要的话你还得重写以下这三个方法,但不是必须的:

onProgressUpdate(Progress…) :  可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。

onPreExecute()   :     这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。

onCancelled()  :           用户调用取消时,要做的操作

4、使用AsyncTask类,以下是几条必须遵守的准则:

Task的实例必须在UI thread中创建;

execute方法必须在UI thread中调用;

不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;

该task只能被执行一次,否则多次调用时将会出现异常;

AsyncTask实现执行过程源码分析:

TestAsyncTask  testAsyncTask = (TestAsyncTask) new TestAsyncTask(TestAsyAty.this,textView,progressBar).execute("");

首先,例如我们在Activity中调用自定义的AsyncTask实现异步操作,先要执行execute()方法执行这个异步任务。

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

可以看到,在execute方法的源码内部,会执行一个executeOnExecutor()方法用以return一个AsyncTask的实例:

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

在这个方法中,改变了参数变量,返回了上文中调用的AsyncTask的实例,还可以看到关键的一点是执行了onPreExecute()方法,于是跳转到onPreExecute()方法:

/**
 * Runs on the UI thread before {@link #doInBackground}.
 *
 * @see #onPostExecute
 * @see #doInBackground
 */
@MainThread
protected void onPreExecute() {
}

注释已经很明确了,在doInBackground()方法之前执行,那么跳转到doInBackground():

/**
 * Override this method to perform a computation on a background thread. The
 * specified parameters are the parameters passed to {@link #execute}
 * by the caller of this task.
 *
 * This method can call {@link #publishProgress} to publish updates
 * on the UI thread.
 *
 * @param params The parameters of the task.
 *
 * @return A result, defined by the subclass of this task.
 *
 * @see #onPreExecute()
 * @see #onPostExecute
 * @see #publishProgress
 */
@WorkerThread
protected abstract Result doInBackground(Params... params);

可以通过重写该方法执行异步任务操作(执行网络请求什么的可以放doInBackground()里面)。在该方法中可以调用publishProgress()方法实现对进度条的更新,增加用户体验度。

在方法之后,执行的自然是onProgressUpdate()方法进行对UI的更新操作:

/**
 * Runs on the UI thread after {@link #publishProgress} is invoked.
 * The specified values are the values passed to {@link #publishProgress}.
 *
 * @param values The values indicating progress.
 *
 * @see #publishProgress
 * @see #doInBackground
 */
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}

现在回到doInBackground()方法,虽然注释中并没有给出下一个要执行的方法,不过通过阅读上文,冰雪聪明の你应该已经想到了:在执行完异步操作之后,下一步自然是要更新UI,执行onPostExecute(Result)方法了:

/**
 * <p>Runs on the UI thread after {@link #doInBackground}. The
 * specified result is the value returned by {@link #doInBackground}.</p>
 *
 * <p>This method won‘t be invoked if the task was cancelled.</p>
 *
 * @param result The result of the operation computed by {@link #doInBackground}.
 *
 * @see #onPreExecute
 * @see #doInBackground
 * @see #onCancelled(Object)
 */
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}

可以看到该方法确实是在doInbackground()方法之后调用的,接受到的result也是doInbackground()的返回值,还有一个要注意的点:如果异步任务呗取消(用户调用onCancelled()方法),那么onPostExecute()方法是不会被执行的!

整体流程结构图如下所示(画的比较简陋,轻喷。。):

AsyncTask简单Demo实现:

下面给出一个比较简单的Demo实现,通过异步操作调用百度的身份证查询API接口查询个人资料数据(result是json数据,这里就不解析了= =)。

注释比较详细这里就不赘述了大家看源码吧(づ ̄ 3 ̄)づ:

TestAsyAty.java

public class TestAsyAty extends Activity {
    TestAsyncTask testAsyncTask;
    ProgressBar progressBar;
    TextView showtv;
    public static final String API_KEY = "f31209d4d0c59c0fb4dcca8b9282f2f9";
    public String path = "http://apis.baidu.com/apistore/idservice/id";
    String httpArg = "id=420984198704207896";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.testasy);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        progressBar.setMax(100);
        showtv = (TextView) findViewById(R.id.textView);
        progressBar.setMax(100);
        testAsyncTask = new TestAsyncTask(showtv,progressBar,httpArg);
        testAsyncTask.execute();
    }
}

TestAsyncTask.java

public class TestAsyncTask  extends AsyncTask<String, Integer, String>
{
    String httpUrl = "http://apis.baidu.com/apistore/idservice/id";
    String httpArg ;
    TextView tv;
    ProgressBar bar;
    public TestAsyncTask(TextView tv,ProgressBar bar,String httpArg) {
        super();
        this.tv = tv;
        this.bar = bar;
        this.httpArg = httpArg;
    }

    /**
     * 这里的String参数对应AsyncTask中的第一个参数
     * 这里的String返回值对应AsyncTask的第三个参数
     * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
     * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
     * @param params
     * @return
     */
    @Override
    protected String doInBackground(String... params) {
        BufferedReader reader = null;
        String result = null;
        StringBuffer sbf = new StringBuffer();
        httpUrl = httpUrl + "?" + httpArg;

        try {
            URL url = new URL(httpUrl);
            HttpURLConnection connection = (HttpURLConnection) url
                    .openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("apikey",  "f31209d4d0c59c0fb4dcca8b9282f2f9");
            connection.connect();
            InputStream is = connection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String strRead = null;
            while ((strRead = reader.readLine()) != null) {
                sbf.append(strRead);
                sbf.append("\r\n");
            }
            reader.close();
            result = sbf.toString();
            if (result != null) {
                publishProgress(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
     * 在doInBackground方法执行结束之后在运行
     * 并且运行在UI线程当中 可以对UI空间进行设置
     */
    @Override
    protected void onPostExecute(String s) {
        tv.setText(s);
    }

    //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
    @Override
    protected void onPreExecute() {
        tv.setText("开始执行异步线程");
    }

    /**
     * 这里的Intege参数对应AsyncTask中的第二个参数
     * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
     * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        int v = values[0];
        bar.setProgress(v);
    }
}

运行效果:

解析什么的就自己做辣~\(≧▽≦)/~~

下篇文章: AsyncTask解析(下)——实现自定义AsyncTask网络传输工具类封装

实现了自定义AsyncTask的封装以及实现细节,可以点进去看看哟QAQ

继续努力~~~好好学习~~~!!!求关注!!!求互粉!!!!!!

时间: 2024-11-05 13:44:34

AsyncTask解析(上)——原理分析与超简单demo实现的相关文章

Android将Library上传到jcenter超简单完整步骤以及遇到的各种坑

Android将Library上传到jcenter超简单完整步骤以及遇到的各种坑 在[玩转SQLite系列](七)打造轻量级ORM工具类SQLiteDbUtil操作数据库 中我们可以看到这个工具类的超简洁用法:只需要在gradle中引入: compile 'cn.bluemobi.dylan:sqlitelibrary:0.1' 即可使用,那么这到底是怎么回事?这个是Android项目所在jcenter的一个远程仓库.我们只需要将你的libray上传到jcenter即可,那么如和将一个项目上传到

会话状态Session解析以及原理分析

我们知道web网站在客户端存储数据有三种形式:1. Cookie   2. hidden(隐藏域) 3.QueryString 其中viewstate什么的都是通过第二种方式隐藏域存储滴. 客户端存储数据有三种形式,那服务器端有几种呢? 嘿嘿 服务器端有:1. Session 2. Application 3. database 4. caching(缓存) 其中session用的较多,当然数据库是必须的. 开发过管理系统项目(不限大小)的童鞋应该都接触过session,在管理系统中sessio

ASP.NET Core 上传多文件 超简单教程

示例源码下载地址 https://qcloud.coding.net/api/project/3915794/files/4463836/download 项目地址 https://dev.tencent.com/u/whuanle/p/asp.netcore_file_upload/attachment 创建应用程序 打开VS 2017 --新建 ASP.NET Core Web 应用程序 --Web 应用程序(模型视图控制器) 程序名字.路径,默认即可 删除不必要的内容 打开 HomeCon

android之AsyncTask原理分析

通过名字就可以知道,AsyncTask主要用于处理android中的异步任务.但是通过源码,我们可以看到它的实现其实还是依赖于Handler的异步消息处理机制.现在我们先来学习它的使用方式,然后再研究源码. 一.AsyncTask的基本用法: AsyncTask是一个抽象类,在之类继承它时,必须指定三个泛型参数,这三个参数的用途如下: 1. 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用. 2. 后台任何执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位.

AsyncTask,IntentService工作原理分析&amp;Android线程池

一,android中的主线程和子线程 android中的主线程可以认为是UI线程,在主线程不可以执行耗时的操作,否则就会给人一种卡顿的感觉.而主线程主要用于处理四大组件,以及处理它们和用户的交互.anroid的子线程的主要功能就是处理耗时操作. 要知道"在android3.0之后,要求网络访问必须在子线程执行,否则会抛出NetWorkOnMainThreadException异常." 二,Android中的线程形态 Android中的线程状态,除了传统的Thread,还包含AsyncT

第5章4节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 命令行参数解析(原创)

天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文"寻求合作伙伴编写<深入理解 MonkeyRunner>书籍".但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在所难免.有需要的就参考下吧,转发的话还请保留每篇文章结尾的出处等信息. 设置好Monkey的CLASSPATH环境变量以指定"/system/framework /framework/monkey.jar"后,/system/bin/monkey这个shell脚本就会通

Tomcat7.0源码分析——请求原理分析(上)

前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程序员自己实现Jsp页面或者Servlet接受请求,后来借助Struts1.Struts2.Spring等中间件后,实际也是利用Filter或者Servlet处理请求,大家肯定要问了,这些Servlet处理的请求来自哪里?Tomcat作为Web服务器是怎样将HTTP请求交给Servlet的呢? 本文就

Android异步任务AsyncTask的使用与原理分析

在上一篇文章<Android缓存机制&一个缓存框架推荐>中说到,在了解了Android缓存机制后我准备自己动手写一个LruCache和DiskLruCache二级缓存的轻量级的图片请求框架,在思考如何搭建这个框架时,纠结于用何种方式去下载图片,是直接new出一个线程呢,还是用看起来稍微高大上档次一点的AsyncTask异步任务来处理?思来想去,还是虚荣心作怪,还是用AsyncTask吧,正好这个工具类我之前用的也比较少,对它的原理也不是很清楚,趁这个机会,好好学一下AsyncTask的

[精华] RDMA技术原理分析、主流实现对比和解析

替换高清大图 请点击此处输入图片描述  摘要: 远程直接内存访问(即Remote Direct Memory Access)是一种直接内存访问技术,它将数据直接从一台计算机的内存传输到另一台计算机,无需双方操作系统的介入,本文旨在技术引导,详细内容请通过文末"阅读原文"参阅<RDMA原理分析.对比和技术实现解析>电子书. RDMA技术最早出现在Infiniband网络,用于HPC高性能计算集群的互联.传统的基于Socket套接字(TCP/IP协议栈)的网络通信,需要经过操作