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, Result>
,這三個參數中的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<String, Integer, String>{
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,必須要在清單文件<application>.....</application>
裏面註冊的。
<service android:name=".LocalIntentService"/>
執行如下代碼
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<Runnable> 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<Runnable>());
}
通過newFixedThreadPool方法的實現可以看到,線程池的最大容量=核心線程數(nThreads),沒有超時策略。
CachedThreadPool
通過Executors的newCachedThreadPool方法來創建
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
通過這個方法的實現可以發現,這中線程池沒有核心線程,並且最大線程數量是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<Runnable>()));
}
這種線程池只有一個核心線程,它確保所有任務在一個線程中順序執行。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<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(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
最近搭建了個人博客,個人博客原文地址