当前位置:  编程技术>移动开发

android异步任务设计思详解(AsyncTask)

    来源: 互联网  发布时间:2014-10-23

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

这里说有设计思想是我根据查看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设计思想,软件架构。


    
 
 

您可能感兴趣的文章:

  • 异步 HTTP 客户端开发包 android-async-http
  • Android加载对话框同时异步执行实现方法
  • android在异步任务中关闭Cursor的代码方法
  • android开发教程之handler异步更新ui
  • Android中BroadcastReceiver(异步接收广播Intent)的使用
  • Android 异步加载图片的实例代码
  • android异步请求服务器数据示例
  • Android 异步获取网络图片并处理导致内存溢出问题解决方法
  • android开发教程之handle实现多线程和异步处理
  • android异步加载图片并缓存到本地实现方法
  • Android多线程及异步处理问题详细探讨
  • Android 异步加载图片分析总结
  • 深入android Unable to resolve target 'android-XX'详解
  • Android工程:引用另一个Android工程的方法详解
  • Android TextView设置背景色与边框的方法详解
  • Android中的android:layout_weight使用详解
  • Android 实现永久保存数据的方法详解
  • 在android开发中尽量不要使用中文路径的问题详解
  • android开发环境搭建详解(eclipse + android sdk)
  • android双缓冲技术实例详解
  • 深入Android开发FAQ的详解
  • Android开发笔记之:一分钟学会使用Logcat调试程序的详解
  • Android对sdcard扩展卡文件操作实例详解
  • Android笔记之:onConfigurationChanged详解
  • Android 动画之AlphaAnimation应用详解
  • 解析后台进程对Android性能影响的详解
  • android ListView 一些重要属性详解
  • 解决Fedora14下eclipse进行android开发,ibus提示没有输入窗口的方法详解
  • Windows下获取Android 源码方法的详解
  • Android selector背景选择器的使用详解
  • Android 动画之RotateAnimation应用详解
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • android教程之使用asynctask在后台运行耗时任务
  • Android中AsyncTask的用法实例分享
  • Android开发笔记之:深入理解多线程AsyncTask
  • Android开发笔记之:AsyncTask的应用详解
  • 申请Android Map 的API Key(v2)的最新申请方式(SHA1密钥)
  • Android瀑布流实例 android_waterfall
  • Android开发需要的几点注意事项总结
  • Android系统自带样式 (android:theme)
  • android 4.0 托管进程介绍及优先级和回收机制
  • Android网络共享软件 Android Wifi Tether
  • Android访问与手机通讯相关类的介绍
  • Android 图标库 Android GraphView
  • Android及andriod无线网络Wifi开发的几点注意事项
  • 轻量级Android开发工具 Android Tools
  • Android 2.3 下StrictMode介绍
  • Android 开发环境 Android Studio
  • IDEA的Android开发插件 idea-android
  • Android手机事件提醒 Android Notifier
  • XBMC的Android客户端 android-xbmcremote
  • Android小游戏 Android Shapes
  • Android电池监控 Android Battery Dog
  • android开发:“android:WindowTitle”没有对应项no resource
  • Android 上类似IOS 的开关控件。 Android ToggleButton
  • Android 将 android view 的位置设为右下角的解决方法
  • Android 2D游戏引擎 Android Angle


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3