这里说有设计思想是我根据查看Android源代码提炼出来的代码逻辑,所以不会跟Google工程师的原始设计思想100%符合(也有可能是0%),但是本文一定可以帮助你理解AsyncTask,也可能有一些你以前没有发现的内容。

大家都知道,Android的主线程(又叫UI线程,线程ID为1)有一些限制策略,使得主线程有些事做不了,比如访问网络就不允许,否则就是报,但在2.3之后的版本,你可以通过添加以下代码更改其限制策略,从而强制使得主线程可以访问网络:

复制代码代码如下:
if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

不过StrictMode是一个开发工具主要用于侦测主线程中的磁盘和网络访问,而不是让你做这“坏”事,其实Android这样限制是有好处的,强制让开发者重视用户体验,一个反面例子是Windows,主线程里什么都可以做,一些很懒的开发者就把所有任务放到主线程里,使得线程经常好卡,比如编辑器UE或Notepad++打开了一个网络上(如samba服务器)的文件,如果突然网络中断了,那你的整个编辑器都卡住,要等好久才会有反应,不过我不确定那是不是因为主线程里访问了网络,不过Windows经常因为这个原因卡。还有一个正面例子是iOS,极其注意响应速度,所以当有用户输入事件时,其内核都有相应的调度,从而优先响应用户操作。

还是回到正题,就是因为主线程的这些限制使开发者不得不写多个线程,当然,你也可以不用AsyncTask,不过你不用也避免不了多线程,如果你不用,就是可能要用Handler和Thread了,我想很多人初学的时候就是那么干的,包括我,因为那时很有可能还没有发现有这个类,于是就经常写Handler和Thread的了,写着写着就发现有一些代码是相同的,你写的Handler和Thread匿名类肯定是重复代码,如下:

复制代码代码如下:
[final Handler handler = new Handler() {
    public void handleMessage(Message msg) {
     System.out.println("The hard have done!");
     // ... front end code
    }
};
new Thread() {
    public void run() {
        doHardWork();
        handler.sendEmptyMessage(0);
    }

private void doHardWork() {
        // ... back end code
    }
}.start();

你可能想到要复用这些代码,当然,你可以通过Copy的方式来复用这段代码,用的时候只要在省略号处写入你的代码就可以了,但更好的复用是将其用一个类封装起来,好吧,那我们就简单的封装一下吧,于是,就变成了这样:

复制代码代码如下:
public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            System.out.println("The hard have done!");
            //...
        }
    };

public void doInBackground() {
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }

private void doHardWork() {
                // ...
            }
        };
    }       
}

这样好像还不行,因为无法告诉后台线程做什么事,做完了也不知道通知,要复用还是得Copy代码,我们可以加两个方法,一个在前台调用一个在后台调用,只要定义一个新类就可以实现复用,于是代码变成这样:

复制代码代码如下:
public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            System.out.println("The hard have done!");
            runInFrontend();    // added
        }
    };

public void doInBackground() {
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }

private void doHardWork() {
                runInBackend();    //added
            }
        };
    }

//added
    protected void runInBackend() {
    }

//added
    protected void runInFrontend() {
    }
}

一个可复用的类就出炉了,我们写一个子类,并用一个Activity来调用一下吧:

复制代码代码如下:
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

new SubHandlerAndThread().doInBackground();
    }

class SubHandlerAndThread extends HandlerAndThread {
        protected void runInBackend() {
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

protected void runInFrontend() {
            System.out.println("Task has been done");
        }
    }
}

这样是不是比直接写Thread和Handler简洁了许多呢,这里我是用sleep来模似长时间事务的,如果在真实的环境中,我们可能是要下载,如果是下载,我们可能希望传入个下载地址的参数到后台线程,来让他按我们的需要下载,我们给加doInBackground方法加一个参数,于是HandlerAndThread类的代码就变成这样:

复制代码代码如下:
public class HandlerAndThread {
    ...

public void doInBackground(final String url) { // added url
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }

private void doHardWork() {
                runInBackend(url);  // added url
            }
        };
    }

protected void runInBackend(String url) { // added url
    }

...
}

而调用类的代码变成这样:

复制代码代码如下:
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

String url = "http://path/to/file";
        new SubHandlerAndThread().doInBackground(url);   //added url
    }

class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected void runInBackend(String url) {    // added url
            System.out.println("Start download from url:" + url);
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

@Override
        protected void runInFrontend() {
            System.out.println("finish download");
        }
    }
}

假如是下一个文件呢,我们是不是加一个进度更新的方法呢,于是又变成这样:

复制代码代码如下:
public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {        // added
            case 0:
                runInFrontend();
                break;

case 1:
                runInFrontendProgress(msg.arg1);
                break;
            }
        }
    };

...

final protected void publishProgress(int progress) {    // added
        handler.obtainMessage(1, progress, 0);
    }

protected void runInFrontendProgress(int progress) {    // added
    }
}

public class MainActivity extends Activity {
    ...

class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected void runInBackend(String url) {
            System.out.println("Start download from url:" + url);
            for (int i = 0; i < 10; ++i) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrack();
                }
                publishProgress(i*10);    // added
            }
        }

...

@Override
        protected void runInFrontendProgress(int progress) { // added
            System.out.println("Progress: " + progress);
        }
    }
}

你可能已经没有耐心一版一版的进化了,那我就跳跃一下吧,一次多加几条需要:一、我们下载完了可能要得到文件的路径,所以我们给runInFrontend方法加一个输入参数filePath表示路径;二、把子类必须实现的方法改成抽象方法,类也改成抽象方法;我把代码中的一些方法名改一下,使其更好理解,把doInBackground改为execute,把runInFrontend改为onPostExecute,把runInFrontendProgress改为onProgressUpdate。最终版如下:

复制代码代码如下:
public abstract class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 0:
                onPostExecute((String) msg.obj);
                break;

case 1:
                onProgressUpdate(msg.arg1);
                break;
            }
        }
    };

public void doInBackground(final String url) {
        new Thread() {
            public void run() {
                String result = runInBackend(url);
                handler.obtainMessage(0, result);
            }

};
    }

final protected void publishProgress(int progress) {
        handler.obtainMessage(1, progress, 0);
    }

abstract protected String runInBackend(String url);
    protected void onPostExecute(String filePath) { }
    protected void onProgressUpdate(int progress) {    }
}
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

String url = "http://path/to/file";
        new SubHandlerAndThread().doInBackground(url);
    }

class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected String runInBackend(String url) {
            System.out.println("Start download from url:" + url);
            for (int i = 0; i < 10; ++i) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                publishProgress(i*10);
            }

return "/path/to/file";
        }

@Override
        protected void onPostExecute(String filePath) {
            System.out.println("Download finished");
        }

@Override
        protected void onProgressUpdate(int progress) {
            System.out.println("Progress: " + progress);
        }
    }
}

这是不是跟Android的AsyncTask很像呢,我想Google就是由于这种需求做出这个类的,Android官网是这样描述AsyncTask的:

This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

意思是这个类使得:不使用Thread和Handler,就可以在后台执行操作然后在发布结果到UI线程。其实他内部的实现就是封装了Thread和Handler,所以你就不必要直接用这两个低层类了,但他的目的也是代码复用,他的实现跟我们上面写的类也差不多。主要有这几点不同:一、AsyncTask使用了线程池而不是单个线程去执行后台任务,该线程池是整个进程共用的,是因为他的线程池对象是一个静态成员变量,这一点很多人搞错,误以为AsyncTask越来创建的线程越多,这不是绝对正确的,因为线程池会根据负载动态调整的,而且有最大值和空闲超时,AsyncTask的配置是最小5,最大128,空闲超时1秒,当然你也可以配置成线程数根据任务数线程递增,关于线程池,可参考这里,后续我会在博客写文章讨论Java线程池;二、AsyncTask的输入和输出参数使用了泛型;三、AsyncTask支持中断当前任务。

现在知道了AsyncTask的设计思想了吧,是不是很简单呢,所以建议童鞋们去看一下它的源码,反正我写代码时有查看源码的习惯,因为我会好奇它是如何实现的,看源码有很多好处,如可以学到好的API设计思想,软件架构。

转:android异步任务设计思详解(AsyncTask)的更多相关文章

  1. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

  2. Android Design Support Library使用详解

    Android Design Support Library使用详解 Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的And ...

  3. Android 之窗口小部件详解--App Widget

    Android 之窗口小部件详解--App Widget  版本号 说明 作者 日期  1.0  添加App Widge介绍和示例  Sky Wang 2013/06/27        1 App ...

  4. [Android新手区] SQLite 操作详解--SQL语法

    该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法  :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解 ...

  5. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

  6. Android 之窗口小部件详解(三)  部分转载

    原文地址:http://blog.csdn.net/iefreer/article/details/4626274. (一) 应用程序窗口小部件App Widgets 应用程序窗口小部件(Widget ...

  7. Android中Canvas绘图基础详解(附源码下载) (转)

    Android中Canvas绘图基础详解(附源码下载) 原文链接  http://blog.csdn.net/iispring/article/details/49770651   AndroidCa ...

  8. Android高效率编码-第三方SDK详解系列(一)——百度地图,绘制,覆盖物,导航,定位,细腻分解!

    Android高效率编码-第三方SDK详解系列(一)--百度地图,绘制,覆盖物,导航,定位,细腻分解! 这是一个系列,但是我也不确定具体会更新多少期,最近很忙,主要还是效率的问题,所以一些有效的东西还 ...

  9. Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

随机推荐

  1. android 指示器 tablatyout

    <android.support.design.widget.TabLayout/>android 材料设计中新出的控件 package com.weavey.loadinglayout; ...

  2. RHEL6.4编译安装企业级LAMMP平台

    一.LAMMP简介 二.使用软件及服务器架构说明 三.配置及安装过程    1.安装arp与httpd    2.安装mysql    3.安装php(php-fpm)    4.安装Xcache   ...

  3. JavaScript document属性和方法

    JavaScript document属性和方法 --------------------------------------------属性: 1. Attributes     存储节点的属性列表 ...

  4. SQLserver CASE WHEN

    declare @shuzu int set @shuzu=1 select (case @shuzu when '1' then '444' when '2'then '555' end) as ' ...

  5. A Brief Review of Supervised Learning

    There are a number of algorithms that are typically used for system identification, adaptive control ...

  6. pb自动注册ole控件

    方法一:  1.手工注册OCX控件 将该控件随程序一起发布,然后,将此文件拷到windows\system,或者直接放在本运行目录,然后执行dos命令,run( "regsvr32   *. ...

  7. Linux/Unix 线程同步技术之互斥量(1)

    众所周知,互斥量(mutex)是同步线程对共享资源访问的技术,用来防止下面这种情况:线程A试图访问某个共享资源时,线程B正在对其进行修改,从而造成资源状态不一致.与之相关的一个术语临界区(critic ...

  8. jsp数据交互(一),九大内置对象

    九大内置对象 九大内置对象 内置对象 类型 说明 作用域 request javax.servlet.ServletRequest 请求对象——在 一次请求/一个Request请求周期 中传递数据,请 ...

  9. 题目1203:IP地址

    题目: http://ac.jobdu.com/problem.php?pid=1203 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:3052 解决:1504 题目描述: 输入一个ip地 ...

  10. 搭建java web开发环境、使用eclipse编写第一个java web程序

    开发工具:eclipse-jee-juno-SR2-win32-x86_64(请自行官网下载) 使用服务器:apache-tomcat-7.0.35-windows-x64(请自行官网下载) 打开 e ...