2013-08-18 58 views
44

Tôi có câu hỏi liên quan đến tình huống xảy ra thường xuyên đơn giản này trong Android.Mẫu WeakReference/AsyncTask trong android

Chúng tôi có một hoạt động chính, chúng tôi gọi AsyncTask cùng với tham chiếu của tính chính, để AsyncTask có thể cập nhật các chế độ xem trên MainActivity.

tôi sẽ phá vỡ sự kiện này thành những bước

  • MainActivity tạo ra một AyncTask, qua tham chiếu của nó với nó.
  • AysncTask, bắt đầu công việc, tải xuống mười tệp ví dụ
  • Người dùng đã thay đổi hướng của thiết bị. Điều này dẫn đến một con trỏ mồ côi trong AsyncTask
  • Khi AsyncTask hoàn tất và cố gắng truy cập hoạt động để cập nhật trạng thái, nó bị treo do con trỏ rỗng.

Giải pháp cho ở trên là để giữ một WeakReference trong AsyncTask theo khuyến cáo của cuốn sách "Pro Android 4"

WeakReference<Activity> weakActivity; 

in method onPostExecute 

Activity activity = weakActivity.get(); 
if (activity != null) { 
    // do your stuff with activity here 
} 

Làm thế nào để giải quyết tình hình này?

Câu hỏi của tôi là, nếu asynctask của tôi đang tải xuống mười tệp và sau khi hoàn thành 5 hoạt động được khởi động lại (do thay đổi định hướng) thì FileDownloadingTask của tôi sẽ được gọi lại một lần nữa?

Điều gì sẽ xảy ra với AsyncTask trước đó ban đầu được gọi?

Cảm ơn bạn và tôi xin lỗi về độ dài của câu hỏi.

+5

Cảm ơn bạn đã đăng câu hỏi có hình dạng và được diễn đạt tốt. – Travis

Trả lời

32

Điều này giải quyết tình huống như thế nào?

Cho phép Activity để thu gom rác, vì vậy bạn không bị rò rỉ bộ nhớ.

Tham chiếu không có nghĩa là AsyncTaskkhông thể một cách mù quáng cố gắng cập nhật giao diện người dùng không còn được đính kèm nữa, điều này sẽ ném ngoại lệ (ví dụ: chế độ xem không được đính kèm với trình quản lý cửa sổ). Tất nhiên bạn phải kiểm tra null để tránh NPE.

nếu asynctask của tôi đang tải xuống mười tệp và sau khi hoàn thành 5 hoạt động được khởi động lại (do thay đổi định hướng) thì FileDownloadingTask của tôi có được gọi lại không?

Tùy thuộc vào triển khai của bạn, nhưng có thể có - nếu bạn không cố tình làm điều gì đó để tải xuống lặp lại không cần thiết, chẳng hạn như lưu vào bộ nhớ cache kết quả ở đâu đó.

Điều gì sẽ xảy ra với AsyncTask trước đó đã được gọi?

Trong các phiên bản trước của Android, nó sẽ chạy để hoàn thành, tải xuống tất cả các tệp chỉ để vứt chúng đi (hoặc có thể lưu vào bộ nhớ cache, tùy thuộc vào triển khai của bạn).

Trong Android mới hơn, tôi nghi ngờ rằng AsyncTask đang bị giết cùng với Activity đã bắt đầu, nhưng cơ sở nghi ngờ của tôi chỉ là bản demo rò rỉ bộ nhớ cho RoboSpice (xem bên dưới) không thực sự bị rò rỉ trên Thiết bị Jellybean.

Nếu tôi có thể đưa ra một số lời khuyên: AsyncTask không phù hợp để thực hiện các tác vụ có khả năng chạy lâu dài như kết nối mạng.

IntentService là cách tiếp cận tốt hơn (và vẫn tương đối đơn giản), nếu một chuỗi công nhân duy nhất có thể chấp nhận được với bạn. Sử dụng một (địa phương) Service nếu bạn muốn kiểm soát các thread-pool - và cẩn thận không để làm việc trên các chủ đề chính!

RoboSpice có vẻ tốt nếu bạn đang tìm cách thực hiện đáng tin cậy mạng trong nền (tuyên bố từ chối trách nhiệm: Tôi chưa thử; tôi không liên kết). Có một số RoboSpice Motivations demo app trong cửa hàng trò chơi giải thích lý do tại sao bạn nên sử dụng nó bằng cách giới thiệu tất cả những điều có thể sai với AsyncTask - bao gồm giải pháp WeakReference.

cũng Xem chủ đề này: Is AsyncTask really conceptually flawed or am I just missing something?

Cập nhật:

Tôi tạo ra một github project với một ví dụ về tải sử dụng IntentService cho một câu hỏi SO (How to fix android.os.NetworkOnMainThreadException?), nhưng nó cũng là liên quan ở đây, tôi nghĩ. Nó có lợi thế thêm rằng, bằng cách trả lại kết quả qua onActivityResult, một bản tải xuống đang bay khi bạn xoay thiết bị sẽ phân phối tới số Activity được khởi động lại.

+2

Cập nhật nhỏ: Lúc này RxJava là một cách tuyệt vời để chạy các tác vụ trong nền, tắt chuỗi chính và có kết quả của bạn được phân phối trên luồng chính (để bạn có thể cập nhật UI vv). Một tùy chọn khác sẽ sử dụng EventBus, – AgentKnopf

6

Lớp WeakReference về cơ bản chỉ ngăn JRE tăng bộ đếm tham chiếu cho phiên bản đã cho.

Tôi sẽ không đi vào quản lý bộ nhớ của Java và trả lời câu hỏi của bạn trực tiếp: WeakReference giải quyết tình huống bằng cách cung cấp AsyncTask một cách để tìm hiểu xem hoạt động gốc của nó vẫn hợp lệ.

Thay đổi hướng chính sẽ không tự động khởi động lại AsyncTask. Bạn phải mã hành vi mong muốn bằng các cơ chế đã biết (onCreate/onDestroy, onSave/RestoreInstanceState).

Liên quan đến gốc AsyncTask, tôi không chắc chắn 100% đó các tùy chọn này sẽ xảy ra:

  • Hoặc Java dừng thread và disposes các AsyncTask, vì đối tượng chỉ giữ một tham chiếu đến nó (gốc Activity) bị hủy
  • Hoặc một số đối tượng Java nội duy trì một tham chiếu đến đối tượng AsyncTask, chặn thu gom rác thải của mình, để lại hiệu quả AsyncTask để kết thúc trong nền

Dù bằng cách nào, thực hành tốt là hủy bỏ/tạm dừng và khởi động lại/tiếp tục AsyncTask theo cách thủ công (hoặc chuyển nó sang Activity mới) hoặc sử dụng Service thay thế.

+0

WeakReference không hữu ích khi bạn sử dụng lớp bên trong AsyncTask của Activity – Qamar

1

Làm cách nào để giải quyết tình huống này?

Không.

Tham chiếu của WeakReference được đặt thành null khi bộ thu gom rác xác định rằng tham chiếu có thể truy cập yếu. Điều này không xảy ra khi một hoạt động bị tạm dừng và không nhất thiết xảy ra ngay lập tức khi hoạt động bị phá hủy và khung hủy tất cả các tham chiếu đến nó. Nếu GC không chạy, hoàn toàn có thể cho AsyncTask hoàn thành trong khi WeakReference vẫn chứa tham chiếu đến hoạt động đã chết.

Không chỉ vậy, nhưng cách tiếp cận này không có gì để ngăn chặn AsyncTask từ CPU tiêu thụ vô dụng.

Cách tiếp cận tốt hơn là giữ Activity duy trì tham chiếu mạnh mẽ đến AsyncTaskcancel(...) trong phương pháp vòng đời giọt nước thích hợp. Các AsyncTask nên theo dõi isCancelled() và ngừng làm việc nếu nó không còn cần thiết.

Nếu bạn muốn AsyncTask để tồn tại qua các thay đổi cấu hình (nhưng không các hình thức hủy hoạt động khác), bạn có thể lưu trữ nó trong một đoạn được giữ lại.