2012-01-09 24 views
5

Đã có một số câu hỏi liên quan đến việc sử dụng thích hợp Threads so với Handlers so với AsyncTask. (như here & here)Có bất kỳ tác động thực sự thực sự nào của Trình xử lý và Chủ đề không?

Các câu hỏi đó giải quyết tốt nhất câu hỏi khi nào nên sử dụng cái gì. Câu hỏi của tôi là nhiều hơn về ý nghĩa hiệu suất trong một số trường hợp nhất định.

Ví dụ, tôi thường thấy người khác viết mã trong đó họ sử dụng Threads chỉ đơn giản là để có thể lên lịch thực hiện mã cho tương lai. Bất cứ khi nào, tôi thấy điều này, tôi theo bản năng cảm thấy như refactoring mã để sử dụng một Handler và chỉ là một bài bị trì hoãn runnable.

Dưới đây là ví dụ trong đó Chủ đề được sử dụng để cập nhật thanh tìm kiếm cho một số phương tiện đang phát với mediaplayer và sau đó là cách tôi sẽ thực hiện.

những gì tôi nhìn thấy rất nhiều:

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

Và cách tôi muốn làm điều đó:

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

     mHandler.post(trackPositionRunnable); 

Rõ ràng, cách ưa thích của tôi là một chút dễ dàng hơn để đọc và nhiều hơn nữa ngắn gọn. Nhưng ý nghĩa hiệu suất là gì? Là một cách tốt hơn phương pháp, về hiệu suất, hơn khác? Nếu vậy, tại sao?

Trả lời

1

Chính xác: Ví dụ đầu tiên của bạn đầy nguy hiểm, vì phải tạo MediaPlayer trên một sợi có riêng Looper và các thao tác từ bất kỳ chủ đề nào khác có thể gây ra lỗi. Tương tự như vậy, vì someListener.onEvent() của bạn có lẽ đang cập nhật giao diện người dùng, nên tốt hơn là bạn nên đăng bài lên trình xử lý trên luồng giao diện người dùng.

Hiệu suất: Tôi không có số đo để cung cấp, nhưng trong ví dụ của bạn, chi phí thời gian chạy là (chuyển đổi luồng) + (chi phí xử lý), so với chỉ (tổng phí xử lý). Vì vậy, đối với bất kỳ thread chuyển đổi trên không> 0, các chủ đề là tốn kém hơn. Mặt khác, nếu toàn bộ ứng dụng của bạn được mã hóa theo phong cách ưa thích của bạn và bất kỳ phần nào của mã của bạn đều chậm và đồng bộ, bạn chỉ làm cho ứng dụng của bạn cảm thấy bị lag.

Đó là lý do tại sao mọi thứ có thể chậm hoặc đồng bộ cần phải hướng tới kiểu luồng (hoặc dịch vụ), mặc dù cảm thấy phức tạp hơn và dễ bị lỗi. Ví dụ MediaPlayer cụ thể của bạn không phải là một đứa trẻ áp phích hoàn hảo để thực hiện trường hợp này.

+0

Cảm ơn! Vì vậy, bạn đang nói khi đồng bộ hóa bắt đầu là một vấn đề mà bạn thực sự không thể tránh sử dụng các luồng/dịch vụ, nhưng đối với các tác vụ nhỏ như ví dụ của tôi, một trình xử lý có lẽ là cách đơn giản nhất? – LuxuryMode

+1

Tôi muốn nói nó hơi khác. Di chuyển mọi thứ tốn thời gian ra khỏi chuỗi giao diện người dùng. Giữ giao diện người dùng chi phí thấp trên luồng giao diện người dùng chính. Hãy nhớ rằng việc đăng bài trong nhiều HandlerThread là một bước tiếp theo hợp lý sạch sẽ; giao diện bài đăng là cách sạch sẽ để yêu cầu mọi thứ và một số lượng nhỏ chuỗi công việc có thể không đồng bộ đủ. Tuy nhiên, hãy lưu ý đến việc đảm bảo tất cả các hoạt động giao diện người dùng của bạn diễn ra trên chuỗi giao diện người dùng chính. –

+0

Đối với "chuyển đổi trên không": vì có một chuỗi chạy trong mọi trường hợp, tôi thấy không có sự khác biệt. Chuỗi "được xử lý" cũng sẽ được chuyển vào và ra như bất kỳ luồng thông thường nào khác. –

4

Mỗi phương pháp phụ thuộc vào những gì bạn định làm trong Runnable đó là liệu nó có hữu ích hay không. Sự khác biệt lớn nhất giữa chúng là liệu bạn có kế hoạch chạm vào giao diện người dùng hay không. Trong Android, bạn không thể chạm vào các thành phần giao diện người dùng khỏi chuỗi giao diện người dùng (ví dụ về trình phát phương tiện của bạn đang vi phạm quy tắc này với Chủ đề thô). Vì quy tắc này chia ngay lập tức những gì bạn có thể và không thể làm với từng phương pháp. Hiệu suất khác biệt giữa các phương pháp này là không đáng kể vì thời gian dành cho công việc nền của bạn sẽ vượt trội hơn bất kỳ sự khác biệt nào giữa chúng.

Trình xử lý thường sử dụng một chuỗi nền khác để thực thi logic, nhưng nó phụ thuộc vào chuỗi nào đã tạo Trình xử lý. Nếu Handler được xây dựng trên UI Thread (để đáp lại lời gọi onSomething) thì Runnable của bạn sẽ chạy bên trong UI Thread, làm cho nó ok khi chạm vào UI Components. Tuy nhiên, nếu bạn đã tạo nó ra khỏi thread UI Runnables được đăng lên nó KHÔNG THỂ chạm vào các thành phần UI.Nhược điểm của Trình xử lý được tạo trên luồng giao diện người dùng có nghĩa là bạn không làm việc này ở chế độ nền nên nếu một công việc mất nhiều thời gian để chạy nó sẽ khóa giao diện người dùng cho đến khi hoàn tất. Trong khi Trình xử lý chạy từ các luồng không phải giao diện người dùng sẽ khắc phục bất kỳ vấn đề nào về khóa giao diện người dùng. Họ mất nhiều công sức để thiết lập và bạn vẫn phải tranh luận cách cập nhật giao diện người dùng một cách an toàn để đáp ứng với công việc nền của bạn (nghĩa là bạn vẫn phải đăng một lần chạy khác trở lại Giao diện người dùng nếu bạn muốn cập nhật giao diện người dùng).

Chủ đề thô sẽ không khóa giao diện người dùng vì chúng đang chạy độc lập với chuỗi giao diện người dùng, nhưng bạn không thể chạm vào thành phần giao diện người dùng trên chúng. Điều đó có nghĩa là bạn sẽ phải thực thi bất kỳ mã nào bạn muốn cập nhật giao diện người dùng bằng lại trên chuỗi giao diện người dùng có nghĩa là cần phải viết nhiều mã hơn để có được chuỗi giao diện người dùng để chạy nó. Điều này có thể rất phức tạp. Nguyên chủ đề thực sự nên tránh vì sự phức tạp trong việc sử dụng chúng.

Ví dụ phổ biến nhất về tác vụ nền đang chờ phản hồi từ máy chủ. Hầu hết các thư viện chặn cho đến khi máy chủ gửi phản hồi có nghĩa là bạn không thể gọi chúng trên chuỗi giao diện người dùng hoặc người dùng khác của bạn sẽ bị chặn thực hiện bất kỳ điều gì cho đến khi máy chủ trả về cuộc gọi. Không chỉ họ sẽ bị chặn, nhưng giao diện người dùng không thể cập nhật chính nó để hiển thị một spinner hoặc nếu không nhìn sống. Điều này là tốt nhất để đẩy ra một chủ đề nền. Về mặt kỹ thuật, các Trình xử lý và Chủ đề có thể thực hiện điều này, nhưng các Trình xử lý phải được xây dựng đặc biệt để chúng sẽ sử dụng một luồng nền thực sự.

Đây là nơi AsyncTask vượt qua Trình xử lý bởi vì nó thực hiện cả các công việc nền thực và cập nhật giao diện người dùng. Nó có một phần để thực hiện một số hoạt động chạy dài trong nền và nó có một phần để cập nhật giao diện người dùng từ chuỗi giao diện người dùng khi hoàn thành nó. Nó thậm chí còn có một phần tiến trình tùy chọn để bạn có thể cung cấp bất kỳ tiến trình trung gian nào cho giao diện người dùng trong khi nhiệm vụ đang chạy. Nhược điểm của một AsyncTask là chúng phải có kết thúc. Các công việc nền tiếp tục chạy để định kỳ kiểm tra xem có điều gì đó đã xảy ra không, ngủ và kiểm tra một số thứ khác không có lợi cho mô hình AsyncTask. Tuy nhiên, đó không phải là để nói rằng bạn không thể sử dụng một Handler để định kỳ khởi động AsyncTask, nhưng chỉ cho sự hoàn chỉnh của cuộc thảo luận tôi đề cập đến.

Cuối cùng, sử dụng Chủ đề thô không phải là tất cả dễ dàng hoặc thậm chí "tốt hơn" bởi vì Trình xử lý có thể thực hiện khá nhiều thứ mà Chủ đề có thể làm với ít mã hơn. Tuy nhiên, xử lý là khó khăn trong việc xác định thread Runnable được thực hiện trên. Thông thường, đó là chuỗi giao diện người dùng và kỹ thuật thiết lập nó để sử dụng một chuỗi không phải UI là khó khăn. Cả hai tùy chọn đều gặp phải sự cố cập nhật giao diện người dùng trong đó bạn phải thực hiện thêm công việc để chạy các công việc giao diện người dùng ở cuối công việc nền thực. AsyncTask thực sự là phương pháp ưa thích của tôi để thực hiện các công việc nền.

-2

Nếu bạn đang làm việc với chủ đề, tôi đề nghị bạn nên sử dụng một bộ xử lý cùng với nó:

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

Bằng cách đó bạn có thể trì hoãn việc thực hiện cho tới chừng nào bạn muốn, hoặc bạn có thể sử dụng postThread.sleep(delay), tôi thích những ngày này.

3

Nó không phải là HandlerThreads. Chúng khá khác nhau:

Chủ đề: Là lớp Java cũ triển khai thread of execution. Như các phần khác của API Java, chúng cũng có sẵn trên Android. Chú ý hơn trong các phiên bản muộn của ngôn ngữ Java, chúng được superseeded bởi Executors framework, do đó, thực hành được khuyến nghị là sử dụng Executor/Runnable, nhưng do nó đơn giản Chủ đề vẫn được sử dụng đôi khi.

Handler: lớp này chỉ có sẵn trong Android, và nó chủ yếu là một cơ chế để giao tiếp với Thread hiện có.Bạn gửi các tin nhắn chủ đề mục tiêu hoặc Runnables, và bạn cũng có thể lên lịch cho giao tiếp này.

Bạn thường cần một số Handler khi bạn cần gửi thứ gì đó cho một chuỗi. Ví dụ, "thứ gì đó" này có thể được đóng gói để xử lý hoặc Runnable được thực thi trong chuỗi đó. Mọi trình xử lý thường được liên kết với luồng hiện tại tại thời điểm diễn giải, trừ khi bạn sử dụng một hàm tạo khác lạ hơn. Một trường hợp sử dụng điển hình là lên lịch một nhiệm vụ lặp đi lặp lại trong chuỗi chính (đó là chuỗi giao diện người dùng). Lưu ý rằng để lên lịch cho tác vụ một lần, có một cách dễ nhất: Activity.runOnUithread.

Bây giờ cho một tác vụ nền cần chạy trong một luồng khác với chủ đề chính: trong cả hai phương pháp, bạn sẽ có một luồng chạy, nhưng việc tạo trình xử lý có nghĩa là Android sẽ bắt đầu Hàng đợi tin nhắn mới cho chuỗi đó , đó là một cái gì đó thường xuyên đề không cần phải có, và vì điều này sẽ có một số chi phí. Vì vậy, nếu bạn cần phải bắt đầu một thread có thể chạy bị cô lập mà không nhận được thông tin, tôi muốn nói rằng Thread đơn giản được ưa thích. Nhưng nếu bạn cần một hàng đợi thực hiện để lên lịch Runnables, bạn có thể chọn giữa một Timer, một Executor, một thread "handle", hoặc thậm chí là AlarmManager. Ưu điểm của Trình xử lý là chúng có thể được gắn vào bất kỳ chuỗi đã tồn tại nào trong ứng dụng của bạn, trong khi Bộ hẹn giờ và Công cụ sẽ khởi chạy nội bộ một chuỗi chuyên dụng mới khi chúng được thiết lập.

Các vấn đề liên quan