关于Android线程和线程池的那些事
线程相关
目前常用的线程有:
- Thread 最基础线程执行方式
- AsyncTask 封装了线程池和Handler,为了方便开发者在子进程中更新UI
- HandlerThread
- IntentService 内部采用HandlerThread执行任务,本质是一个Service,但是表现的更像Thread。因为Thread在后台容易被杀死,所以常用来在后台运行。
AsyncTask
使用方法
class MyAsyncTask extends AsyncTask<URL,Integer,Long>{
/**
* 执行异步任务
* @param urls
* @return
*/
@Override
protected Long doInBackground(URL... urls) {
int count=100;
long totalSize=0;
for (int i=0;i<count;i++){
totalSize+=10;
publishProgress(i*100/count); //调用onProgressUpdate
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (isCancelled()){
break;
}
}
return totalSize;
}
/**
* 执行之前调用
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/**
* 完成时调用
* @param aLong
*/
@Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
tv_display.setText("Download"+aLong+"bytes");
}
/**
* 后台进度发生改变时调用
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
tv_display.setText("进度"+values[0]);
}
}
new MyAsyncTask().execute();
注意事项
- AsyncTask 必须在主线程创建
- 必须在主线程执行,也就是调用excute();
- 不要手动调用doInBackground,onPreExecute,onPostExecute,onProgressUpdate
- 每个AsyncTask对象仅能执行一次
- AsyncTask在android3.0 以上是串行执行,在android 3.0以下是并行执行。表现状态为用同一个AsyncTask类创建多个AsyncTask对象,并同时调用excute。android 3.0以上会在一个任务执行完成以后,执行另外一个任务。而早期版本会同时执行。
- 如果希望android3.0以上,AsyncTask也能并发执行,那么需要调用executeOnExecutor用来替代excute
HandlerThread
源码
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
可以看到其内部的run方法,直接新建了Looper。这样就能在非UI线程中,创建Handler了。
这个方法一般配合IntentService使用,
注意事项
- Handler的run 方法是个死循环,在不使用的时候,需要手动调用quit,或者quitSafely来结束线程
IntentService
使用方法
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
this(null);
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.d(TAG, "onHandleIntent: "+intent.getStringExtra("action"));
SystemClock.sleep(3000);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: MyIntentService");
}
}
Intent intent=new Intent(ThreadTestActivity.this,MyIntentService.class);
for (int i=0;i<5;i++){
intent.putExtra("action","action"+i);
startService(intent);
}
注意:虽然IntentService很像thread ,但是本质仍然是service,需要在androidManifest中进行注册。
源码
@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);
}
可以看出IntentService在创建的时候,就创建了HandlerThread,然后通过他的Looper创建了Handler。
而在IntentService每次被startServcie的时候,都会调用onStartCommand,然后startCommand又会去调用onstart方法,所以我们来看下onstart方法。
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
可以看出,在onstart方法中,intentService 将startSevice的intent和startId。放入了handler中。既让放入了handler中,那么我们就需要去看下handlerMessage方法
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);
}
}
这个就很简单了,在handler中如果收到消息,则调用抽象方法onHandlerIntent 去处理,而onHandlerIntent ,就是我们需要实现的方法。也就实现了,在非UI线程执行某个操作。
而最后的stopSerlf,传入的是startId。这个方法会对比现在传入的startId和IntentrService收到的最后第一个startId是不是一样的。也就是当前处理的消息是不是最后一条消息,如果是最后一条消息,则停止该service。
注意: 由于IntentService内部是由handler实现的,所以也具有Handler的特点。也就是串行执行的,一个方法执行完成以后,才会执行下一个方法。
线程池相关
android中常用的线程池
- ThreadPoolExecutor
- FixedThreadPool
- CachedThreadPool
- ScheduledThreadPool
- SingleThreadExecutor
其中ThreadPoolExecutor可以算作是线程池接口Executor最基础的实现。后面几个都是对于ThreadPoolExecutor的封装
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
核心线程的数量,默认一直存活。可以通过allowCoreThreadTimeOut和keepAliveTime配置是否闲置超时关闭和超时的时间
maximumPoolSize
线程池最大的线程数,超出将阻塞
keepAliveTime
配置非核心线程超时关闭的时间。通过allowCoreThreadTimeOut也可以配置核心线程超时关闭的时间
unit
超时的单位,枚举值。
workQueue
存储任务的队列
threadFactory
线程工厂,提供创建新进程的功能
FixedThreadPool
核心代码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
从上面代码可以看出,是创建了一个具有以下特点的线程池
- 核心线程一直存活,
- 线程数量固定的线程池。
- 只包含核心线程。
- 队列没有大小限制。
该线程使用场景:适合数量较少耗时较长的任务
CachedThreadPool
核心代码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
从上面代码可以看出,该方法创建了一个具有以下特点的线程池
- 没有核心线程
- 线程数量几乎没有限制的
- 超时时间为60秒
- 所有的任务都会立即执行
该线程使用场景:适合数量大耗时较少的任务
ScheduledThreadPool
核心代码
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
该线程池的特点
- 核心线程固定
- 非核心线程几乎无限大
- 非核心线程超时的时候,会立马回收
该线程使用场景:执行定时和具有周期性的任务
SingleThreadExecutor
核心代码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
该线程池的特点
- 只有一个核心线程
- 没有非核心线程
- 可以确保所有任务在一个线程中都能按顺序执行
该线程使用场景:可以统一所有外界任务到一个线程中,任务之间不需要处理线程同步的问题。