一、概述
Android 的线程分主线程和子线程,主线程主要处理界面相关的事情,而子线程往往用于执行耗时操作。Android 中扮演子线程角色的有:AsyncTask、IntentService、HandlerThread。AsyncTask 封装了线程池和 Handler,主要用于在子线程中更新UI。 HandlerThread 是一种具有消息循环的线程,在它的内部可以使用 Handler。IntentService 是一个 Service,系统对其进行了封装使其可以方便地执行后台任务,执行完任务后自动调用 onDestroy()
退出。它是一个 Service,因而在执行后台任务的时候不容易被系统杀死。
二、AsyncTask
AsyncTask 是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后在主线程中回调执行的进度以及最终结果,从而更新 UI。其实,AsyncTask 内部封装了 Thread 和 Handler。
ASyncTask 是一个抽象类。
public abstract class AsyncTask
主要有 4 个核心的方法:
onPreExecute():在主线程执行,在异步任务执行之前的准备工作,比如:提示 Loading
doInBackground(Params... params):在线程池中执行,此方法用于执行异步任务,params 表示异步任务的输入参数。
onProgressUpdated(Progress... value):在主线层中执行,当后台任务的进度发生改变时此方法被调用。
onPostExecute(Result result):在主线程执行,在异步任务执行完成后回调此方法。
onCancelled():在主线程执行,当异步任务被取消时会回调此方法。
举个栗子:
class DownloadTask extends AsyncTask{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Long doInBackground(URL... urls) {
return null;
}
@Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
}
new DownloadTask().execute(url1,url2,url3);
划重点来啦 !!!
1. AsyncTask 的类必须在主线程中加载,不过这个在 Android 4.1 及以上版本已经在 ActivityThread 中初始化了。
2. AsyncTask 对象必须在主线程中创建。
3.
execute()
必须在 UI 线程中调用。4. 不要在程序中直接调用
onPreExcute()
、doInBackground()
、onPostExecute()
和onProgressUpdate()
。5. 一个 AsyncTask 对象只能执行一次,即只能调用一次
execute()
,否则会报运行异常。6. 可以通过
executeOnExecutor()
来并行执行任务。
三、HandlerThread
HandlerThread 继承 Thread,它封装有 Looper 和 MessageQueue,可以绑定 Handler,从而实现线程间通信/异步消息处理机制。
举个栗子
/**
* 使用 HandlerThread
*/
public class HandlerThreadActivity extends AppCompatActivity {
private TextView mTvServiceInfo;
private HandlerThread mCheckMsgThread;
//HandlerThread 的 handler
private Handler mCheckMsgHandler;
private boolean isUpdateInfo;
private static final int MSG_UPDATE_INFO = 0x110;
//与UI线程管理的handler
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
//创建后台线程
initBackThread();
mTvServiceInfo = findViewById(R.id.id_textview);
}
@Override
protected void onResume() {
super.onResume();
//开始查询
isUpdateInfo = true;
mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
@Override
protected void onPause() {
super.onPause();
//停止查询
isUpdateInfo = false;
mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO);
}
private void initBackThread() {
mCheckMsgThread = new HandlerThread("check-message-coming");
mCheckMsgThread.start();
//绑定 HandlerThread 的 Looper
mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if (isUpdateInfo) {
//循环执行 HandlerThread Handler 的 handleMessage()
mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
}
}
};
}
/**
* 模拟从服务器解析数据
*/
private void checkForUpdate() {
try {
//模拟耗时
Thread.sleep(1000);
//切换回主线程
mHandler.post(new Runnable() {
@Override
public void run() {
String result = "实时更新中,当前大盘指数:%d";
result = String.format(result, (int) (Math.random() * 3000 + 1000));
mTvServiceInfo.setText(Html.fromHtml(result));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
mCheckMsgThread.quit();
}
}
这个栗子里面通过 HandlerThread 线程的 Handler 来通知 UI 线程的 Handler 来更新 UI,同时在 handleMessage() 方法中循环执行 HandlerThread 的方法,这个也是一种定时循环执行任务的方法。
源码分析:
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
//内部有一个 Handler 成员变量
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
}
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
我们仔细看看 run()
,这里面调用了 Looper.prepare()
,Loop.loop()
。Looper.prepare()
中创建了一个 Looper 对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在 Looper 对象的构造过程中,初始化了一个 MessageQueue ,作为该 Looper 对象成员变量。Looper.loop()
就不断地循环从 MessageQueue 中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
如果我们不想自己初始化 HandlerThread 的 Handler 对象,可以直接使用 HandlerThread 里面的 Handler,使用的时候就只能用 post(Runnable r)
,因为 HandlerThread 的 Handler 没有派生子类,只能使用 Message.callback 来执行任务。
源码
四、IntentService
IntentService 是一种特殊的 Service,它继承了 Service,并且是一个抽象类,子类必须实现 onHandleIntent()
才可以使用。IntentService 可用于执行后台任务,我们可以通过 startService(Intent)
来提交请求,该Service会在需要的时候创建,当完成所有的任务以后自己关闭,且请求是在工作线程处理的。而且由于它是 Service,比单纯的后台线程优先级高,不容易被系统杀死。
优点:
1.不需要自己去 new Thread
2.不需要考虑在什么时候关闭该 Service
举个栗子
使用 IntentService 上传照片
public class UploadImgService extends IntentService {
private static final String ACTION_UPLOAD_IMG = "com.zhy.blogcodes.intentservice.action.UPLOAD_IMAGE";
public static final String EXTRA_IMG_PATH = "com.zhy.blogcodes.intentservice.extra.IMG_PATH";
/**
* 外部调用开启任务
* @param context
* @param path
*/
public static void startUploadImg(Context context, String path) {
Intent intent = new Intent(context, UploadImgService.class);
intent.setAction(ACTION_UPLOAD_IMG);
intent.putExtra(EXTRA_IMG_PATH, path);
context.startService(intent);
}
public UploadImgService() {
super("UploadImgService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_UPLOAD_IMG.equals(action)) {
final String path = intent.getStringExtra(EXTRA_IMG_PATH);
handleUploadImg(path);
}
}
}
private void handleUploadImg(String path) {
try {
//模拟上传耗时
Thread.sleep(3000);
Intent intent = new Intent(MainActivity.UPLOAD_RESULT);
intent.putExtra(EXTRA_IMG_PATH, path);
//通过广播通知 UI 线程
sendBroadcast(intent);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG","onCreate");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("TAG","onBind");
return null;
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.e("TAG","onSatrtCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Log.e("TAG","onSatrt");
super.onStart(intent, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("TAG","onDestroy");
}
}
上面的代码主要就是继承IntentService,然后复写onHandleIntent方法,根据传入的intent来选择具体的操作。
运行 Log:
04-16 07:56:59.894 19936-19936/com.innovator.intentservicetest E/TAG: onCreate
onSatrtCommand
onSatrt
04-16 07:57:00.500 19936-19936/com.innovator.intentservicetest E/TAG: onSatrtCommand
onSatrt
04-16 07:57:00.774 19936-19936/com.innovator.intentservicetest E/TAG: onSatrtCommand
onSatrt
04-16 07:57:01.788 19936-19936/com.innovator.intentservicetest E/TAG: onSatrtCommand
onSatrt
04-16 07:57:01.965 19936-19936/com.innovator.intentservicetest E/TAG: onSatrtCommand
onSatrt
通过 startUploadImg(Context context, String path)
来创建多个任务,可以看到 onCreate()
只调用了一次,onStartCommand()
和 onStart()
会回调多次。
划重点啦 !!!
源码解析:
package android.app;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
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);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
protected abstract void onHandleIntent(Intent intent);
}
可以看到它在 onCreate()
里面初始化了一个 HandlerThread,同时实例化了一个绑定该 HandlerThread 的 Handler。当任务开始的时候,会回调 onStartCommand()
,即回调 onStart()
,在 onStart()
中可以看到这个 Handler 发送了一条消息给自己,于是就会调用自己的处理方法,最后调用 onHandleIntent((Intent)msg.obj)
方法,所以我们在使用 IntentService 的时候需要重写 onHandleIntent((Intent)msg.obj)
。
而且,由于 HandlerThread 是串行执行任务的,所以 IntentService 也是串行执行任务的,执行完成后会调用 stopSelf(msg.arg1);
判断是否销毁该 Service(stopSelf(msg.arg1);
会等待所有消息都处理完毕才会终止服务)。所以当任务完成,销毁 Service 回调 onDestory()
时,源码里面会释放了我们的 Looper: mServiceLooper.quit()
。
源码
五、线程池
使用线程池有几个好处:
1.重用线程池的线程,避免为线程的创建和销毁所带来的性能开销
2.能有效控制线程池的最大并发数,避免大量线程之间因互相抢占资源而导致的阻塞现象
3.能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
5.1 ThreadPoolExecutor
ThreadPoolExecutor 是线程池的真正实现,它的构造方法为:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize:线程池的核心线程数,默认情况下,核心线程会在线程池一直存活,即使它们处于闲置的状态。如果将 ThreadPoolExecutor 的
allowCoreThreadTimeOut
属性为 true,那么闲置的核心线程在等待新任务到来的时候会有超时策略,这个时间间隔超过keepAliveTime
后,核心线程也会被终止。maximumPoolSize:线程池所能容纳的最大线程数。当活动线程数达到这个数值后,后续的新任务会被阻塞。
keepAliveTime:非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。
unit:超时时长单位:毫秒,秒,分
workQueue:线程池的任务队列,通过线程池的
execute()
提交的 Runnable 对象就存储在这个队列中。threadFactory:线程工厂,为线程池提供创造新线程的功能。
ThreadPoolExecutor 执行任务时的规则:
1.如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
2.如果线程池中的线程数量已经达到或者超过核心线程数量,那么任务就会被插入到任务队列中排队等待执行
3.如果步骤2中的任务无法插到任务队列,这往往是由于任务队列已满,这时如果线程数量未达到线程池的最大值,那么就会立刻启动一个非核心线程来执行任务。
4.如果步骤3中线程数量已经达到线程池规定的最大值,那么就会拒绝执行这项任务,ThreadPoolExecutor 就会调用 RejectedExecutionHandler 的
rejectExecution()
来通知调用方。
5.2 线程池分类
-
1.FixedThreadPool:一种线程数量固定的线程池,核心线程数就是线程池能容纳的最大线程数。所以当线程处于空闲状态时,它们并不会回收,除非线程池被关闭了。当所有线程都处于活动状态时,新任务都会处于等待状态,直到有空闲的线程。因为 FixedThreadPool 只有核心线程并且这些核心线程不会被回收,这就意味着它能更加快速地响应外界的请求。FixedThreadPool 的任务队列没有大小限制。
构造方法:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
()); }
使用方法:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
fixedThreadPool.execute(Runnable r);
-
CachedThreadPool:一种线程数量不定的线程池,只有非核心线程,并且最大线程数为 Interger.MAX_VALUE。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理任务,否则就会利用空闲线程来处理新任务。线程池中的空闲线程都有超时机制,超时时长为 60s,超过 60s 就会被回收。它比较适合执行大量的耗时较少的任务。
构造方法:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue
()); }
使用方法:
ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
cacheThreadPool.execute(Runnable r);
-
ScheduledThreadPool:核心线程数量固定,线程池能容纳的最大线程数没有限制,并且当非核心线程闲置时会被立即回收。主要用于执行定时任务和具有固定周期的重复任务。
构造方法:
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()); }
使用方法:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
//延迟2秒后执行该任务
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
}
}, 2, TimeUnit.SECONDS);
//延迟1秒后,每隔2秒执行一次该任务
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
}
}, 1, 2, TimeUnit.SECONDS);
-
SingleThreadExecutor:这类线程池内部只有一个核心线程,它确保所有任务都在同一个线程中按顺序执行。意义就是统一所有外界的任务到同一线程中,使得这些任务之间不需要处理线程同步的问题。
构造方法:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
())); }
使用方法:
ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();
//延迟1秒后,每隔2秒执行一次该任务
singleThreadScheduledPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
Log.v("zxy", "线程:" + threadName + ",正在执行");
}
},1,2,TimeUnit.SECONDS);
singleThreadScheduledPool.execute(Runnable r);
六、参考资料
Android HandlerThread 完全解析
Android IntentService完全解析 当Service遇到Handler
Android性能优化之使用线程池处理异步任务