多線程操做

 

對於多線程的操做:有兩種方式:網絡

1.繼承Thread類。new Thread(){run()}.start();多線程

2.實現Runnable類。new Thread(){new Runnable(){}}.start();異步

 

對於多線程的異步操做,處理方式有兩種:oop

  1. Handler+Thread方式,即對Looper對象的管理
  2. AsyncTask
  3. 多線程的封裝-線程池(線程池工廠或者自定義線程)

 

Handler+Thread方式:

對於Handler+Thread分爲主線程與副線程的操做,只要咱們對Looper對象操做便可,將消息message或者runnable進行send或者post便可。佈局

主線程:runOnUiThread(runnable action)、view.post(runnable action)、主線程的Handler.post(runnable action)、Handler.sendMessage(message)post

副線程:副線程的Handler.post(runnable action)性能

對於主線程的handler.sendMessage()再也不闡述,在activity上聲明一個Handler,默認handler構造器就是與主線程的Looper綁定,即在Handler的handleMessage()都是在主線程上的操做。ui

對於不管主線程仍是副線程,針對handler.post(runnable action)的狀況,Hanlder的封裝以下:spa

/**
     * UI線程 handler
     **/
private static Handler mUiHandler;

/**
    *副線程handler
    **/
private static Handler SUB_THREAD1_HANDLER;

/**
     * 副線程1
     */
private static HandlerThread SUB_THREAD1;
/**
    * 鎖
    **/
private static final Object mMainHandlerLock = new Object();

/**
     * 取得UI線程Handler
     *
     * @return
     */
    public static Handler getMainHandler() {
        if (mUiHandler == null) {
            synchronized (mMainHandlerLock) {
//                if (mUiHandler == null) {
                mUiHandler = new Handler(Looper.getMainLooper());
//                }
            }
        }
        return mUiHandler;
    }

/**
     * 得到副線程1的Handler.<br>
     * 副線程能夠執行比較快但不能在ui線程執行的操做.<br>
     * 此線程禁止進行網絡操做.若是須要進行網絡操做.
     * 請使用NETWORK_EXECUTOR</b>
     *
     * @return handler
     */
    public static Handler getSubThread1Handler() {
        if (SUB_THREAD1_HANDLER == null) {
            synchronized (ThreadManager.class) {
                SUB_THREAD1 = new HandlerThread("SUB1");
                SUB_THREAD1.setPriority(Thread.MIN_PRIORITY);//下降線程優先級
                SUB_THREAD1.start();
                SUB_THREAD1_HANDLER = new Handler(SUB_THREAD1.getLooper());
            }
        }
        return SUB_THREAD1_HANDLER;
    }

 

 view.post()與handler.post()的區別,使用不當可能致使內存泄露:.net

相關:http://blog.csdn.net/a740169405/article/details/69668957

若是調用view.post()方法的線程對象被GC-root引用(慎用static修飾Thread),則會形成內存泄漏

由於:

  GC-root->Thread->ThreadLocal->ViewRootImpl->runnable

可是主線程除外,由於雖然主線程的runQueue沒法被回收,可是每次執行完performTraversals(),都會將runQueue中的對象執行並清除。

常常咱們將線程池對象定爲static進行復用,所以須要慎用view.post().

  

 

 runOnUiThread(runnable action)與view.post(runnable action)的對比

共同點:第一種runOnUiThread,和第二種view.post()都是要求主線程執行,都是經過獲取主線程的handler,經過handler.post(),把action封裝成message傳到Looper的消息循環中,當handler再次處理該message時,直接調用action的run方法。

區別:view.post(runnable action)是在view初始化完成後才執行post(runnnable),而runOnUiThread()是主線程當即執行,若是在onCreate()進行初始化activity時,須要對佈局文件中的控件進行調整時,直接使用runOnUiThread代替view.post可能會形成view的信息獲取都爲空。

由於在activity建立時,生命週期onCreate(),佈局文件初始化還沒完成,在onCreate()方法上,僅僅是將控件的相關信息寫入內存,還沒映射到設備上,所以控件信息還沒初始化,廣泛上初始化完成要在onResume()方法以後。

 

handler原理

咱們能夠經過調用handler的post方法,把Runnable對象(通常是Runnable的子類)傳過去;handler會在looper中調用這個Runnable的Run方法執行。

Runnable是一個接口,不是一個線程,通常線程會實現Runnable。因此若是咱們使用匿名內部類是運行在UI主線程的,若是咱們使用實現這個Runnable接口的線程類,則是運行在對應線程的。

View.post原理

View.post(Runnable)方法。在post(Runnable action)方法裏,View得到當前線程(即UI線程)的Handler,而後將action對象post到Handler裏。在Handler裏,它將傳遞過來的action對象包裝成一個Message(Message的callback爲action),而後將其投入UI線程的消息循環中。在Handler再次處理該Message時,有一條分支(未解釋的那條)就是爲它所設,直接調用runnable的run方法。而此時,已經路由到UI線程裏,所以,咱們能夠毫無顧慮的來更新UI。

 拓展

若是咱們要進行耗時的操做,好比下載操做,咱們不能再UI線程;也不能直接在副線程直接進行下載,由於咱們要考慮多線程直接的顧慮,咱們引入線程池。

/**
     * AsyncTask的默認線程池Executor. 負責長時間的任務(網絡訪問) 默認3個線程
     */
    public static final Executor NETWORK_EXECUTOR;
    static {
        NETWORK_EXECUTOR = initNetworkExecutor();
    }
private static Executor initNetworkExecutor() {
        Executor result;
        // 3.0以上
        if (Build.VERSION.SDK_INT >= 11) {
            //result = AsyncTask.THREAD_POOL_EXECUTOR;
            result = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>());
        }
        // 3.0如下, 反射獲取
        else {
            Executor tmp;
            try {
                Field field = AsyncTask.class.getDeclaredField("sExecutor");
                field.setAccessible(true);
                tmp = (Executor) field.get(null);
            } catch (Exception e) {
                tmp = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                        new LinkedBlockingQueue<Runnable>());
            }
            result = tmp;
        }

        if (result instanceof ThreadPoolExecutor) {
            //線程數改成CPU 核數 +1
            ((ThreadPoolExecutor) result).setCorePoolSize(FitmixUtil.getPhoneCpuCoreNum() + 1);
        }

        return result;
    }
/**
     * 在網絡線程上執行異步操做. 該線程池負責網絡請求等操做 長時間的執行(如網絡請求使用此方法執行) 固然也能夠執行其餘 線程和AsyncTask公用
     *
     * @param run
     */
    public static void executeOnNetWorkThread(Runnable run) {
        try {
            NETWORK_EXECUTOR.execute(run);
        } catch (RejectedExecutionException e) {
        }
    }

 

 

 AsyncTask

引言:

我不太贊成封裝好就會影響性能的說法,在我實際的運用中,真正的缺點來自於AsyncTask的全局線程池只有5個工做線程,也就是說,一個APP若是運用AsyncTask技術來執行線程,那麼同一時間最多隻能有5個線程同時運行,其餘線程將被阻塞(注:不運用AsyncTask執行的線程,也就是本身new出來的線程不受此限制),因此AsyncTask不要用於多線程取網絡數據,由於極可能這樣會產生阻塞,從而下降效率。

 

 

AsyncTask是封裝好的線程池,比起Thread+Handler的方式,AsyncTask在操做UI線程上更方便,由於onPreExecute()、onPostExecute()及更新UI方法onProgressUpdate()均運行在主線程中,這樣就不用Handler發消息處理了;

 

一個異步任務的執行通常包括如下幾個步驟:

1.execute(Params... params),執行一個異步任務,須要咱們在代碼中調用此方法,觸發異步任務的執行。

2.onPreExecute(),在execute(Params... params)被調用後當即執行,通常用來在執行後臺任務前對UI作一些標記。

3.doInBackground(Params... params),在onPreExecute()完成後當即執行,用於執行較爲費時的操做,此方法將接收輸入參數和返回計算結果。在執行過程當中能夠調用publishProgress(Progress... values)來更新進度信息。

4.onProgressUpdate(Progress... values),在調用publishProgress(Progress... values)時,此方法被執行,直接將進度信息更新到UI組件上。

5.onPostExecute(Result result),當後臺操做結束時,此方法將會被調用,計算結果將作爲參數傳遞到此方法中,直接將結果顯示到UI組件上。

相關文章
相關標籤/搜索