2012-01-09 74 views
5

關於ThreadsHandlersAsyncTask的正確使用有很多問題。 (如here & here處理程序與線程是否有任何真正的性能影響?

這些問題很好地解決了何時使用什麼問題。我的問題更多的是關於某些類型案件的績效影響。

作爲一個例子,我經常會看到其他人編寫代碼,他們使用Threads只是爲了能夠爲將來安排一些代碼執行。無論何時,當我看到這一點時,我本能地想要重構代碼以使用Handler,並且只是推遲了runnable

下面是一個例子,其中一個線程用於更新一些媒體的搜索欄,用mediaplayer進行播放,然後是我會這樣做的方式。

我看到了很多:

if (positionTracker != null && positionTracker.isAlive() 
       && !positionTracker.isInterrupted()) { 
      return; 
     } 

     positionTracker = new Thread(new Runnable() { 

      public void run() { 
       int currentPosition = 0; 
       int total = player.getDuration(); 
       while (player != null && CurrentPosition < total) { 
        try { 
         Thread.sleep(1000); 
         currentPosition = player.getCurrentPosition(); 
        } catch (InterruptedException e) { 
         return; 
        } catch (Exception e) { 
         return; 
        } 


        if (someListener != null) { 
         someListener.onEvent(); 
        } 

       } 
      } 

     }, "position tracker thread"); 

     positionTracker.start(); 

,我喜歡做的方式:

Runnable trackPositionRunnable = new Runnable() { 
      @Override 
      public void run() { 
       currentPosition = player.getCurrentPosition(); 
       if (someListener != null) { 
        someListener.onEvent(); 
        mHandler.postDelayed(this, 1000); 
       } 
      } 
     }; 

     mHandler.post(trackPositionRunnable); 

很顯然,我的首選方法是有點更容易閱讀和更多簡潔。 但是性能影響是什麼?在性能方面,單向方法比其他方法更好嗎?如果是這樣,爲什麼?

回答

1

正確性:你的第一個例子是充滿了危險,因爲MediaPlayer必須在一個線程中創建了自己的Looper和運營從任何其他線程可能會導致錯誤。同樣,由於您的someListener.onEvent()大概是更新了UI,因此最好知道是發佈到UI線程上的處理程序。

性能:我沒有測量要提供,但在您的示例中,運行時成本是(線程切換)+(處理器開銷),而不僅僅是(處理器開銷)。因此,對於任何線程切換開銷> 0,線程更昂貴。另一方面,如果您的整個應用程序都以您喜歡的風格編碼,並且任何一段代碼都很慢並且同步,那麼您的應用程序就會感覺遲緩。

這就是爲什麼任何潛在的緩慢或同步需要朝向線程(或服務)風格的原因,儘管感覺更復雜且容易出錯。您的特殊MediaPlayer示例不是用於製作此案例的完美海報。

+0

謝謝!所以,你說的同步開始時是一個你無法避免使用線程/服務的問題,但對於像我的例子這樣的小任務來說,處理程序可能是最簡單的方法呢? – LuxuryMode 2012-01-09 20:50:08

+1

我會說有點不同。將所有耗時的工作從UI線程中移走。在主UI線程上保留低成本的用戶界面。請記住,在多個HandlerThreads之間發佈是一個相當乾淨的下一步; post接口是一種乾淨的方式來請求事物,而少量的工作線程可能是異步的。但要非常注意確保您的所有UI操作都發生在主UI線程上。 – 2012-01-09 21:07:34

+0

至於「開關開銷」:因爲有一個線程在任何情況下運行,我沒有看到任何區別。 「已處理」線程也將以任何其他常規線程的方式進入和退出。 – 2013-09-04 14:10:38

4

每種方法都取決於您計劃在該Runnable中執行的操作,以確定它是否有用。它們之間最大的區別在於你是否打算接觸UI。在Android中,您無法觸摸UI線程中的UI組件(您的媒體播放器示例正在使用原始線程打破此規則)。因爲這個規則立即將你可以或不可以做的每種方法分開。這些方法之間的性能差異可以忽略不計,因爲運行後臺作業所花費的時間將超過它們之間的差異。

處理程序通常使用另一個後臺線程來執行邏輯,但它取決於哪個線程構造了處理程序。如果處理程序是在UI線程上構建的(作爲對回調onSomething的響應),那麼您的Runnable將在UI線程內運行,從而可以觸摸UI組件。但是,如果您創建的UI線程無法觸及UI組件,則Runnables會將其發佈到它。處理程序在UI線程上創建的缺點意味着您不在後臺執行這些操作,因此如果作業需要很長時間才能運行,則會鎖定用戶界面直至完成。雖然處理程序從非UI線程運行將解決任何鎖定用戶界面的問題。他們需要更多的工作來設置,並且您仍然必須應對如何安全地更新UI以響應您的後臺工作(即,如果要更新UI,您仍然必須將另一個可運行內容發佈回UI線程)。

原始線程不會鎖定UI,因爲它們獨立於UI線程運行,但無法觸及UI組件。這意味着您必須執行任何代碼才能在UI線程上更新UI,這意味着需要編寫更多代碼才能讓UI線程運行它。這可能非常複雜。由於使用它們的複雜性,真正應該避免原始線程。

後臺任務最常見的例子是等待來自服務器的響應。大多數庫會阻塞,直到服務器發送響應,這意味着您無法在UI線程上調用它們,否則在服務器返回呼叫之前,您的用戶將被阻止執行任何操作。它們不僅會被阻塞,而且UI也無法自我更新以顯示微調或其他外觀。這最好推到後臺線程。技術上的處理程序和線程可以做到這一點,但處理程序必須專門構建,以便它們將使用真正的後臺線程。

這是AsyncTask勝過處理程序的地方,因爲它同時處理真正的後臺作業和UI更新。它有一個部分用於在後臺執行一些長時間運行的操作,並且它有一個用於在UI線程完成時更新UI的部分。它甚至有一個可選的進度部分,因此您可以在任務運行時向UI提供任何中間進度。 AsyncTask的缺點是它們必須結束。後臺作業繼續運行以定期檢查是否發生了某些事情,睡眠和檢查更多,這些都不利於AsyncTask模型。然而,這並不是說你不能使用Handler來定期啓動AsyncTask,但是爲​​了討論的完整性,我提到了這一點。

最後,使用原始線程並不是那麼容易,甚至「更好」,因爲處理程序幾乎可以做任何線程可以用較少代碼執行的任何操作。但是,處理程序在確定Runnable在哪個線程上執行時非常棘手。通常它是UI線程,並且在技術上設置它來使用非UI線程是棘手的。這兩個選項都會遇到UI更新問題,因爲您必須在真正的後臺作業結束時執行額外的工作才能運行UI作業。 AsyncTask是我做後臺作業的首選方法。

-2

如果你正在處理的線程,我建議你同時使用一個處理程序與它:

Handler handle = new Handler(); 
Thread new Thread() 
{ 
    @Override 
    public void run() 
    { 
    try 
    { 
     handle.postDelayed(new Runnable() 
     { 
      @Override 
      public void run() 
      {   
      "your code goes here" 
      } 
     },delay); 
} 
     catch(Exception e) 
     { 
     e.printStackTrace(); 
     } 
    }; 
    } 

這樣可以延遲執行的,只要你願意,也可以使用postThread.sleep(delay),我更喜歡這些日子。

3

它不是Handler vs Threads。他們是完全不同的東西:

線程:是舊的Java類實現thread of execution。作爲Java API的其他部分,它們也可以在Android上使用。注意,在Java語言的後期版本中,它們被Executors framework所取代,所以推薦的做法是使用Executor/Runnable,但由於它的簡單性,線程有時仍被使用。

處理器:這個類是僅在Android中使用,它主要是與現有Thread溝通的機制。您發送目標線程Messages或Runnables,並且您還可以安排此通信。

當您需要將某些內容發送給某個主題時,您通常需要一個Handler。例如,這個「東西」可以是封裝的數據進行處理,或者可以在該線程中執行的Runnable。每個處理程序通常都在實例化時與當前線程關聯,除非使用更奇特的構造函數。一個典型的用例是在主線程(這是UI線程)中安排重複任務。請注意,對於安排一次性任務,有一個最簡單的方法:Activity.runOnUithread

現在對於需要在一個線程中運行比主一個不同的後臺任務:在這兩種方法,你就會有一個線程在運行,但創建一個處理程序意味着Android將啓動一個新的消息隊列的線程,這是普通線程不需要的東西,因此會有一些開銷。因此,如果你需要啓動一個可以在不接收信息的情況下運行的線程,我會說簡單的線程是首選。但是如果你需要一個執行隊列來調度Runnables,你可以在Timer,Executor,「處理過的」線程或者AlarmManager之間進行選擇。處理程序的優點是它們可以附加到應用程序中任何已經存在的線程,而定時器和執行程序將在它們設置時在內部啓動一個新的專用線程。

相關問題