Android 中的异步任务经常使用的一种方式是:Handler + Thread 组合来实现的。Thread 负责子线程的耗时操做,Handler 负责线程间的通讯,用的最多的当属子线程和主线程通讯。java
Android 为了简化操做,提供了 AsyncTask 类来实现异步任务,而且轻松实现子线程和主线程间的通讯。android
三个参数表明的含义git
package com.app; import android.os.AsyncTask; /** * Created by ${zyj} on 2016/8/2. */ public class MyTask<T> extends AsyncTask<T , Integer , T> { private TaskListener taskListener ; public MyTask(){ } //执行预处理,它运行于UI线程,能够为后台任务作一些准备工做,好比绘制一个进度条控件。 @Override protected void onPreExecute() { if ( taskListener != null ){ taskListener.start(); } } //运行于UI线程,能够对后台任务的结果作出处理,结果就是doInBackground(Params...)的返回值。 @Override protected void onPostExecute(T t) { if ( taskListener != null ){ taskListener.result( t ); } } /** * 更新子线程进度,运行于UI线程 * @param values */ @Override protected void onProgressUpdate(Integer... values) {; if ( taskListener != null ){ taskListener.update( values[0] ); } } //运行与后台线程 @Override protected T doInBackground(T... ts) { if ( taskListener != null ){ return (T) taskListener.doInBackground( ts[0] ) ; } return null; } public MyTask setTaskListener(TaskListener taskListener ){ this.taskListener = taskListener ; return this ; } /** * 更新进度 * @param progress */ public void updateProgress( int progress ){ publishProgress( progress ); } public interface TaskListener<T>{ void start() ; void update( int progress ) ; T doInBackground( T t ); void result( T t ); } /** * 取消一个正在执行的任务 */ public void cancle(){ if ( !isCancelled() ){ cancel( true ) ; } } }
package com.app; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import wifi.app.wei.com.myapplication.R; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyTask<String>().setTaskListener(new MyTask.TaskListener() { @Override public void start() { Log.d( "task--" , "start 开始了, 运行在主线程" ) ; } @Override public void update(int progress) { } @Override public Object doInBackground(Object o) { Log.d( "task--" , "doInBackground , 运行在子线程" ) ; return null; } @Override public void result(Object o) { Log.d( "task--" , "result , 运行在主线程" ) ; } }).execute( "" ) ; } }
package com.app; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.widget.TextView; import wifi.app.wei.com.myapplication.R; public class MainActivity extends AppCompatActivity { private TextView textView ; private MyTask myTask ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById( R.id.tv1 ); myTask = new MyTask<String>().setTaskListener(new MyTask.TaskListener() { @Override public void start() { Log.d( "task--" , "start 开始了, 运行在主线程" ) ; textView.setText( "任务开始了" ); } @Override public void update(int progress) { textView.setText( "进度" + progress ); } @Override public Object doInBackground(Object o) { Log.d( "task--" , "doInBackground , 运行在子线程" ) ; for ( int i = 0 ; i < 100 ; i++ ){ try { Thread.sleep( 100 ) ; myTask.updateProgress( i ) ; //每隔100毫秒,更新一下进度 } catch (InterruptedException e) { e.printStackTrace(); } } return "结束了"; } @Override public void result(Object o) { Log.d( "task--" , "result , 运行在主线程" ) ; textView.setText( "" + o ); } }) ; //开始执行任务 myTask.execute( "" ) ; } }
执行效果图github
(1)、若是异步任务须要联网,则须要添加联网权限缓存
<uses-permission android:name="android.permission.INTERNET"/>微信
(2)、AsyncTask实例必须在UI线程中建立,execute(Params…)方法必须在UI线程中调用。不用手动调用onPreExecute()。并发
(3)、一个任务只能被执行一次app
能够调用 myTask.cancle() ; 框架
可是这个方法并无真正的结束任务,只是设置了一个标志位,把当前线程中断了。异步
取消任务实践
package com.app; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.TextView; import wifi.app.wei.com.myapplication.R; public class MainActivity extends AppCompatActivity { private TextView textView ; private MyTask myTask ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById( R.id.tv1 ); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //取消任务 myTask.cancle(); } }); myTask = new MyTask<String>().setTaskListener(new MyTask.TaskListener() { @Override public void start() { Log.d( "task--" , "start 开始了, 运行在主线程" ) ; textView.setText( "任务开始了" ); } @Override public void update(int progress) { textView.setText( "进度" + progress ); } @Override public Object doInBackground(Object o) { Log.d( "task--" , "doInBackground , 运行在子线程" ) ; for ( int i = 0 ; i < 100 ; i++ ){ try { Thread.sleep( 100 ) ; myTask.updateProgress( i ) ; //每隔100毫秒,更新一下进度 } catch (InterruptedException e) { e.printStackTrace(); } } return "结束了"; } @Override public void result(Object o) { Log.d( "task--" , "result , 运行在主线程" ) ; textView.setText( "" + o ); } }) ; //开始执行任务 myTask.execute( "" ) ; } }
当点击textView时,调用了 myTask.cancle() ;方法后,Android studio 控制台抛出了异常
经过这里咱们发现,AsyncTask 虽然提供了cancle( true ) 方法来中止任务,可是这个方法只是中断了这个线程,可是并不能真正意思上的中止任务,这也是不少人说 AsyncTask 的弊端。极容易形成内存溢出的。
几种结束任务的间接实现方式:
一、判断标志位的办法:
咱们要知道在java的线程中,没有办法中止一个正在运行中的线程。在Android的AsyncTask中也是同样的。若是必需要中止一个线程,咱们能够采用这个线程中设置一个标志位,而后在线程run方法或AsyncTask的doInBackground方法中的关键步骤判断这个标志位以决定是否继续执行。而后在须要终止此线程的地方改变这个标志位以达到中止线程的目的。
二、合理的利用Exception
从外部调用AsyncTask的cancel方法并不能中止一个已经启动的AsyncTask。这个cancel方法的做用与线程的interrupt方法类似,调用了一个线程的interrupt方法以后线程仍然运行,可是若是该线程的run方法里面调用过sleep的或者wait方法后,处于sleep或wait状态,则sleep和wait当即结束而且抛出InterruptedException异常。AsyncTask的cancel方法也同样,若是在这个Task的doInBackground方法中调用了sleep或wait方法,当在UI线程中调用了这个Task实例的cancel方法以后,sleep或wait当即结束而且抛出InterruptedException异常,可是若是捕获该异常的代码后面还有其余代码,则这些代码还会继续执行。
三、能够在UI上作手脚
若是用户在后台线程正获取内容时作出了取消的行为,咱们能够根据用户的这种行为在UI上当即作出反馈,此时,即便线程完成了数据的Loading,咱们也不让数据显示出来,算是一种投机取巧的办法吧。
在上面的代码演示中,执行任务用的都是 myTask.execute() , 这个默认是串行执行任务的。好比同一时刻有两个任务要处理,AsyncTask 会先执行第一个任务,等第一个任务执行结束,而后才会执行第二个任务。
在AsyncTask中还有一个并行处理任务的方法:executeOnExecutor( Executor exe , Params... params ) 。
下面是串行执行任务execute()的源码
经过看源码,发现其实串行执行任务也是调用了并行的方法 executeOnExecutor () , 只不过启用了一个默认的 sDefaultExecutor (sDefaultExecutor 是一个串行的线程池)。
有串行线程池,那么势必就有一个并行线程池 , 在AsyncTask里面源码里面定义了一个并行线程池: THREAD_POOL_EXECUTOR 。
能够看到并行 THREAD_POOL_EXECUTOR 是经过 new ThreadPoolExecutor() 来建立的
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
参数说明:
corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所容许的空闲时间
unit: 线程池维护线程所容许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略
咱们知道,受限于硬件、内存和性能,咱们不可能无限制的建立任意数量的线程,由于每一台机器容许的最大线程是一个有界值。也就是说ThreadPoolExecutor管理的线程数量是有界的。线程池就是用这些有限个数的线程,去执行提交的任务。然而对于多用户、高并发的应用来讲,提交的任务数量很是巨大,必定会比容许的最大线程数多不少。为了解决这个问题,必需要引入排队机制,或者是在内存中,或者是在硬盘等容量很大的存储介质中。J.U.C提供的ThreadPoolExecutor只支持任务在内存中排队,经过BlockingQueue暂存尚未来得及执行的任务。
任务的管理是一件比较容易的事,复杂的是线程的管理,这会涉及线程数量、等待/唤醒、同步/锁、线程建立和死亡等问题。ThreadPoolExecutor与线程相关的几个成员变量是:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize,它们共同负责线程的建立和销毁。
corePoolSize:
线程池的基本大小,即在没有任务须要执行的时候线程池的大小,而且只有在工做队列满了的状况下才会建立超出这个数量的线程。这里须要注意的是:在刚刚建立ThreadPoolExecutor的时候,线程并不会当即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,因此没有任务须要执行的时候,线程池的大小不必定是corePoolSize。
maximumPoolSize:
线程池中容许的最大线程数,线程池中的当前线程数目不会超过该值。若是队列中任务已满,而且当前线程个数小于maximumPoolSize,那么会建立新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为何说是曾经呢?由于线程池建立以后,能够调用setMaximumPoolSize()改变运行的最大线程的数目。
poolSize:
线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize。
keepAliveTime
当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。若是allowCoreThreadTimeout设置为true,则全部线程均会退出直到线程数量为0。
了解了各个参数的含义以后,咱们来看看 AsyncTask 中默认的并行线程队列 THREAD_POOL_EXECUTOR 各项的数值
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
小测试:我手上的手机是联想 k50-t5 , 在设置里面看处处理器为 8 核1.7GHZ , 运行 Runtime.getRuntime().availableProcessors(); 方法获得的值为:8 。
另外咱们也能够总结出:
总结:
//开始执行 串行任务 myTask.execute( "" ) ; 或者 myTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR , "" ) ; //开始执行 并行任务 myTask.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "" ) ;
上一部分咱们已经明白了AsyncTask 的默认并行线程池 THREAD_POOL_EXECUTOR 是经过 new ThreadPoolExecutor() 来建立的 , 那么咱们也能够本身定义一个线程池。
首先来看 ThreadPoolExecutor 的构造函数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
经过看构造方法,发现 corePoolSize 、maximunPoolSize 、keepAliveTime 、unit 、workQueue 是必需要写的。
分析最后一个构造
if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException();
corePoolSize :最小值 0
maximunPoolSize :最小值 1
corePoolSize 必须小于或者等于 maximunPoolSize
主要来看 workQueue , 这个是就是线程队列了。
下面是AsyncTask并行线程池 THREAD_POOL_EXECUTOR 里面所使用的线程队列,128 表明线程队列的长度
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
下面给出一个完整的例子:
//建立缓冲队列 队列长度:100 BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(100); //建立线程池 核心线程:5个 最大线程:10个 线程空闲存活时间:1秒 Executor executor = new ThreadPoolExecutor( 5 , 10 , 1 , TimeUnit.SECONDS , sPoolWorkQueue ) ; //添加任务到缓冲队列 myTask1.executeOnExecutor( executor , "" ) ;
线程建立规则
一个任务经过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。
当一个任务经过execute(Runnable)方法欲添加到线程池时:
一、 若是此时线程池中的数量小于corePoolSize,即便线程池中的线程都处于空闲状态,也要建立新的线程来处理被添加的任务。
二、 若是此时线程池中的数量等于 corePoolSize,可是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
三、 若是此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,而且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
四、 若是此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,而且线程池中的数量等于maximumPoolSize,那么经过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,若是三者都满了,使用handler处理被拒绝的任务。
五、 当线程池中的线程数量大于 corePoolSize时,若是某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池能够动态的调整池中的线程数。
线程池按如下行为执行任务
任务队列执行的逻辑:
FIFO 先进先出
在第8节咱们详解了如何自定义线程池,讲解了 ThreadPoolExecutor 构造方法的每一个参数的用法,可是若是自定义线程池都要写那么多参数,岂不是很麻烦。
幸运的是,系统的 java.util.concurrent 包下面 Executors 类提供了不少简单的方法,供咱们使用,这对苦逼的码农来讲,是很好的福音。
Executors 里面的方法有
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); } public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newWorkStealingPool(int parallelism) { return new ForkJoinPool (parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); } public static ScheduledExecutorService newSingleThreadScheduledExecutor() { return new DelegatedScheduledExecutorService (new ScheduledThreadPoolExecutor(1)); } public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); } public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) { return new DelegatedScheduledExecutorService (new ScheduledThreadPoolExecutor(1, threadFactory)); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); } public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory) { return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); } public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }
猛一看,方法那么多,其实把方法单独领出来
public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) public static ExecutorService newSingleThreadExecutor() public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) public static ExecutorService newWorkStealingPool() public static ExecutorService newWorkStealingPool(int parallelism) public static ScheduledExecutorService newSingleThreadScheduledExecutor() public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) public static ExecutorService newCachedThreadPool() public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) public static ScheduledExecutorService newScheduledThreadPool ( int corePoolSize, ThreadFactory threadFactory)
这样一归类,就清晰不少了,就12个方法,而后每2个方法又能够归为一组,也就是6组。
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
|
这两个函数用于建立一个最大线程数目固定的线程池,该线程池用一个共享的无界队列来存储提交的任务。参数nThreads指定线程池的最大线程数,参数threadFactory是线程工厂类,主要用于自定义线程池中建立新线程时的行为。须要说明的是,建立线程池时,若是线程池没有接收到任何任务,则线程池中不会建立新线程,在线程池中线程数目少于最大线程数时,每来一个新任务就建立一个新线程,当线程数达到最大线程数时,再也不建立新线程,新来的任务存储在队列中,以后线程数目再也不变化!
建立一个线程数量固定大小,任务队列无限大的线程池。当队列中须要的线程数超出定义的线程的时候,全部任务将在队列中排队,等待空闲线程执行。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
线程池中的任务使用无界队列存储,第二个函数能够指定threadFactory,自定义建立线程时的行为。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
这个方法用于建立ForkJoin框架中用到的ForkJoinPool线程池,第一个函数中的参数用于指定并行数,第二个函数没有参数,它默认使用当前机器可用的CPU个数做为并行数。
public static ExecutorService newWorkStealingPool() { return new ForkJoinPool (Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); }
该方法用于建立只有一个线程的线程池,而且该线程定时周期性地执行给定的任务
须要注意的是: 线程在周期性地执行任务时若是遇到Exception,则之后将再也不周期性地执行任务。
public static ScheduledExecutorService newSingleThreadScheduledExecutor() { return new DelegatedScheduledExecutorService (new ScheduledThreadPoolExecutor(1)); }
方法用于建立线程数数目能够随着实际状况自动调节的线程池,也有两种类型的函数签名:
public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
|
这种线程池的最大线程数只受到操做系统能够建立的最大线程数数目限制,当线程池有不少任务须要处理时,会不断地建立新线程,当任务处理完毕以后,若是某个线程空闲时间大于60s,则该线程将会被销毁。由于这种线程池可以自动调节线程数量,因此比较适合执行大量的短时间的小任务。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
建立一个大小无限的线程池,线程池中得线程可以周期性地执行给定的任务,有两种函数签名:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }
总结:
一、本篇文章的demo例子已上传至github: https://github.com/zyj1609wz/AsyncTaskDemo
二、本人微信公众帐号:zhaoyanjun125 , 欢迎关注