AsyncTask的相关任务总结

来源:互联网 发布:java游戏编程入门pdf 编辑:程序博客网 时间:2024/06/17 11:50

AsyncTask总结     

       刚开始接触android,在项目中有个业务用到了AsyncTask,在修改的过程中,发现了一些知识点,记录下。

      业务:关键字搜索框,在搜索框的文字变动的时候,每次变化,都要获取到匹配keyword的数据,并进行页面展示

      采用的方法:textwatcher在检测到keyword变动的时候,采用AsyncTask,每次变化都生成一个新的task,在doInbackGround中进行数据查询和组合筛选,在onPostExecute中进行数据展示

      问题:在搜索的时候,响应的速度很慢,期待优化搜索

        -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   

      过程1:

      想法:由于业务需求,没法采用分批查询的方式,来获取,只能在获取全部数据之后,进行数据筛选和封装,所以优化的重点思路放在数据的筛选和封装之中。

             1,采用先展示的方法,在获取到前几条数据之后,先将前几条数据展示给用户,在doinbackground中,继续进行剩余数据的筛选,(采用的for循环),待筛选完毕之后,通知adapter进行数据变更

     结果:测试之后,可以在doinback中发送handler携带数据,进行页面刷新。改动之后,在数据量不大的时候,并无特殊的效果

-----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   ---------------

     过程2: 

       想法:在初步改动效果不明显的情况下,对原来的速度缓慢进行自测,发现在keyword快速变动的时候,反映尤其缓慢,通过log打印抓取到keyword变化的时间,和task执行的时间以及顺序,发现最后一个keyword的执行排在最后,初步判定是个串行的队列,需要等待前面的task执行完毕,再执行。由此发现,如果一秒钟keyword变动十次,那么正确的结果也需要等待前面的9次的task执行,并且刷新页面之后,才能执行。又发现要个可以优化的地方。

     优化过程的知识查询结论:

         1.在api1.6之前,asyncTask的execute是个串行队列。在1.6和2.3之间,是一个并行队列。在2.3之后,又回到了串行队列。

                    而且是维护在一个单独的线程池里,每次执行一个,下一个需要等待之前的执行完成。

         2.asyncTask提供了取消的方法,cancle,用来取消一个任务,方法中的bool参数表示当前的任务是否可以被取消。但是这个方法有2个特变:首先是,调用这个方法,只是改变了task的iscancle的状态,仅此而已,并不会帮你取消任务,需要手动根据iscancle的状态,来return或者break等取消任务;其次,cancle不一定成功,

   

生命周期

关于AsyncTask存在一个这样广泛的误解,很多人认为一个在Activity中的AsyncTask会随着Activity的销毁而销毁。然后事实并非如此。AsyncTask会一直执行doInBackground()方法直到方法执行结束。一旦上述方法结束,会依据情况进行不同的操作。

  • 如果cancel(boolean)调用了,则执行onCancelled(Result)方法
  • 如果cancel(boolean)没有调用,则执行onPostExecute(Result)方法

AsyncTask的cancel方法需要一个布尔值的参数,参数名为mayInterruptIfRunning,意思是如果正在执行是否可以打断,如果这个值设置为true,表示这个任务可以被打断,否则,正在执行的程序会继续执行直到完成。如果在doInBackground()方法中有一个循环操作,我们应该在循环中使用isCancelled()来判断,如果返回为true,我们应该避免执行后续无用的循环操作。

总之,我们使用AsyncTask需要确保AsyncTask正确地取消。

不好好工作的cancel()

简而言之的答案,有时候起作用

如果你调用了AsyncTask的cancel(false),doInBackground()仍然会执行到方法结束,只是不会去调用onPostExecute()方法。但是实际上这是让应用程序执行了没有意义的操作。那么是不是我们调用cancel(true)前面的问题就能解决呢?并非如此。如果mayInterruptIfRunning设置为true,会使任务尽早结束,但是如果的doInBackground()有不可打断的方法会失效,比如这个BitmapFactory.decodeStream() IO操作。但是你可以提前关闭IO流并捕获这样操作抛出的异常。但是这样会使得cancel()方法没有任何意义。


结果:根据上述的只是,采用了一个来保存已经运行的task,并且在keyword变动的时候,取消前一个task,删除task,只保留当前运行的task,同时根据iscancel在循环中判断是否继续进行循环。根据此次变动,相当于取消了中间的几次task,在线程池中,最后一个task,尽量靠前执行。参考变动之后的打印捕捉,效率有大幅度提升,尤其是在变化很快的时候

 ============ ============ ============ ============ ============ ============ ============ ============ ============ ============

 3.想法:在上述变动之后,打印结果显示,还是串行的执行,由于可以根据task的iscancle状态来区分,并且cancel的task不会执行postExecute。考虑采用并发执行,有可能存在的数据混乱的问题,有如下结论:

        最后一个task之前的所有的task都已经被置为iscancle的,如果此时task还没有执行到onpostExecute,则不会执行了。如果有的线程因为执行速度快,已经执行到了onPostExecute(),那么此时就会分为2个情况,定义最后一个正确的为task_right,如果在task_right之前执行了,会被覆盖没问题,如果在task_right之后,由于已经是iscancle的状态,所以也不会执行,应该是没问题,

    知识:在查询了相关之后,发现task要进行并发执行,google有提供一个max=5的线程池来并发执行,executeOnExecutor


AsyncTask新增了两个预定义的线程池SERIAL_EXECUTORTHREAD_POOL_EXECUTOR

其实 THREAD_POOL_EXECUTOR 并不是新增的,之前的就有,只不过之前(Android 2.3)它是AsyncTask私有的,未公开而已。THREAD_POOL_EXECUTOR 是一个corePoolSize为5的线程池,也就是说最多只有5个线程同时运行,超过5个的就要等待。所以如果使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) 就跟2.3版本的 AsyncTask.execute() 效果是一样的。

SERIAL_EXECUTOR 是新增的,它的作用是保证任务执行的顺序,也就是它可以保证提交的任务确实是按照先后顺序执行的。它的内部有一个队列用来保存所提交的任务,保证当前只运行一个,这样就可以保证任务是完全按照顺序执行的,默认的execute()使用的就是这个,也就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR) 与execute()是一样的。


结果:采用并发执行,及时响应keyword的正确task


--------------------------------------------------------------------------------------------------------------------------------------------------------------------

   -----大牛的建议------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------   -----------------

 ============== ============== ============== ============== ============== ============== ============== ============== ==============



改善你的设计,少用异步处理

线程的开销是非常大的,同时异步处理也容易出错,难调试,难维护,所以改善你的设计,尽可能的少用异步。对于一般性的数据库查询,少量的I/O操作是没有必要启动线程的。

与主线程有交互时用AsyncTask,否则就用Thread

AsyncTask被设计出来的目的就是为了满足Android的特殊需求:非主线程不能操作(UI)组件,所以AsyncTask扩展Thread增强了与主线程的交互的能力。如果你的应用没有与主线程交互,那么就直接使用Thread就好了。

当有需要大量线程执行任务时,一定要创建线程池

线程的开销是非常大的,特别是创建一个新线程,否则就不必设计线程池之类的工具了。当需要大量线程执行任务时,一定要创建线程池,无论是使用AsyncTask还是Thread,因为使用AsyncTask它内部的线程池有数量限制,可能无法满足需求;使用Thread更是要线程池来管理,避免虚拟机创建大量的线程。比如从网络上批量下载图片,你不想一个一个的下,或者5个5个的下载,那么就创建一个CorePoolSize为10或者20的线程池,每次10个或者20个这样的下载,即满足了速度,又不至于耗费无用的性能开销去无限制的创建线程。

对于想要立即开始执行的异步任务,要么直接使用Thread,要么单独创建线程池提供给AsyncTask

默认的AsyncTask不一定会立即执行你的任务,除非你提供给他一个单独的线程池。如果不与主线程交互,直接创建一个Thread就可以了,虽然创建线程开销比较大,但如果这不是批量操作就没有问题。

Android的开发没有想像中那样简单,要多花心思和时间在代码上和测试上面,以确信程序是优质的


 ============== ============== ============== ============== ============== ============== ============== ============== ============== ============== ============== ============== ============== ============== ============== ============== ============== ==============

总结:

1,在解析到可以展示的几个数据的时候,先展示给用户,子线程继续剩余的工作,

2.asynctask是可以cancle的,但是cancle有自己的特点,根据iscancle在for循环等耗时操作中判断进行返回,根据iscancel在doinback刚开始的时候,进行判断,是否return null

3,在此项目中,由于可以cancle并且只需要最后一次的数据,可以进行并发查询,并且根据正确的task进行渲染