I. 线程
线程在Android系统中扮演者一个很重要的角色,从用途上来说,可以分为主线程和子线程,主线程一般用来处理界面与用户的交互,而子线程则往往用来执行一些耗时操作,例如I/O操作和网络访问,在Android3.0之后网络访问必须放到子线程中执行,否则会抛异常(NetworkOnMainThreadException),这样做的目的也是为了防止用户在主线程中做耗时操作,这样很容易引起ANR。在Android系统中还有一些扮演线程的角色:AsyncTask、IntentService和HandlerThread,虽然它们都有别于传统的线程,但是它们的本质仍然是传统的线程。
II. 线程池
线程是操作系统中最小的调度单位,操作系统创建、销毁线程的开销代价是比较大的,试想一下如果不断地创建销毁线程,那么系统就会频繁地创建销毁线程,同时创建太多的线程在CUP调度时切换调度单位也是一个问题,除非线程数量少于CUP的核数才能保证绝对的并行。频繁地创建销毁线程带来不小的系统开销,这个时候线程池就是明智的选择,一个线程池会缓存一定数量的线程,通过线程池来避免频繁的创建销毁线程所带来的系统开销。Android中的线程池来源于Java,主要通过executor来派生特定类型的线程池,不同种类的线程池有不同的特性。
III. AsyncTask
AsyncTask是一个抽象泛型类,
public abstract class AsyncTask
这三个参数中的Params是执行异步任务传入的参数类型,Progress是异步任务需要向外发出的进度值得类型,即publishProgress方法的参数类型,Result是doInbackground方法的返回值类型。下面是一个例子:
new MyAsyncTask("task1").execute("param1");
new MyAsyncTask("task1").execute("param1", "param2");
new MyAsyncTask("task1").execute("param1", "param2", "param3");
class MyAsyncTask extends AsyncTask{
private String name;
public MyAsyncTask(String tag){
name = tag;
}
/**
* 在主线程工作,一般在这个方法中提示正在加载某些资源
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/**
* 在子线程工作(使用线程池),在后台异步加载资源,可以通过publishProgress(Progress... values)调用onProgressUpdate来更新进度
*
* @param params 这个参数是new MyAsyncTask().execute(params) 时传递的参数,其中 ... 表示参数是动态的,
* 接收的时候是一个数组
*
* @return 返回值会传递到onPostExecute方法的参数
*/
@Override
protected String doInBackground(String... params) {
//模拟耗时任务
SystemClock.sleep(2000);
Log.d(TAG, "doInBackground: " + name + ":params.length=" + params.length);
int progress = 10;
publishProgress(progress);
return name;
}
/**
* 在主线程工作,一般在这个方法根据参数去更新UI,例如消除掉正在加载的对话框,将结果刷新到界面
*
* @param s doInBackground的返回值
*
*/
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
/**
* 在主线程工作,用于更新进度,比如在下载的场景提示当前的下载进度
*
* @param values 在doInBackground中调用publishProgress(Progress... values)的参数
*/
@Override
protected void onProgressUpdate(Integer... values) {
//Log.d(TAG, "onProgressUpdate: progress=" + values[0]);
}
}
从打印结果可以看出任务时串行执行的,打印结果相差了2秒,我使用的模拟器版本是Android-22(5.1.1)。Android1.6之前的版本是串行的,1.6到Android3.0之间是并行的,3.0开始默认又变为串行的了,当然在3.0之后我们可以通过AsyncTask的 executeOnExecutor来实现并行。
new MyAsyncTask("task1 on executor").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
new MyAsyncTask("task2 on executor").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
new MyAsyncTask("task3 on executor").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
我们在代码中模拟通过SystemClock.sleep(2000)来模拟还是任务, 但是通过结果可以看到是在同一秒钟(46)打印的结果,这就意味着任务是并行处理的。
IV. HandlerThread
HandlerThread继承自Thread,其实也没做什么,最主要的工作是帮我们做了 Looper.prepare() 和Looper.loop(),然后HanlderThread可以直接使用Handler。下面是HandlerThread的run方法的实现:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
你可能会发现其中有一句代码notifyAll();
,这是干什么的,先看看getLoopere方法
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
你会发现里面有一个wait(),其实就是通知等待的代码,你可以执行了,这是一个同步机制。
HandlerThread用法的一个例子:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Handler mHandler;
private HandlerThread mThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThread = new HandlerThread("#HandlerThread");
mThread.start();
mHandler = new Handler(mThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage: " + msg.what);
mHandler.sendEmptyMessageDelayed(0, 1000);
}
};
}
@Override
protected void onResume() {
super.onResume();
mHandler.sendEmptyMessageDelayed(0, 1000);
}
@Override
protected void onPause() {
super.onPause();
mHandler.removeMessages(0);
}
@Override
protected void onDestroy() {
mThread.quit();
super.onDestroy();
}
}
由于HandlerTHread的run方法是无限循环,所以有必要去调用它的quit方法或者quitSafely方法去退出,这是一个良好的编程习惯。
V. IntentService
IntentService是一种比较特殊的Service,它继承自Service,是一个抽象类,因此只有它的实现类才能使用。它内部封装了HandlerThread和Handler,从它的构造方法就可以看出来:
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
从这个构造方法中我们可以看到首先创建了一个HandlerThread,然后用它的Looper来创建了一个Handler,这就意味着IntentService的操作逻辑是在HandlerThread中执行了,所以IntetnService是可以用来做耗时操作的,另外由于它是Service的原因,所以它的优先级是要比普通的Thread高的。IntentService首次启动会调用这个构造方法,每次启动都会回调onStartCommad方法,看看onStartCommad方法:
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
onStartCommad方法会调onStart方法,在onStart方法中,将消息发送到了mServiceHandler中
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
当消息到达handleMessage会继续掉onHandleIntent方法,当这个onHandleIntent方法执行结束,会调stopSelf(int startId)方法来停止服务,如果目前后多个后台任务,那么当onHandleIntent执行完最后一个任务时,stopSelf(int startId)才会直接停止服务。
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
这个方法是一个抽象方法,我们在自己的实现类中实现这个方法,在这个方法中做我们的操作逻辑。
例子:
public class LocalIntentService extends IntentService {
private static final String TAG = "LocalIntentService";
public LocalIntentService() {
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
String action = intent.getStringExtra("action");
SystemClock.sleep(1000);
Log.d(TAG, "onHandleIntent: " + action);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "LocalIntentService was Destroy");
}
}
别忘了这是个Service,必须要在清单文件
.....
里面注册的。
执行如下代码
Intent intent = new Intent(this, LocalIntentService.class);
intent.putExtra("action","task1");
startService(intent);
intent.putExtra("action","task2");
startService(intent);
intent.putExtra("action","task2");
startService(intent);
可以看到是三个任务结束后才停止服务的。
VI. Android中的线程池
1)使用线程池的好处
①重用线程池中的线程,避免因为线程的创建和销毁带来的系统性能开销;
②能有效控制线程池的最大并发数,避免大量线程之间因互相抢占系统资源而导致阻塞的现象;
③能够对线程进行简单的管理,并提供定时执行和指定间隔时间循环执行等功能。
2)ThreadPoolExecutor
android中的线程池概念来源于java中的Executor,而Executor是一个接口,真正线程池的实现 是ThreadPoolExecutor,Android下的线程池主要分为4类,都是直接或间接配置ThreadPoolExecutor来实现的,看看ThreadPoolExecutor构造方法的参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory) {}
①corePoolSize:线程池的核心线程数,默认情况下,核心线程在线程池中是一直存活的,即使它们处于空闲的状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut设置为ture,那空闲的核心线程在等待任务的过程中就会有超时策略,时间有keepAliveTime参数所指定,当等待时间超过keepAliveTime所指定的时间后,核心线程就会被终止;
②maximumPoolSize:线程池所能容纳的最大线程数,包括核心和非核心线程,当活动线程到达最大值,后续的任务就会被阻塞;
③keepAliveTiem:非核心线程闲置时的超时时长,当非核心线程空闲时间超过这个值,就会被终止。如果ThreadPoolExecutor的allowCoreThreadTimeOut设置为true,这个时间同样会作用在核心线程上;
④unit:keepAliveTime时间单位,这是一个枚举,如:TimeUnit.DAYS等;
⑤workQueue:线程池中的任务队列,通过execute方法提交的Runable对象会存储在这个参数中;
⑥threadFactory:线程工厂,提供线程的创建功能;
⑦另外还有一个不常用的参数 RejectedExecutionHandler handler,在线程数量和任务队列到达上限时,会通过这个handler通知外界异常。
VII. 线程池的的执行规则
1)如果线程池中的线程数量未到达核心线程的数量,那么会直接启动一个核心线程来处理任务;
2)如果线程池中的线程数已经达到或者超过核心线程数,那么任务会被插入到线程池的任务队列中排队等候;
3)如果步骤2中无法插入到任务队列中(原因可能是任务队列已经满了),这个时候如果线程池中的线程数未达到最大容量,则会直接启动一个非核心线程来处理任务;
4)如果步骤3中线程数量已经达到最大值,那么就拒绝执行任务, 这个时候就会通过 RejectedExecutionHandler handler这个参数来通知调用者。
VIII. 线程池的分类
FixedThreadPool
通过Executors的newFixedThreadPool方法来创建,这种线程池只有核心线程,没有超时策略,当所有线程处于活动状态时,任务会处于等待状态。由于这种线程池只有核心线程,并且即使空闲也不会被回收(除非线程池被回收),这就意味着它能够快速响应外界的请求。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
通过newFixedThreadPool方法的实现可以看到,线程池的最大容量=核心线程数(nThreads),没有超时策略。
CachedThreadPool
通过Executors的newCachedThreadPool方法来创建
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
通过这个方法的实现可以发现,这中线程池没有核心线程,并且最大线程数量是Integer.MAX_VALUE,这就意味着没有线程数量的限制,空闲超时时间为60秒。new SynchronousQueue
ScheduledThreadPool
通过Executors的newScheduledThreadPool方法来创建
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
通过这个方法的实现,可以看到,指定了核心线程数量,非核心线程数没有限制,非核心线程超时时间为10毫秒(相当于空闲后立即回收)。这类线程池适用于执行定时的任务和固定周期的重复任务。
SingleThreadPool
通过Executors的newSingleThreadExecutor方法来创建
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
这种线程池只有一个核心线程,它确保所有任务在一个线程中顺序执行。SingleThreadExecutor的意义在于,统一所有的任务在一个线程中处理,这使得这些任务之间不需要处理线程同步问题。
IX. 4种线程池的演示例子
Runnable command = new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
Log.d(TAG, "command is running");
}
};
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
fixedThreadPool.execute(command);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(command);
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
//延时两秒后执行
scheduledThreadPool.schedule(command, 2000, java.util.concurrent.TimeUnit.MILLISECONDS);
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
singleThreadPool.execute(command);
X. 我们在前面说到,AsyncTask使用到了线程池,我们去看看它的线程池是怎么样的
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
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 sPoolWorkQueue =
new LinkedBlockingQueue(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
①核心线程数: Math.max(2, Math.min(CPU_COUNT - 1, 4)),其中CPU_COUNT 是CUP的核数,CPU_COUNT = Runtime.getRuntime().availableProcessors();
②最大线程数:CPU_COUNT * 2 + 1
③超时时间:30S
以上源码均来自Android-24