Android線程

Android線程概述

線程分為主線程和子線程,主線程主要處理和界面相關的事情,子線程則往往用于處理耗時操作。

線程是操作系統最小的調度單元,同時線程又是一種受限的系統資源,即線程不可能無限制的產生,并且線程的創建和銷毀都會有響應的開銷。線程池會緩存一定數量的線程,通過線程池可以避免因為頻繁創建和銷毀線程所帶來的系統開銷。android中的線程池來源于Java,主要是通過Executor來派生特定類型的線程池。

主線程和子線程

主線程主要處理界面交互相關的邏輯,因為用戶隨時會和界面發生交互,因此主線程在任何時候都必須要有較高的響應速度,否則就會產生一種界面卡頓的感覺。

子線程,也叫工作線程,除了主線程以外的線程都叫子線程。

Android中的主線程也叫UI線程,作用是運行四大組件,以及處理它們和用戶的交互。子線程執行耗時任務,比如網絡請求,I/O操作等。

四種線程形態

Thread

最基本的線程使用方法,從Java繼承而來。使用方法可以直接從Thread生成變量,或者使用Runnable初始化Thread。不再贅述。

AsyncTask

封裝了線程池,和Handler,主要為了方便開發者在子線程中更新UI。

可以在線程池中執行后臺任務,然后把執行的進度和最終的結果傳遞給主線程中更新UI。從實現上來說,AsyncTask封裝了Thread和Handler,可以很方便的執行后臺任務并更新UI。但是AsyncTask不適合進行特別耗時的任務,耗時任務建議使用線程池做。

四個核心方法

  • onPreExecute,在主線程中執行,異步任務執行之前,此方法被調用,一般可以用于做一些準備工作
  • doInBackground,在線程池中執行,用于執行異步任務,可以調用publishProgres更新任務的進度,publishProgress會調用onProgressUpdate方法。
  • onProgressUpdate,主線程中執行,進度發生變化。
  • onPostExecute,主線程中執行,異步任務執行完成之后,此方法會被調用。

一些限制

  • AsyncTask的必須在主線程中加載。
  • AsyncTask的對象必須在主線程中創建。
  • execute方法必須在UI線程中調用。
  • 不能直接調用onPreExecute,onPostExecute,doInBackground,onProgressUpdate。
  • 一個AsyncTask對象只能執行一次,即只能調用一次execute方法。
  • android 1.6之前AsyncTask串行執行,android 1.6到android 3.0之前為并行執行,android 3.0包括(3.0)串行執行。

工作原理

execute直接調用了executeOnExecutor,兩個方法源代碼如下:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
        //直接調用了executeOnExecutor方法。
}
 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
            //此處保證一個AsyncTask只執行一次
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException(".....");
                case FINISHED:
                    throw new IllegalStateException("......");
            }
        }
        mStatus = Status.RUNNING;
        //調用onPreExecute,此時仍然在主線程上
        onPreExecute();
        mWorker.mParams = params;
        //在線程池上執行
        exec.execute(mFuture);
        return this;
    }

sDefaultExecutor是一個串行的線程池,sDefaultExecutor代碼如下:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
            //保存需要執行的線程的隊列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        //當前正在執行的線程
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
                //offer,想隊列的尾部插入一個數據。
                //不能直接將參數的Runnable加入到隊列中,因為在參數的run方法執行完之后執行scheduleNext方法,所以新建了一個Runnable對象,在新建的Runnable對象的run方法中直接執行了參數的run方法。
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
                //獲取頭部的對象,并賦值給mActivie,如果不是null,則在線程池THREAD_POOL_EXECUTOR上執行。
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
   }

從上面的代碼中可以看到AsyncTask是現行執行的。

AsyncTask有兩個線程池,分別為sDefaultExecutor和THREAD_POOL_EXECUTOR,其中THREAD_POOL_EXECUTOR用來真正的執行AsyncTask,sDefaultExecutor用來做排隊,保證AsyncTask順序執行。

如何保證能夠更新UI

AsyncTask有一個InternalHandler,其代碼如下:

 private static class InternalHandler extends Handler {
         //構造函數,使用的Looper時主線程Looper,保證其處理消息是在主線程上。
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

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

使用方法

  1. 生成AsncTask的對象,需要重寫doInBackground,和onPostExecute方法。其中doInBackground方法是子線程需要執行的操作,onPostExecute表示子線程處理之后主線程需要執行的操作。如果需要可以重寫onProgressUpdate方法,在主線程顯示進度的變化。
  2. 執行AsyncTask的execute方法。

HandlerThread

是一種具有了消息循環的線程,在它的內部可以使用Handler。實現非常簡單,就是在run方法中通過Looper.prepare來創建消息隊列,通過loop方法開啟消息循環,這樣在實際的使用中就可以在HandlerThread中創建Handler了。其run方法如下:

@Override
    public void run() {
        mTid = Process.myTid();
        //創建消息隊列
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        //開啟消息循環
        Looper.loop();
        mTid = -1;
    }

HandlerThread有getLooper方法獲取該線程對應的Looper,將次Looper傳遞給Handler就可以子線程中執行一些操作,比如主線程需要子線程執行網絡請求,則可以通過Handler post一個Runnable即可,此Runnable會請求網絡數據,數據請求完成之后將數據保存起來,或者將數據更新到UI(需要進行線程切換,比如使用主線程的handler來更新界面)。使用方法示例代碼:

public class Activity1 extends Activity implement OnClickListener{
    //HandlerThread使用的Handler
    private Handler mHandler;
    private HandlerThread mHandlerThread;
    private Handler uiHandler = new Handler(){
        @override
         public void handleMessage(Message msg) {
         //處理消息
         //更新界面
         }
    };
    
    private Button btn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(this);
        mHandlerThread = new HandlerThread("Test");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
    }
    
    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
          Log.d("MainActivity", "test HandlerThread...");
          try {
           //從網絡請求數據
           getDataFromNetWork();
           //使用uiHandler執行一個post請求
           uiHandler.obtainMessage(1,"finished").sendToTarget();
           } catch (Exception e) {
               e.printStackTrace();
           }
        }
    };
    
    @Override
    public void onClick(View v) {
        switch(v.getId()) {
        case R.id.btn :
            mHandler.post(mRunnable);
            break;
        default :
            break;
        }
    }
    
    @Override
    protected void onDestroy() {
        mRunning = false;
        //移除回調
        mHandler.removeCallbacks(mRunnable);
        super.onDestroy();
    }
}

使用步驟:

  1. 聲明Handler,HandlerThread對象。
  2. 生命Runnable對象
  3. 初始化HandlerThread對象, mHandlerThread = new HandlerThread("Test");
  4. HandlerThread啟動,mHandlerThread.start();
  5. 初始化Handler對象,mHandler = new Handler(mHandlerThread.getLooper());
  6. 執行Runnable,mHandler.post(mRunnable);
  7. 在Runnable對象的run方法執行完畢后可以調用主線程的Handler,進行界面更新。

IntentService

是一個服務(Service),系統對其進行了封裝使其可以更方便地執行后臺任務,IntentService內部采用了HandlerThread來執行任務,當任務執行完畢之后IntentService會自動退出。作為一個服務,IntentService不容易被系統殺死從而可以盡量保證任務的執行。

IntentService是Service的子類,但它是一個抽象類,因此需要創建它的子類才能使用。其封裝了HandlerThread和Handler。從其代碼中可以看出其使用了HandlerThread。

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

Intent的處理

看其onStartCommand方法,onStartCommand掉哦那個onStart方法,而onStart的代碼為:

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //直接調用了mServiceHandler發送消息,保證了消息在子線程中被調用。
        mServiceHandler.sendMessage(msg);
    }

ServiceHanlder對消息的處理如下:

private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
                //調用onHandleIntent處理消息
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
}

IntentService的onHandleIntent方法為一個抽象方法,需要子類重寫。

protected abstract void onHandleIntent(Intent intent);

由于Looper是順序執行任務的,因此IntentService也是順序執行任務的。

線程池

線程池的優點

  • 重用線程池中的線程,避免因為線程的創建和銷毀帶來的性能開銷。
  • 能有效的控制線程池的最大并發數,避免大量的線程之間因相互搶占系統資源而導致的阻塞現象。
  • 能夠對線程驚醒簡單的管理,并提供定時執行以及指定間隔循環執行等功能。

Android中得線程池來源于Java中的Executor,Executor是一個接口,真正的線程池的實現為ThreadPoolExecutor. ThreadPoolExecutor提供了一些列參數來配置線程池,根據不同的參數可以創建不同的線程池,從線程池的功能特性來說,Android的線程池主要有四類,通過Executors所提供的方法來得到。

ThreadPoolExecutor

ThreadPoolExecutor是線程池的真正實現,其構造方法提供了一系列參數來配置線程池。構造方法如下所示:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) 
  • corePoolSize,核心線程數,默認情況下核心線程會一直存活,即使它們處于閑置狀態。如果線程池的allowCoreThreadTimeOut屬性為true,則閑置的核心線程在等待新任務到來時會有超時策略,時間間隔有keepAliveTime決定。
  • maximumPoolSize,線程池所能容納的最大線程數,當活動線程數達到這個數值后,后續的新任務將會被阻塞。
  • keepAliveTime,非核心線程閑置時的超時時長,超過這個時長,非核心線程就會被回收。allowCoreThreadTimeOut屬性為true,keepAliveTime同樣作用于核心線程。
  • unit,keepAliveTime的單位,使用TimeUnit的值。
  • workQueue,任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個參數中。
  • threadFactory,線程工廠,為線程池創建一個新的線程。ThreadFactory是一個接口,只有一個Thread newThread(Runnable r)方法。

除了上面的參數外還有一個參數,RejectedExecutionHandler,當線程池無法執行任務時會調用handler的rejectedExecution方法通知調用者。

規則

  • 如果線程池的線程數量未達到核心線程的數量,就會直接啟動一個核心線程。
  • 如果線程池中的線程數量已經達到或者超過核心線程的數量,那么任務會被插入到任務隊列中排隊等待執行。
  • 如果無法將任務插入到任務隊列中,這往往是由于任務隊列已經滿了,這個時候如果線程數量未達到線程池規定的最大值,那么會立刻啟動一個非核心線程執行任務。
  • 如果上面的線程數量已經達到線程池規定的最大值,那么就拒絕執行此任務會調用RejectedExecutionHandler 的rejectedExecution方法。

AsyncTask的線程池

  private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
  private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
  private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
  private static final int KEEP_ALIVE = 1;
  
  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);

    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

從上面的代碼可以知道AsyncTask對THREAD_POOL_EXECUTOR這個線程池進行了配置,最后的規則為:

  • 核心線程數為CPU核心數+1
  • 線程池的最大線程數為CPU核心數的2倍 + 1
  • 核心線程無超市機制,非核心線程在閑置時的超時時間為1秒
  • 任務隊列的容量為128

線程池分類

  • FixedThreadPool,通過Executors的newFiexedThreadPool方法創建,是線程數量固定的線程池。只有核心線程且核心線程不會被回收,能更快的響應外界的請求。
  • CachedThreadPool,通過Executors的newCachedThreadPool方法創建,只有非核心線程,且最大線程數為Integer.MAX_VALUE。
  • ScheduleedThreadPool,通過Executors的newScheduleedThreadPool方法創建,核心線程個數固定,非核心線程個數無限制,用于執行定時任務和具有固定周期的重復任務。
  • SingleThreadPool,通過Executors的newSingleThreadPool方法創建,只有一個核心線程,確保所有的任務都在同一個線程中順序執行。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 線程分為主線程和子線程,主線程主要是做與界面相關的事,而子線程往往用于做耗時操作。Android中扮演線程的角色有...
    SeanMa閱讀 1,040評論 0 6
  • 線程 操作系統調度最小單元,不可能無限制產生(受限的系統資源),線程創建與銷毀有相應的開銷。當系統中存在大量線程的...
    墨染書閱讀 657評論 1 5
  • 1. 線程的定義 線程(thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作...
    b923228cc7b5閱讀 833評論 1 4
  • 3.4 Android線程 3.4.1 簡介 在Android中默認情況下一個進程只有一個線程,也就是主線程,其他...
    jianhuih閱讀 215評論 0 0
  • 心里要有光+話癆 這兩個詞是在一個育兒專家那里得來的. 她說心里要有光,然后她跟我一樣是話癆. 其實真實生活中現在...
    美人吟閱讀 253評論 0 0