[android] Handler vs AsyncTask vs Thread



Answers

如果你看看AsyncTaskHandler的源代碼,你會看到他們的代碼純粹是用Java編寫的。 (當然,也有一些例外,但這不是重點。)

所以在AsyncTaskHandler沒有魔法。 作為開發人員,他們只是讓你的工作更輕鬆。

例如:如果程序A調用方法A(),則方法A()可以與程序A在不同的線程中運行。您可以使用以下方法輕鬆驗證它:

Thread t = Thread.currentThread();    
int id = t.getId();

為什麼你應該使用新的線程? 你可以穀歌的。 許多原因。

那麼, ThreadAsyncTaskHandler之間的區別是什麼?

AsyncTaskHandler是用Java編寫的(在內部它們使用一個Thread ),所以您可以使用HandlerAsyncTask執行所有AsyncTask ,您也可以使用Thread來實現。

HandlerAsyncTask真的可以幫助你嗎?

最明顯的原因是調用者線程和工作線程之間的通信。 ( 調用者線程 :調用Worker線程來執行某個任務的線程,調用者線程不一定是UI線程)。 當然,您可以通過其他方式在兩個線程之間進行通信,但由於線程安全問題,存在很多缺點(和危險)。

這就是為什麼你應該使用HandlerAsyncTask 。 他們為你做了大部分工作,你只需要知道要覆蓋哪些方法。

HandlerAsyncTask之間的區別是:當調用者線程UI線程時使用AsyncTask 。 這是android文檔所說的:

AsyncTask支持正確和簡單地使用UI線程。 該類允許執行後台操作並在UI線程上發布結果,而無需操縱線程和/或處理程序

我想強調兩點:

1)易於使用UI線程(所以,當調用者線程是UI線程時使用)。

2)不需要操作處理程序。 (意思是:你可以使用Handler而不是AsyncTask,但AsyncTask是一個更簡單的選項)。

這篇文章中有很多東西我還沒有說過,例如:什麼是UI線程,或者為什麼它更容易。 你必須知道每種背後的一些方法並使用它,你會完全理解為什麼。

@:當你閱讀Android文檔時,你會看到:

Handler允許您發送和處理與線程的MessageQueue關聯的Message和Runnable對象

起初他們可能看起來很奇怪。 只要理解每個線程都有每個消息隊列(就像一個待辦事項列表),線程就會接收每條消息並執行,直到消息隊列為空(就像完成工作並上床睡覺一樣)。 所以,當Handler通信時,它只是給調用者線程發送消息,並等待處理。 複雜? 請記住, Handler可以以安全的方式與調用者線程進行通信。

Question

我對Android中的HandlersAsyncTaskThreads之間的差異略有困惑。 我在這裡閱讀了很多博客和問題。

Handler是後台線程,可讓您與UI進行通信。 例如更新進度條應通過Handler完成。 使用處理程序,您具有MessagingQueues的優勢,所以如果您想要安排消息或更新多個UI元素或者有重複任務。

AsyncTask是相似的,事實上它們使用Handler ,但不在UI線程中運行,因此它適用於獲取數據,例如獲取Web服務。 稍後您可以與UI進行交互。

但是Thread不能與UI交互,提供更多的“基本”線程,並且你錯過了AsyncTask所有抽象。

不過,我想要在服務中運行套接字連接。 應該在一個處理程序或線程中運行,還是在AsyncTask ? UI交互不是必需的。 它在我使用的性能方面有所不同嗎?

同時, documentation得到了重大改進。




當你啟動一個應用程序時,會創建一個進程來執行代碼。 為了有效地使用計算資源,可以在流程內啟動線程,以便當時可以執行多個任務。 所以線程允許你通過有效利用cpu而不需要空閒時間來構建高效的應用程序。

在Android中,所有組件都在一個被調用的主線程上執行。 Android系統隊列任務並在主線程上逐個執行它們。 當執行長時間運行的任務時,應用程序變得無響應。

為了防止這種情況,您可以創建工作線程並運行後台或長時間運行的任務。

處理器

由於android使用單線程模型,UI組件被創建為非線程安全的,這意味著只有它創建的線程才能訪問它們,這意味著UI組件應該只在主線程上更新。 當UI組件在主線程上運行時,在工作線程上運行的任務不能修改UI組件。 這是Handler進入畫面的地方。 處理程序在Looper的幫助下可以連接到新線程或現有線程,並運行連接線程中包含的代碼。

處理程序使得可以進行線程間通信。 使用Handler,後台線程可以向其發送結果,並且連接到主線程的處理程序可以更新主線程上的UI組件。

的AsyncTask

由android提供的AsyncTask使用線程和處理程序在後台運行簡單的任務,並將後台線程的結果更新為主線程。

有關示例請參閱android線程,處理程序,asynctask和線程池




AsyncTask用於執行一些後台計算並將結果發佈到UI線程(具有可選的進度更新)。 既然你不關心用戶界面,那麼HandlerThread似乎更合適。

你可以產生一個後台Thread並通過使用Handlerpost方法將消息傳遞回你的主線程。




Thread

您可以使用新Thread進行長時間運行的後台任務,而不會影響UI線程。 從Java線程,你不能更新UI線程。

由於普通的Thread對Android架構沒有多大用處,因此引入了線程的輔助類。

您可以在線程性能文檔頁面找到您的查詢的答案。

Handler

Handler允許您發送和處理與線程的MessageQueue關聯的Message和Runnable對象。 每個Handler實例都與單個線程和該線程的消息隊列相關聯。

處理程序有兩個主要用途:

  1. 安排消息和可運行子程序在將來的某個點執行;

  2. 排隊一個動作,以便在不同於你自己的線程上執行。

documentation

AsyncTask支持正確和簡單地使用UI線程。 該類允許您執行後台操作並在UI線程上發布結果,而無需操作線程和/或處理程序。

缺點:

  1. 默認情況下,應用程序將其創建的所有AsyncTask對象推送到單個線程中。 因此,它們以串行方式執行,並且與主線程一樣,特別長的工作包可以阻塞隊列。 由於這個原因,使用AsyncTask處理持續時間短於5ms的工作項目。

  2. AsyncTask對像也是隱式引用問題的最常見犯罪者。 AsyncTask對像也存在與明確引用相關的風險。

HandlerThread

您可能需要一種更傳統的方法來在較長時間運行的線程上執行一個工作塊( 與AsyncTask不同,它應該用於5ms工作負載 ),並且有一些手動管理該工作流的能力。 處理程序線程實際上是一個長時間運行的線程,可以從隊列中抓取工作,並對其進行操作。

ThreadPoolExecutor

這個類管理一組線程的創建,設置它們的優先級,並管理這些線程之間的工作分配方式。 隨著工作負載的增加或減少,該類會加速或破壞更多線程以適應工作負載。

如果工作負載更大並且單個HandlerThread不夠用,則可以使用ThreadPoolExecutor

不過,我想要在服務中運行套接字連接。 應該在一個處理程序或線程中運行,還是在AsyncTask中運行? UI交互不是必需的。 它在我使用的性能方面有所不同嗎?

由於UI交互不是必需的,您可能不會使用AsyncTask 。 普通線程沒有多大用處,因此HandlerThread是最佳選擇。 由於您必須維護套接字連接,主線程上的Handler根本沒有用處。 創建一個HandlerThread並從HandlerThread獲取一個Handler

 HandlerThread handlerThread = new HandlerThread("SocketOperation");
 handlerThread.start();
 Handler requestHandler = new Handler(handlerThread.getLooper());
 requestHandler.post(myRunnable); // where myRunnable is your Runnable object. 

如果你想回到UI線程,你可以使用一個更多的處理程序來處理響應。

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            //txtView.setText((String) msg.obj);
            Toast.makeText(MainActivity.this,
                    "Foreground task is completed:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

在你的Runnable ,你可以添加

responseHandler.sendMessage(msg);

有關實現的更多細節可以在這裡找到:

Android:在一個線程中敬酒




在我看來,線程並不是執行套接字連接的最有效方式,但它們確實提供了運行線程方面最多的功能。 我說,因為從經驗來看,長時間運行線程會導致設備非常熱和資源密集。 即使是一個簡單的while(true)會在幾分鐘內加熱手機。 如果你說UI交互不重要,也許一個AsyncTask是好的,因為它們是為長期過程而設計的。 這只是我的看法。

UPDATE

請忽略我的上面的答案! 我在2011年回答了這個問題,當時我對Android的經驗遠不及現在。 我上面的答案是誤導性的,被認為是錯誤的。 我要離開它,因為很多人在糾正我的意見後對它進行了評論,並且我吸取了教訓。

在這個線程上還有其他更好的答案,但我至少會給我更正確的答案。 使用常規Java Thread沒有任何問題; 然而,你應該真的小心你的實現方式,因為做錯了可能是處理器密集型的(最顯著的症狀可能是你的設備正在升溫)。 AsyncTask對於你想在後台運行的大多數任務非常理想(常見的例子是磁盤I / O,網絡調用和數據庫調用)。 但是, AsyncTask不應該用於在用戶關閉應用程序或將其設備置於待機狀態後可能需要繼續的特別長的進程。 我會說在大多數情況下,任何不屬於UI線程的東西都可以在AsyncTask




public class RequestHandler {

    public String sendPostRequest(String requestURL,
                                  HashMap<String, String> postDataParams) {

        URL url;

        StringBuilder sb = new StringBuilder();
        try {
            url = new URL(requestURL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(15000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);


            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));
            writer.write(getPostDataString(postDataParams));

            writer.flush();
            writer.close();
            os.close();
            int responseCode = conn.getResponseCode();

            if (responseCode == HttpsURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                sb = new StringBuilder();
                String response;
                while ((response = br.readLine()) != null){
                    sb.append(response);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException {
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (first)
                first = false;
            else
                result.append("&");

            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }

        return result.toString();
    }

}



Links