Android AsyncTask cho các hoạt động chạy lâu dài


90

Trích dẫn tài liệu cho AsyncTask được tìm thấy ở đây , nó cho biết:

Lý tưởng nhất là AsyncTasks nên được sử dụng cho các hoạt động ngắn (tối đa là vài giây.) Nếu bạn cần duy trì các luồng chạy trong thời gian dài, bạn nên sử dụng các API khác nhau được cung cấp bởi java.util.concurrent pacakge chẳng hạn như Executor, ThreadPoolExecutor và FutureTask.

Bây giờ câu hỏi của tôi đặt ra: tại sao? Các doInBackground()chức năng chạy ra khỏi thread UI vì vậy những gì hại là có bằng việc có một hoạt động dài chạy đây?


2
Vấn đề tôi gặp phải khi triển khai ứng dụng (sử dụng asyncTask) cho một thiết bị thực tế là doInBackgroundchức năng hoạt động lâu dài sẽ đóng băng màn hình nếu thanh tiến trình không được sử dụng.
venkatKA

2
Vì một AsyncTask được liên kết với Hoạt động mà nó được khởi chạy. Vì vậy, nếu Hoạt động bị hủy, phiên bản AsyncTask của bạn cũng có thể bị hủy.
IgorGanapolsky

Điều gì sẽ xảy ra nếu tôi tạo AsyncTask trong một Dịch vụ? Điều đó sẽ không giải quyết được vấn đề sao?
xoáy

1
Sử dụng IntentService, Giải pháp hoàn hảo để chạy lâu trong nền.
Keyur Thumar

Câu trả lời:


120

Đây là một câu hỏi rất hay, cần có thời gian là một Lập trình viên Android để hiểu hết vấn đề. Thật vậy AsyncTask có hai vấn đề chính có liên quan:

  • Chúng liên kết kém với vòng đời hoạt động
  • Chúng tạo ra các rò rỉ bộ nhớ rất dễ dàng.

Bên trong ứng dụng RoboSpice Motivations ( có sẵn trên Google Play ), chúng tôi trả lời câu hỏi đó một cách chi tiết. Nó sẽ cung cấp cho bạn một cái nhìn chuyên sâu về AsyncTasks, Loaders, các tính năng và nhược điểm của chúng và cũng giới thiệu cho bạn một giải pháp thay thế cho các yêu cầu mạng: RoboSpice. Yêu cầu mạng là một yêu cầu phổ biến trong Android và về bản chất là các hoạt động chạy lâu dài. Đây là một đoạn trích từ ứng dụng:

Vòng đời của AsyncTask và Activity

AsyncTasks không tuân theo vòng đời của phiên bản Activity. Nếu bạn khởi động AsyncTask bên trong Hoạt động và bạn xoay thiết bị, Hoạt động sẽ bị hủy và một phiên bản mới sẽ được tạo. Nhưng AsyncTask sẽ không chết. Nó sẽ tiếp tục sống cho đến khi hoàn thành.

Và khi hoàn tất, AsyncTask sẽ không cập nhật giao diện người dùng của Hoạt động mới. Thật vậy, nó cập nhật phiên bản cũ của hoạt động không được hiển thị nữa. Điều này có thể dẫn đến Ngoại lệ của kiểu java.lang.

Sự cố rò rỉ bộ nhớ

Rất thuận tiện để tạo AsyncTasks làm lớp bên trong Hoạt động của bạn. Vì AsyncTask sẽ cần thao tác các khung nhìn của Activity khi nhiệm vụ đã hoàn thành hoặc đang thực hiện, việc sử dụng lớp bên trong của Activity có vẻ thuận tiện: các lớp bên trong có thể truy cập trực tiếp vào bất kỳ trường nào của lớp bên ngoài.

Tuy nhiên, nó có nghĩa là lớp bên trong sẽ giữ một tham chiếu vô hình trên cá thể lớp bên ngoài của nó: Activity.

Về lâu dài, điều này dẫn đến rò rỉ bộ nhớ: nếu AsyncTask tồn tại lâu, nó sẽ giữ cho hoạt động "tồn tại" trong khi Android muốn loại bỏ nó vì nó không thể hiển thị được nữa. Hoạt động không thể được thu thập rác và đó là cơ chế trung tâm để Android bảo tồn tài nguyên trên thiết bị.


Thực sự là một ý tưởng rất tồi khi sử dụng AsyncTasks cho các hoạt động chạy dài. Tuy nhiên, chúng phù hợp với những hoạt động ngắn hạn như cập nhật Chế độ xem sau 1 hoặc 2 giây.

Tôi khuyến khích bạn tải xuống ứng dụng RoboSpice Motivations , ứng dụng này thực sự giải thích chuyên sâu về vấn đề này và cung cấp các mẫu và minh họa về các cách khác nhau để thực hiện một số hoạt động nền.


@Snicolas Chào bạn. Tôi có một ứng dụng quét dữ liệu từ thẻ NFC và gửi đến máy chủ. Nó hoạt động tốt trong các khu vực tín hiệu tốt nhưng không có tín hiệu AsyncTask làm cho cuộc gọi web tiếp tục chạy. Ví dụ: hộp thoại tiến trình chạy trong vài phút và sau đó khi nó biến mất, màn hình chuyển sang màu đen và không phản hồi. AsyncTask của tôi là một lớp bên trong. Tôi đang viết Trình xử lý để hủy nhiệm vụ sau X giây. Ứng dụng dường như gửi dữ liệu cũ đến máy chủ vài giờ sau khi quét. Điều này có thể là do AsyncTask không hoàn thành và sau đó có thể hoàn thành vài giờ sau? Tôi sẽ đánh giá cao bất kỳ cái nhìn sâu sắc nào. cảm ơn
rùa cao

Theo dõi để xem điều gì sẽ xảy ra. Nhưng có, điều đó hoàn toàn có thể xảy ra! Nếu bạn desin cũng AsyncTask, bạn có thể hủy bỏ nó khá đúng, đó sẽ là một tốt điểm bắt đầu nếu bạn không muốn chuyển sang RS hoặc một dịch vụ ...
Snicolas

@Snicolas Cảm ơn bạn đã trả lời. Tôi đã thực hiện một bài đăng trên SO ngày hôm qua phác thảo vấn đề của mình và hiển thị mã xử lý mà tôi đã viết để thử dừng AsyncTask sau 8 giây. Bạn có phiền hãy xem qua, nếu bạn có thời gian? Việc gọi AsyncTask.cancel (true) từ một Trình xử lý có hủy tác vụ đúng cách không? Tôi biết mình nên kiểm tra định kỳ giá trị của iscancell () trong doInBackgroud của mình, nhưng tôi nghĩ không áp dụng cho trường hợp của mình vì tôi chỉ tạo một cuộc gọi web một dòng HttpPost và không xuất bản các bản cập nhật trên giao diện người dùng. ví dụ: liệu có thể tạo HttPost từ IntentService không
cowboy


Bạn nên dùng thử RoboSpice (trên github). ;)
Snicolas

38

tại sao ?

Bởi vì AsyncTask, theo mặc định, sử dụng một nhóm chủ đề mà bạn không tạo . Không bao giờ ràng buộc tài nguyên từ một nhóm mà bạn không tạo, vì bạn không biết yêu cầu của nhóm đó là gì. Và đừng bao giờ ràng buộc tài nguyên từ một nhóm mà bạn không tạo nếu tài liệu cho nhóm đó yêu cầu bạn không làm như vậy, như trường hợp ở đây.

Đặc biệt, bắt đầu với Android 3.2, nhóm chuỗi được sử dụng theo AsyncTaskmặc định (đối với các ứng dụng android:targetSdkVersionđược đặt thành 13 trở lên) chỉ có một chuỗi trong đó - nếu bạn buộc chuỗi này vô thời hạn, thì không tác vụ nào khác của bạn sẽ chạy.


Cảm ơn vì lời giải thích này .. Tôi không thể tìm thấy bất kỳ điều gì thực sự có lỗi về việc sử dụng AsyncTasks cho các hoạt động chạy lâu (mặc dù tôi thường tự ủy quyền chúng cho Dịch vụ), nhưng lập luận của bạn về việc kết hợp ThreadPool đó (mà bạn thực sự có thể không có giả định nào về kích thước của nó) dường như tại chỗ. Như một phần bổ sung cho bài đăng của Anup về việc sử dụng một dịch vụ: Bản thân dịch vụ đó cũng phải chạy tác vụ trên một luồng nền và không chặn luồng chính của chính nó. Một tùy chọn có thể là IntentService, hoặc đối với các yêu cầu phức tạp hơn về đồng thời, hãy áp dụng chiến lược đa luồng của riêng bạn.
baske

Nó có giống nhau ngay cả khi tôi khởi động AsyncTask bên trong một Dịch vụ không ?? Ý tôi là, hoạt động lâu dài vẫn là một vấn đề?
xoáy

1
@eddy: Có, vì điều đó không thay đổi bản chất của nhóm chủ đề. Đối với a Service, chỉ cần sử dụng a Threadhoặc a ThreadPoolExecutor.
CommonsWare

Cảm ơn bạn @CommonsChỉ là câu hỏi cuối cùng, TimerTasks có cùng nhược điểm của AsyncTasks không? hoặc là một điều hoàn toàn khác?
xoáy

1
@eddy: TimerTasklà từ Java tiêu chuẩn, không phải Android. TimerTaskphần lớn đã bị loại bỏ để ủng hộ ScheduledExecutorService(mặc dù tên của nó, là một phần của Java tiêu chuẩn). Cả hai đều không bị ràng buộc với Android và vì vậy bạn vẫn cần một dịch vụ nếu bạn muốn những thứ đó chạy trong nền. Và, bạn thực sự nên xem xét AlarmManager, từ Android, vì vậy bạn không cần một dịch vụ quanh quẩn chỉ xem đồng hồ tích tắc.
CommonsWare

4

Tác vụ Aysnc là các chuỗi chuyên biệt vẫn được sử dụng với GUI ứng dụng của bạn nhưng vẫn giữ các tác vụ nặng về tài nguyên của chuỗi giao diện người dùng. Vì vậy, khi những thứ như cập nhật danh sách, thay đổi chế độ xem của bạn, v.v. yêu cầu bạn thực hiện một số thao tác tìm nạp hoặc thao tác cập nhật, bạn nên sử dụng các tác vụ không đồng bộ để có thể giữ các thao tác này ngoài chuỗi giao diện người dùng nhưng lưu ý rằng các thao tác này vẫn được kết nối với giao diện người dùng bằng cách nào đó .

Đối với các tác vụ chạy lâu hơn, không yêu cầu cập nhật giao diện người dùng, bạn có thể sử dụng các dịch vụ thay thế vì chúng có thể hoạt động ngay cả khi không có giao diện người dùng.

Vì vậy, đối với các tác vụ ngắn, hãy sử dụng các tác vụ không đồng bộ vì chúng có thể bị giết bởi Hệ điều hành sau khi hoạt động sinh sản của bạn chết (thường sẽ không chết khi đang hoạt động nhưng sẽ hoàn thành nhiệm vụ của nó). Và đối với các tác vụ dài và lặp đi lặp lại, hãy sử dụng các dịch vụ thay thế.

để biết thêm thông tin, Xem chủ đề:

AsyncTask lâu hơn một vài giây?

AsyncTask sẽ không dừng ngay cả khi hoạt động đã bị phá hủy


1

Vấn đề với AsyncTask là nếu nó được định nghĩa là lớp bên trong không tĩnh của hoạt động, nó sẽ có một tham chiếu đến hoạt động. Trong trường hợp hoạt động mà vùng chứa tác vụ không đồng bộ kết thúc, nhưng công việc nền trong AsyncTask vẫn tiếp tục, đối tượng hoạt động sẽ không được thu gom rác vì có tham chiếu đến nó, điều này gây ra rò rỉ bộ nhớ.

Giải pháp để khắc phục điều này là xác định tác vụ không đồng bộ là lớp hoạt động tĩnh bên trong và sử dụng tham chiếu yếu đến ngữ cảnh.

Tuy nhiên, bạn nên sử dụng nó cho các tác vụ nền đơn giản và nhanh chóng. Để phát triển ứng dụng với mã sạch, tốt hơn là sử dụng RxJava để chạy các tác vụ nền phức tạp và cập nhật giao diện người dùng với kết quả từ nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.