最近打算关掉博客,专注公众号,发现瘫痪的博客上面有一些 3 年前写的技术文章,看了一下还是不舍得扔掉,暂且迁移到这里。微信上贴代码的技术文章一般不受人待见,注意前方高能预警。
0. Android 多线程实现方式
通常来说,一个应用至少有一个进程,而一个进程至少有一个线程。 线程是 CPU 调度的基本单位,进程是系统资源分配的基本单位。
进程独享内存资源,一个进程可以看作一个 JVM ,一个进程崩溃后,在保护模式下一般不会对其它进程产生影响。 同一个进程中的线程共享内存资源,一个线程死掉就导致整个进程死掉。
Android 提供了四种常用的多线程实现方式:
1. AsyncTask
Android AsyncTask 类,它是封装好的线程池,操作 UI 线程极其方便。 AsyncTask 的三个泛型参数:
public abstract class AsyncTask<Params, Progress, Result>
- Params ,传入参数类型,即 doInBackground() 方法中的参数类型; - Progress,异步任务执行过程中返回的任务执行进度类型,
即 publishProgress() 和onProgressUpdate() 方法中传入的参数类型; - Result,异步任务执行完返回的结果类型\,即 doInBackground() 方法中返回值的类型。 四个回调方法: - onPreExecute(),在主线程执行,做一些准备工作。 - doInBackground(),在线程池中执行,该方法是抽象方法,在此方法中可以调用 publishProgress() 更新任务进度。 - onProgressUpdate(),在主线程中执行,在 publishProgress() 调用之后被回调,展示任务进度。 - onPostExecute(),在主线程中执行,异步任务结束后,回调此方法,处理返回结果。
注意:
改动如下:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
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;
2. 异步消息机制
异步消息机制的三大主角: Handler ,Message 和 Looper 。 Looper 负责创建 MessageQueue 消息对列,然后进入一个无限 for 循环中,不断地从消息队列中取消息,如果消息队列为空,当前线程阻塞,Handler 负责向消息队列中发送消息。
Looper
Looper 有两个重要的方法: prepare() 和 loop()。 prepare() , Looper 与当前线程绑定,一个线程只能有一个 Looper 实例和一个 MessageQueue 实例。
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true)); 保证 Looper 对象在当前线程唯一
}
// Looper 的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
loop ,进入一个无限 for 循环体中,不断地从消息队列中取消息,然后交给消息的 target 属性的 dispatchMessage 方法去处理。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 无限循环体,有没有想过在 UI 线程里,有这样一个死循环,为什么界面没卡死??
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
Handler
Handler 负责向消息队列中发送消息。 在 Activity 中我们直接可以 new Handler ,那是因为在 Activity 的启动代码中,已经在当前 UI 线程中调用了 Looper.prepare() 和 Looper.loop() 方法。 在子线程中 new Handler 必须要在当前线程(子线程)中创建好 Looper 对象和消息队列,代码如下
//在子线程中
Looper.prepare();
handler = new Handler() {
public void handleMessage(Message msg) {
//处理消息
};
};
Looper.loop();
之后,你拿着这个 Handler 对象就可以在其他线程中,往这个子线程的消息队列中发消息了。
HandlerThread
HandlerThread 可以看作在子线程中创建一个异步消息处理机制的简化版,HandlerThread 对象自动帮我们在工作线程里创建 Looper 对象和消息队列。
使用方法:
mHandlerThread = new HandlerThread("MyHandlerThread");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//处理消息
}
};
之后你就可以使用 Handler 对象往工作线程中的消息队列中发消息了。
看一下源码片段:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
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;
}
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;
}
}
注意:handler 在 UI 线程中初始化的,looper 在一个子线程中执行,我们必须等 mLooper 创建完成之后,才能调用 getLooper ,源码中是通过 wait 和 notify 解决两个线程的同步问题。
3. IntentService
IntentService 可以看成是 Service 和 HandlerThread 的合体。它继承自 Service ,并可以处理异步请求,其内部有一个 WorkerThread 来处理异步任务,当任务执行完毕后,IntentService 自动停止。
如果多次启动 IntentService 呢? 看到 HandlerThread ,你就应该想到多次启动 IntentService ,就是将多个异步任务放到任务队列里面,然后在 onHandlerIntent 回调方法中串行执行,执行完毕后自动结束。
下面对源码进行简单的解析,IntentService 源码:
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 方法在工作线程中执行,执行完调用 stopSelf() 结束服务。
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* enabled == true 时,如果任务没有执行完,当前进程就死掉了,那么系统就会令当前进程重启。
* 任务会被重新执行。
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
// 上面已经讲过,HandlerThread 对象 start 之后,会在工作线程里创建消息队列 和 Looper 对象。
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
// 获得 Looper 对象初始化 Handler 对象。
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
// IntentService 每次启动都会往工作线程消息队列中添加消息,不会创建新的线程。
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
// 官方建议 IntentService onStartCommand 方法不应该被重写,注意该方法会调用 onStart 。
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
//服务停止会清除消息队列中的消息,除了当前执行的任务外,后续的任务不会被执行。
mServiceLooper.quit();
}
/**
* 不建议通过 bind 启动 IntentService ,如果通过 bind 启动 IntentService ,那么 onHandlerIntent 方法不会被回调。Activity 与 IntentService 之间的通信一般采用广播的方式。
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* 子类必须要实现,执行具体的异步任务逻辑,由 IntentService 自动回调。
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
IntentService 源码很容易理解,你也可以就自己的应用场景封装自己的 IntentService 。
几种场景:
4. ThreadPoolExcutor
图片来自 Jakob Jenkov 博客
ThreadPool
用来管理一组工作线程,任务队列( BlockingQueue )中持有的任务等待着被线程池中的空闲线程执行。
常用构造方法:
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
);
简单使用
创建 ThreadFactory ,当然也可以自定义。
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());
}
};
创建 ThreadPoolExecutor 。
// 根据 CPU 核心数确定线程池容量。
public static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
mThreadPoolExecutor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2 + 1,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
backgroundPriorityThreadFactory
);
执行。
mThreadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
//do something
}
});
Future future = mThreadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
//do something
}
});
//任务可取消
future.cancel(true);
Future<Integer> futureInt = mThreadPoolExecutor.submit(new Callable<Integer>() {
@override
public Integer call() throws Exception {
return 0;
}
});
//获取执行结果
futureInt.get();
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>(){
@override
public Integer call() throws Exception {
return 0;
}
});
mThreadPoolExecutor.submit(task);
task.get();