關於Android線程和線程池的那些事


線程相關

目前常用的線程有:

  • 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();

注意事項

  1. AsyncTask 必須在主線程創建
  2. 必須在主線程執行,也就是調用excute();
  3. 不要手動調用doInBackground,onPreExecute,onPostExecute,onProgressUpdate
  4. 每個AsyncTask對象僅能執行一次
  5. AsyncTask在android3.0 以上是串行執行,在android 3.0以下是並行執行。表現狀態為用同一個AsyncTask類創建多個AsyncTask對象,並同時調用excute。android 3.0以上會在一個任務執行完成以后,執行另外一個任務。而早期版本會同時執行。
  6. 如果希望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>()));
    }

該線程池的特點

  • 只有一個核心線程
  • 沒有非核心線程
  • 可以確保所有任務在一個線程中都能按順序執行

該線程使用場景:可以統一所有外界任務到一個線程中,任務之間不需要處理線程同步的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM