本篇是多線程系列的第四篇,如果對前三篇感興趣的也可以去看看。
除了前面的線程池的使用外,在Android中,我們除了通過Thread
創建線程外,還可以通過 AsyncTask
、IntentService
、HandleThread
來創建,線程池前面一篇已經詳細介紹了,下面對其他幾個方法簡單的介紹。
1.1、HandleThread
1.1.1、源碼
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
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;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public int getThreadId() {
return mTid;
}
}
可以看到 HandlerThread
繼承的是 Thread
,其在內部自己實現了 Looper
,可以單獨發送和接收消息,可以實現ui線程到子線程通信和子線程到子線程的通信。
1.1.2、使用
1、創建HandlerThread
並啓動
// 創建 HandlerThread 實例對象,參數爲自定義線程名字,作爲標記
handlerThread = new HandlerThread("HandlerThreadTest");
// 啓動 HandlerThread
handlerThread.start();
2、創建工作線程,並複寫 handleMessage
方法
// 創建工作線程,複寫 handleMessage 方法
workHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
boolean isMain = Looper.myLooper() == Looper.getMainLooper();
System.out.println("收到消息:what=" + msg.what + ",message=" + (String) msg.obj);
System.out.println("IsMainThread=" + isMain + ",currentThread:" + Thread.currentThread());
}
};
3、發送消息 (這裏模擬在子線程中發送消息)
new Thread(new Runnable() {
@Override
public void run() {
// 使用工作線程向工作線程發送數據。
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "測試數據";
workHandler.sendMessage(msg);
boolean isMain = Looper.myLooper() == Looper.getMainLooper();
System.out.println("IsMainThread=" + isMain + ",currentThread:" + Thread.currentThread());
}
}).start();
4、釋放
// 釋放
mHandlerThread.quitSafely();
運行結果:
可以看到上面的示例實現了在兩個子線程之間數據傳送。
如果我們將發送消息放在ui線程,即實現了ui線程到子線程的數據通信。
1.2、IntentService
1.2.1、源碼
同樣的,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((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@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);
}
@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;
}
@WorkerThread
protected abstract void onHandleIntent(Intent intent);
}
通過源碼,可以看到 IntentService
是一個抽象類,還有一個抽象方法 onHandleIntent
,繼承至 Service
1.2.2、使用
新建一個MyIntentService
繼承至 IntentService
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super("IntentService");
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate: ");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
boolean isMainThread = Looper.getMainLooper() == Looper.myLooper();
Log.i(TAG, "onHandleIntent: isMainThread=" + isMainThread);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "onHandleIntent: 任務結束");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
}
}
2、跟Service
一樣,需要在 AndroidManifest.xml
中進行註冊
<service android:name=".MyIntentService">
3、啓動服務
startService(new Intent(this, MyIntentService.class));
運行結果:
可以看到在MyIntentService
中 onHandleIntent
默認給我們開啓了一個子線程來執行耗時操作,且當任務執行結束後自動停止服務,和Service的區別是 使用Service 可以同時執行多個請求,而使用IntentService 只能同時執行一個請求。
1.3、AsyncTask
AsyncTask
也是我們在Android中使用較爲頻繁的異步通信方法,我們可以通過 AsyncTask
輕鬆的實現子線程到ui線程的通信,保證了線程安全,減少了我們通過 Thread
+ Handler
這種複雜的組合方法來實現,而且AsyncTask
實現原理也是通過線程池,所以也具有上面線程池的好處。
1.3.1、使用
public abstract class AsyncTask<Params, Progress, Result>{
// ......
}
首先可以看到 AsyncTask
是一個抽象類,所以,我們在使用的時候需要創建自己的類來實現它,還要實現裏面唯一的一個抽象方法。
protected abstract Result doInBackground(Params... params);
可以看到抽象方法有三個泛型參數,分別是.
Params : 開始異步任務執行時傳入的參數類型,與
doInBackground()
參數和執行時調用 excute() 參數類型一致。Progress : 異步任務執行過程中,返回下載進度值的類型,與
onProgressUpdate()
參數類型一致。Result : 異步任務執行完成後,返回的結果類型,與
doInBackground()
的返回值類型還有onPostExecute()
參數類型一致
使用 AsyncTask
時,除了必須實現的抽象方法外,一般還有幾個方法比較重要,可以根據我們的需求進行重寫
- onPreExecute:主線程,執行線程任務前自動調用,通過在這個方法裏面進行相應的初始化操作。
- doInBackground:子線程,必須實現的抽象方法,方法裏面爲工作線程,可以進行耗時操作。
- onProgressUpdate:主線程,任務執行時候,獲取進度,通過在
doInBackground
方法中調用publishProgress
來觸發 - onPostExecute :主線程,當任務執行結束後自動調用,可進行UI更新。
簡單示例:
// 新建 MyAsyncTask 實現 抽象類 AsyncTask
// Params:Integer
// Progress:String
// Result:Double
public class MyAsyncTask extends android.os.AsyncTask<Integer, String, Double> {
@Override
protected void onPreExecute() {
super.onPreExecute();
System.out.println(TAG + "--onPreExecute");
}
// 這裏的參數類型爲我們自己設置的Params 參數類型
@Override
protected Double doInBackground(Integer... integers) {
// 模擬耗時操作
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(TAG + "--doInBackground length=" + integers.length);
for (Integer i : integers) {
System.out.println(TAG + "--doInBackground data=" + i);
// 在 doInBackground 調用 publishProgress 觸發 onProgressUpdate
publishProgress(i + "—更新後的值");
}
// 這裏模擬返回結果,參數類型爲我們設置的 Double
return 2.2;
}
// 這裏的參數類型爲我們自己設置的String類型 (Progress)
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
for (String data : values) {
System.out.println(TAG + "--onProgressUpdate date=" + data);
}
}
// 任務執行結束後自動觸發,參數類型爲我們自己設置的Result 參數類型
@Override
protected void onPostExecute(Double aDouble) {
super.onPostExecute(aDouble);
System.out.println(TAG + "--onPostExecute aDouble=" + aDouble);
}
}
調用 MyAsyncTask
:
通過 execute
方法調用,需要在主線程裏面執行,傳入參數需要和 doInBackground
參數一致,及 Params
設置的類型。
只能調用一次,如果調用兩次
execute
方法,會拋異常Caused by: java.lang.IllegalStateException: Cannot execute task: the task is already running.
MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute(100,101,102);
結果:
1.3.2、源碼分析
我們深入 AsyncTask
大致看看它是怎麼實現的,首先從 構造方法入手
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
// 通過Callable創建一個任務
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 從 doInBackground 中獲取
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
// 任務執行結束,將結果 發送到
postResult(result);
}
// 返回結果
return result;
}
};
// 將任務傳給 FutureTask ,複寫其 done 方法
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
// WorkerRunnable
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
我們看到構造方法中,先創建了一個Callable
的任務,在 多線程(一)、基礎概念及notify()和wait()的使用 我們介紹了通過 Callable
來創建線程,可以在線程結束後獲取結果,我們也看到在 任務裏面通過 result = doInBackground(mParams)
獲取到結果。任務創建後,將其放入到 FutureTask
中,這是 Callable
線程創建的標準用法。
看完構造方法,我們再來看調用 execute
方法執行的時候,它又做了啥。
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
這裏沒啥信息,直接調用了executeOnExecutor
方法,傳入了 sDefaultExecutor
和我們設置的參數,再看 executeOnExecutor
方法:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
// 調用了 onPreExecute 方法
onPreExecute();
mWorker.mParams = params;
// 執行了我們在構造方法裏面創建的mFuture
exec.execute(mFuture);
return this;
}
這裏面我們還是看到很有用的信息,首先調用了 onPreExecute
方法,這也是我們在執行 AsyncTask
方法的時候,調用的第一個方法,用來進行一些初始化操作,我們的任務通過 exec.execute(mFuture)
進行執行,看看我們傳入的 sDefaultExecutor
是個啥。
最終創建了 SerialExecutor
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
內部聲明瞭一個雙端隊列ArrayDeque
類型的mTasks
(雙端隊列中offer方法表示從隊列尾插入,poll()表示從隊列頭獲取元素)。
這裏通過 mTasks.offer
將我們的任務不斷的放入阻塞隊列中,再通過 mTasks.poll()
拿出任務,最終通過 THREAD_POOL_EXECUTOR
來執行。
再看 THREAD_POOL_EXECUTOR
又是啥?
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
我們前面說了,AsyncTask
內部是通過線程池來執行的,看這裏應該就清楚了,任務的執行,實質是通過創建的 threadPoolExecutor
線程池,來執行任務。按照前面的老規矩,看看具體參數。
參數說明:
private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int KEEP_ALIVE_SECONDS = 3;
int corePoolSize: 1
int maximumPoolSize: 20
long keepAliveTime:3
TimeUnit unit:TimeUnit.SECONDS
BlockingQueue<Runnable> workQueue:new SynchronousQueue<Runnable>()
1個核心線程,非核心線程數目爲20,使用 SynchronousQueue
阻塞隊列。
這裏源碼是查看的API 29 ,不同的版本有區別。
看到了任務的執行,那麼AsyncTask是怎麼實現子線程到主線程的數據通信的呢?
postResult:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
前面構造中,我們看到通過 doInBackground
返回的結果,通過 postResult
方法傳遞出去了,而在 postResult
裏面就是通過Handler,來將數據從子線程傳到了主線程。
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
我們再看看publishProgress
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
其本質,也是通過 Handler
來實現子線程到主線程的數據通信。
總結:所以,AsyncTask 本質是通過線程池來執行任務,並且封裝了 Handler
來實現子線程到ui線程的數據傳輸。
優點:AsyncTask 是一個輕量級的異步任務處理類,輕量級體現在,使用方便、代碼簡潔上,而且整個異步任務的過程可以通過cancel()進行控制。
缺點:不適用於處理長時間的異步任務,一般這個異步任務的過程最好控制在幾秒以內,如果是長時間的異步任務就需要考慮多線程的控制問題;當處理多個異步任務時,UI更新變得困難。
二、總結
多線程部分一共四篇,這篇收了個尾,後面也會繼續對Android其他知識點進行總結,Android進階系列也會一直寫下去,如果文中有錯誤的地方,歡迎大佬們批評指點。