線程相關
目前常用的線程有:
- 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>()));
}
該線程池的特點
- 只有一個核心線程
- 沒有非核心線程
- 可以確保所有任務在一個線程中都能按順序執行
該線程使用場景:可以統一所有外界任務到一個線程中,任務之間不需要處理線程同步的問題。