Android 開發(fā)藝術(shù)探索筆記(十五) 之 Android 的線程和線程池

一、概述

Android 的線程分主線程和子線程,主線程主要處理界面相關(guān)的事情,而子線程往往用于執(zhí)行耗時(shí)操作。Android 中扮演子線程角色的有:AsyncTaskIntentServiceHandlerThreadAsyncTask 封裝了線程池和 Handler,主要用于在子線程中更新UI。 HandlerThread 是一種具有消息循環(huán)的線程,在它的內(nèi)部可以使用 Handler。IntentService 是一個(gè) Service,系統(tǒng)對(duì)其進(jìn)行了封裝使其可以方便地執(zhí)行后臺(tái)任務(wù),執(zhí)行完任務(wù)后自動(dòng)調(diào)用 onDestroy() 退出。它是一個(gè) Service,因而在執(zhí)行后臺(tái)任務(wù)的時(shí)候不容易被系統(tǒng)殺死。

二、AsyncTask

AsyncTask 是一種輕量級(jí)的異步任務(wù)類,它可以在線程池中執(zhí)行后臺(tái)任務(wù),然后在主線程中回調(diào)執(zhí)行的進(jìn)度以及最終結(jié)果,從而更新 UI。其實(shí),AsyncTask 內(nèi)部封裝了 Thread 和 Handler。

ASyncTask 是一個(gè)抽象類。

public abstract class AsyncTask<Params,Progress,Result>

主要有 4 個(gè)核心的方法:

  • onPreExecute():在主線程執(zhí)行,在異步任務(wù)執(zhí)行之前的準(zhǔn)備工作,比如:提示 Loading

  • doInBackground(Params... params):在線程池中執(zhí)行,此方法用于執(zhí)行異步任務(wù),params 表示異步任務(wù)的輸入?yún)?shù)。

  • onProgressUpdated(Progress... value):在主線層中執(zhí)行,當(dāng)后臺(tái)任務(wù)的進(jìn)度發(fā)生改變時(shí)此方法被調(diào)用。

  • onPostExecute(Result result):在主線程執(zhí)行,在異步任務(wù)執(zhí)行完成后回調(diào)此方法。

  • onCancelled():在主線程執(zhí)行,當(dāng)異步任務(wù)被取消時(shí)會(huì)回調(diào)此方法。

舉個(gè)栗子:

class DownloadTask extends AsyncTask<URL,Integer,Long>{

        @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);

劃重點(diǎn)來啦 !!!

  • 1. AsyncTask 的類必須在主線程中加載,不過這個(gè)在 Android 4.1 及以上版本已經(jīng)在 ActivityThread 中初始化了。

  • 2. AsyncTask 對(duì)象必須在主線程中創(chuàng)建。

  • 3. execute() 必須在 UI 線程中調(diào)用。

  • 4. 不要在程序中直接調(diào)用 onPreExcute()doInBackground()onPostExecute()onProgressUpdate()

  • 5. 一個(gè) AsyncTask 對(duì)象只能執(zhí)行一次,即只能調(diào)用一次 execute(),否則會(huì)報(bào)運(yùn)行異常。

  • 6. 可以通過 executeOnExecutor() 來并行執(zhí)行任務(wù)。

三、HandlerThread

HandlerThread 繼承 Thread,它封裝有 Looper 和 MessageQueue,可以綁定 Handler,從而實(shí)現(xiàn)線程間通信/異步消息處理機(jī)制。

舉個(gè)栗子

/**
 * 使用 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);

        //創(chuàng)建后臺(tái)線程
        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) {
                    //循環(huán)執(zhí)行 HandlerThread Handler 的 handleMessage()
                    mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
                }
            }
        };


    }

    /**
     * 模擬從服務(wù)器解析數(shù)據(jù)
     */
    private void checkForUpdate() {
        try {
            //模擬耗時(shí)
            Thread.sleep(1000);
            //切換回主線程
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    String result = "實(shí)時(shí)更新中,當(dāng)前大盤指數(shù):<font color='red'>%d</font>";
                    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();
    }

}

這個(gè)栗子里面通過 HandlerThread 線程的 Handler 來通知 UI 線程的 Handler 來更新 UI,同時(shí)在 handleMessage() 方法中循環(huán)執(zhí)行 HandlerThread 的方法,這個(gè)也是一種定時(shí)循環(huán)執(zhí)行任務(wù)的方法。

源碼分析:

/**
 * 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;
    
    //內(nèi)部有一個(gè) 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;
    }

我們仔細(xì)看看 run(),這里面調(diào)用了 Looper.prepare()Loop.loop()Looper.prepare() 中創(chuàng)建了一個(gè) Looper 對(duì)象,并且把該對(duì)象放到了該線程范圍內(nèi)的變量中(sThreadLocal),在 Looper 對(duì)象的構(gòu)造過程中,初始化了一個(gè) MessageQueue ,作為該 Looper 對(duì)象成員變量。Looper.loop() 就不斷地循環(huán)從 MessageQueue 中取消息處理了,當(dāng)沒有消息的時(shí)候會(huì)阻塞,有消息的到來的時(shí)候會(huì)喚醒。

如果我們不想自己初始化 HandlerThread 的 Handler 對(duì)象,可以直接使用 HandlerThread 里面的 Handler,使用的時(shí)候就只能用 post(Runnable r),因?yàn)?HandlerThread 的 Handler 沒有派生子類,只能使用 Message.callback 來執(zhí)行任務(wù)。

源碼

四、IntentService

IntentService 是一種特殊的 Service,它繼承了 Service,并且是一個(gè)抽象類,子類必須實(shí)現(xiàn) onHandleIntent() 才可以使用。IntentService 可用于執(zhí)行后臺(tái)任務(wù),我們可以通過 startService(Intent) 來提交請(qǐng)求,該Service會(huì)在需要的時(shí)候創(chuàng)建,當(dāng)完成所有的任務(wù)以后自己關(guān)閉,且請(qǐng)求是在工作線程處理的。而且由于它是 Service,比單純的后臺(tái)線程優(yōu)先級(jí)高,不容易被系統(tǒng)殺死。

優(yōu)點(diǎn):

  • 1.不需要自己去 new Thread

  • 2.不需要考慮在什么時(shí)候關(guān)閉該 Service

舉個(gè)栗子

使用 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";

    /**
     * 外部調(diào)用開啟任務(wù)
     * @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 {
            //模擬上傳耗時(shí)
            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,然后復(fù)寫onHandleIntent方法,根據(jù)傳入的intent來選擇具體的操作。

運(yùn)行 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) 來創(chuàng)建多個(gè)任務(wù),可以看到 onCreate()只調(diào)用了一次,onStartCommand()onStart() 會(huì)回調(diào)多次。

劃重點(diǎn)啦 !!!

源碼解析:

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() 里面初始化了一個(gè) HandlerThread,同時(shí)實(shí)例化了一個(gè)綁定該 HandlerThread 的 Handler。當(dāng)任務(wù)開始的時(shí)候,會(huì)回調(diào) onStartCommand(),即回調(diào) onStart(),在 onStart() 中可以看到這個(gè) Handler 發(fā)送了一條消息給自己,于是就會(huì)調(diào)用自己的處理方法,最后調(diào)用 onHandleIntent((Intent)msg.obj) 方法,所以我們?cè)谑褂?IntentService 的時(shí)候需要重寫 onHandleIntent((Intent)msg.obj)

而且,由于 HandlerThread 是串行執(zhí)行任務(wù)的,所以 IntentService 也是串行執(zhí)行任務(wù)的,執(zhí)行完成后會(huì)調(diào)用 stopSelf(msg.arg1); 判斷是否銷毀該 Service(stopSelf(msg.arg1);會(huì)等待所有消息都處理完畢才會(huì)終止服務(wù))。所以當(dāng)任務(wù)完成,銷毀 Service 回調(diào) onDestory()時(shí),源碼里面會(huì)釋放了我們的 Looper: mServiceLooper.quit()

源碼

五、線程池

使用線程池有幾個(gè)好處:

  • 1.重用線程池的線程,避免為線程的創(chuàng)建和銷毀所帶來的性能開銷

  • 2.能有效控制線程池的最大并發(fā)數(shù),避免大量線程之間因互相搶占資源而導(dǎo)致的阻塞現(xiàn)象

  • 3.能夠?qū)€程進(jìn)行簡(jiǎn)單的管理,并提供定時(shí)執(zhí)行以及指定間隔循環(huán)執(zhí)行等功能。

5.1 ThreadPoolExecutor

ThreadPoolExecutor 是線程池的真正實(shí)現(xiàn),它的構(gòu)造方法為:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
                          
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
  • corePoolSize:線程池的核心線程數(shù),默認(rèn)情況下,核心線程會(huì)在線程池一直存活,即使它們處于閑置的狀態(tài)。如果將 ThreadPoolExecutor 的 allowCoreThreadTimeOut 屬性為 true,那么閑置的核心線程在等待新任務(wù)到來的時(shí)候會(huì)有超時(shí)策略,這個(gè)時(shí)間間隔超過 keepAliveTime 后,核心線程也會(huì)被終止。

  • maximumPoolSize:線程池所能容納的最大線程數(shù)。當(dāng)活動(dòng)線程數(shù)達(dá)到這個(gè)數(shù)值后,后續(xù)的新任務(wù)會(huì)被阻塞。

  • keepAliveTime:非核心線程閑置時(shí)的超時(shí)時(shí)長(zhǎng),超過這個(gè)時(shí)長(zhǎng),非核心線程就會(huì)被回收。

  • unit:超時(shí)時(shí)長(zhǎng)單位:毫秒,秒,分

  • workQueue:線程池的任務(wù)隊(duì)列,通過線程池的 execute() 提交的 Runnable 對(duì)象就存儲(chǔ)在這個(gè)隊(duì)列中。

  • threadFactory:線程工廠,為線程池提供創(chuàng)造新線程的功能。

ThreadPoolExecutor 執(zhí)行任務(wù)時(shí)的規(guī)則:

  • 1.如果線程池中的線程數(shù)量未達(dá)到核心線程的數(shù)量,那么會(huì)直接啟動(dòng)一個(gè)核心線程來執(zhí)行任務(wù)

  • 2.如果線程池中的線程數(shù)量已經(jīng)達(dá)到或者超過核心線程數(shù)量,那么任務(wù)就會(huì)被插入到任務(wù)隊(duì)列中排隊(duì)等待執(zhí)行

  • 3.如果步驟2中的任務(wù)無法插到任務(wù)隊(duì)列,這往往是由于任務(wù)隊(duì)列已滿,這時(shí)如果線程數(shù)量未達(dá)到線程池的最大值,那么就會(huì)立刻啟動(dòng)一個(gè)非核心線程來執(zhí)行任務(wù)。

  • 4.如果步驟3中線程數(shù)量已經(jīng)達(dá)到線程池規(guī)定的最大值,那么就會(huì)拒絕執(zhí)行這項(xiàng)任務(wù),ThreadPoolExecutor 就會(huì)調(diào)用 RejectedExecutionHandler 的 rejectExecution() 來通知調(diào)用方。

5.2 線程池分類

  • 1.FixedThreadPool:一種線程數(shù)量固定的線程池,核心線程數(shù)就是線程池能容納的最大線程數(shù)。所以當(dāng)線程處于空閑狀態(tài)時(shí),它們并不會(huì)回收,除非線程池被關(guān)閉了。當(dāng)所有線程都處于活動(dòng)狀態(tài)時(shí),新任務(wù)都會(huì)處于等待狀態(tài),直到有空閑的線程。因?yàn)?FixedThreadPool 只有核心線程并且這些核心線程不會(huì)被回收,這就意味著它能更加快速地響應(yīng)外界的請(qǐng)求。FixedThreadPool 的任務(wù)隊(duì)列沒有大小限制。

    構(gòu)造方法:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    

使用方法:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
fixedThreadPool.execute(Runnable r);
  • CachedThreadPool:一種線程數(shù)量不定的線程池,只有非核心線程,并且最大線程數(shù)為 Interger.MAX_VALUE。當(dāng)線程池中的線程都處于活動(dòng)狀態(tài)時(shí),線程池會(huì)創(chuàng)建新的線程來處理任務(wù),否則就會(huì)利用空閑線程來處理新任務(wù)。線程池中的空閑線程都有超時(shí)機(jī)制,超時(shí)時(shí)長(zhǎng)為 60s,超過 60s 就會(huì)被回收。它比較適合執(zhí)行大量的耗時(shí)較少的任務(wù)。

    構(gòu)造方法:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    

使用方法:

ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
cacheThreadPool.execute(Runnable r);
  • ScheduledThreadPool:核心線程數(shù)量固定,線程池能容納的最大線程數(shù)沒有限制,并且當(dāng)非核心線程閑置時(shí)會(huì)被立即回收。主要用于執(zhí)行定時(shí)任務(wù)和具有固定周期的重復(fù)任務(wù)。

    構(gòu)造方法:

    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秒后執(zhí)行該任務(wù)
      scheduledThreadPool.schedule(new Runnable() {
          @Override
          public void run() {

          }
      }, 2, TimeUnit.SECONDS);
      
      //延遲1秒后,每隔2秒執(zhí)行一次該任務(wù)
      scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
          @Override
          public void run() {

          }
      }, 1, 2, TimeUnit.SECONDS);
  • SingleThreadExecutor:這類線程池內(nèi)部只有一個(gè)核心線程,它確保所有任務(wù)都在同一個(gè)線程中按順序執(zhí)行。意義就是統(tǒng)一所有外界的任務(wù)到同一線程中,使得這些任務(wù)之間不需要處理線程同步的問題。

    構(gòu)造方法:

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    

使用方法:

ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();

      //延遲1秒后,每隔2秒執(zhí)行一次該任務(wù)
      singleThreadScheduledPool.scheduleAtFixedRate(new Runnable() {
          @Override
          public void run() {
              String threadName = Thread.currentThread().getName();
              Log.v("zxy", "線程:" + threadName + ",正在執(zhí)行");
          }
      },1,2,TimeUnit.SECONDS);
      
   singleThreadScheduledPool.execute(Runnable r);   

六、參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容