2012-05-13 49 views
48

Trong dự án của tôi, tôi đang sử dụng BroadcastReceiver s làm cuộc gọi lại từ chuỗi đang chạy dài (ví dụ: thông báo cho hoạt động tải xuống đã hoàn tất và gửi một số dữ liệu phản hồi từ Công nhân Thread để hoạt động có thể hiển thị thông báo thích hợp cho người dùng ..). Để sử dụng BroadcastReceiver s, tôi phải cẩn thận đăng ký và hủy đăng ký người nhận phát mỗi khi tôi sử dụng và cũng phải quan tâm đến những thông điệp nào cần gửi khi tôi sử dụng phương pháp này để thực hiện các hành động khác nhau (như tải xuống, tạo WebService) cuộc gọi, v.v.). Và cũng để gửi các đối tượng tùy chỉnh thông qua mục đích của Broadcast tôi cũng cần phải làm cho các đối tượng Parcelable.Android BroadcastReceiver hoặc phương thức gọi lại đơn giản?

Không giống như cách tiếp cận này, tôi cũng đã thấy cách tiếp cận phương thức gọi lại dường như đơn giản hơn phương pháp tôi sử dụng. Các phương thức gọi lại là thực hiện các phương thức Giao diện đơn giản có thể được sử dụng để đạt được hiệu ứng tương tự như tin nhắn ứng dụng của BroadcastRecaiver. Cách tiếp cận này không cần thực thi Parcelable để trả về các đối tượng phức tạp và nó không sử dụng các phím như BroadcastReceiver .. Tôi nghĩ rằng phần xấu là tôi cần kiểm tra đối tượng gọi lại cho giá trị null trước khi tôi muốn gọi phương thức gọi lại. Và cũng để đảm bảo rằng tôi đang chạy mã từ việc triển khai trên chuỗi giao diện người dùng để tôi có thể cập nhật giao diện người dùng mà không có lỗi.

Ok, tôi hy vọng bạn hiểu ý tôi muốn nói :).

Bây giờ câu hỏi là bạn có nghĩ rằng phương pháp gọi lại là tốt hơn (nhẹ hơn, sạch hơn, nhanh hơn ..) so với phương pháp BroadcastReceiver khi được sử dụng ngay bên trong một ứng dụng đơn lẻ? (Lưu ý rằng tôi không sử dụng Android Service để làm việc nền .. chỉ cần AsyncTaskThread s)

Cảm ơn bạn!

Trả lời

78

Đây là một câu hỏi rất thú vị và tôi chạy vào cùng một vấn đề. Theo tôi, cả hai cơ chế có thể được sử dụng hoàn toàn và cách tiếp cận phù hợp để sử dụng phụ thuộc vào trường hợp sử dụng của bạn. Dưới đây là một số điểm cần xem xét trước khi quyết định.

Sử dụng callback-cơ chế có một số lợi ích, nhưng cũng có những hạn chế:

PRO

  • Đó là về phía trước đơn giản và thẳng để thực hiện.
  • Bạn nhận được loại an toàn giữa các thành phần tương tác với nhau.
  • Bạn có thể trả về các đối tượng tùy ý.
  • Nó đơn giản hóa việc thử nghiệm vì bạn chỉ phải tiêm một cuộc gọi lại mô hình (ví dụ: được tạo thông qua mockito hoặc một cái gì đó tương tự) trong các bài kiểm tra đơn vị.

CHỐNG

  • Bạn phải chuyển sang các chủ đề chính để làm thao tác giao diện người dùng.
  • Bạn chỉ có thể có mối quan hệ 1-1. Mối quan hệ 1 đến n (mẫu quan sát) không thể thực hiện được nếu không có thêm công việc. Trong trường hợp này, tôi thích cơ chế Observer/Observable của Android.
  • Như bạn đã nói, bạn luôn phải kiểm tra null trước khi gọi hàm gọi lại nếu cuộc gọi lại có thể là tùy chọn.
  • Nếu thành phần của bạn nên cung cấp một loại API dịch vụ với các chức năng dịch vụ khác nhau và bạn không muốn có giao diện gọi lại chỉ với một vài chức năng gọi lại chung, bạn phải quyết định xem có cung cấp giao diện gọi lại đặc biệt cho từng chức năng dịch vụ không. hoặc liệu bạn có cung cấp một giao diện gọi lại duy nhất với nhiều chức năng gọi lại hay không. Trong trường hợp sau, tất cả các máy khách gọi lại được sử dụng cho các cuộc gọi dịch vụ tới API của bạn phải thực hiện giao diện gọi lại đầy đủ mặc dù phần lớn các đối tượng phương thức sẽ trống. Bạn có thể làm việc xung quanh điều này bằng cách thực hiện một sơ khai với các thân trống và làm cho trình khách gọi lại của bạn kế thừa từ nhánh đó, nhưng điều này là không thể nếu đã kế thừa từ một lớp cơ sở khác. Có lẽ bạn có thể sử dụng một số loại proxy gọi lại động (xem http://developer.android.com/reference/java/lang/reflect/Proxy.html), nhưng sau đó nó trở nên thực sự phức tạp và tôi sẽ nghĩ đến việc sử dụng một cơ chế khác.
  • Máy khách cho các cuộc gọi lại phải được truyền qua các phương thức/thành phần khác nhau nếu người gọi không thể truy cập trực tiếp vào dịch vụ.

Một số điểm về BroadcastReceiver -approach:

PRO

  • Bạn đạt được một khớp nối lỏng lẻo giữa các thành phần của bạn.
  • Bạn có thể có mối quan hệ 1 đến n (bao gồm 1 đến 0).
  • Phương pháp onReceive() luôn được thực hiện trên chuỗi chính.
  • Bạn có thể thông báo cho các thành phần trong toàn bộ ứng dụng của mình, vì vậy các thành phần giao tiếp không phải "nhìn thấy" từng phần.

CHỐNG

  • Đây là một cách tiếp cận rất chung chung, do marshalling và unmarshalling dữ liệu vận chuyển bằng các Intent là một nguồn lỗi bổ sung.
  • Bạn phải thực hiện các hành động của Intent độc đáo (ví dụ: bằng cách thêm tên gói) nếu bạn muốn loại bỏ mối tương quan với các ứng dụng khác, vì mục đích ban đầu của chúng là thực hiện phát sóng giữa các ứng dụng.
  • Bạn phải quản lý số BroadcastReceiver đăng ký và hủy đăng ký. Nếu bạn muốn thực hiện điều này một cách thoải mái hơn, bạn có thể triển khai chú thích tùy chỉnh để chú thích Hoạt động của mình với các hành động cần được đăng ký và triển khai lớp cơ sở Activity đăng ký và hủy đăng ký với IntentFilter s trong respit onResume(). onPause() phương pháp.
  • Như bạn đã nói, dữ liệu được gửi với Intent phải triển khai giao diện Parcelable, nhưng hơn nữa có giới hạn kích thước nghiêm ngặt và sẽ gây ra vấn đề về hiệu suất nếu bạn vận chuyển một lượng lớn dữ liệu bằng Intent. Xem http://code.google.com/p/android/issues/detail?id=5878 để thảo luận về điều đó. Vì vậy, nếu bạn muốn gửi hình ảnh ví dụ bạn phải lưu trữ chúng tạm thời trong một kho lưu trữ và gửi một ID hoặc URL tương ứng để truy cập hình ảnh từ người nhận của bạn Intent xóa nó khỏi kho lưu trữ sau khi sử dụng. Điều này dẫn đến các vấn đề tiếp theo nếu có nhiều người nhận (khi nào hình ảnh sẽ bị xóa khỏi kho lưu trữ và ai nên làm điều đó?).
  • Nếu bạn lạm dụng loại cơ chế thông báo này, luồng kiểm soát của ứng dụng có thể bị ẩn và khi gỡ lỗi, bạn sẽ vẽ đồ thị theo trình tự Intent s để hiểu điều gì đã kích hoạt lỗi cụ thể hoặc tại sao chuỗi thông báo này bị hỏng điểm.

Theo ý kiến ​​của tôi, ngay cả ứng dụng dành cho thiết bị di động cũng phải có kiến ​​trúc dựa trên ít nhất 2 lớp: lớp giao diện người dùng và lớp lõi (với logic nghiệp vụ, v.v.). Nói chung, các tác vụ chạy dài được thực hiện theo một chủ đề riêng (có thể thông qua AsyncTask hoặc HandlerThread nếu sử dụng MessageQueue s) bên trong lớp lõi và giao diện người dùng sẽ được cập nhật khi tác vụ này đã được hoàn thành. Nói chung với callbacks bạn đạt được một khớp nối chặt chẽ giữa các thành phần của bạn, vì vậy tôi thích sử dụng cách tiếp cận này chỉ trong một lớp và không cho giao tiếp trên các ranh giới lớp. Đối với việc phát sóng tin nhắn giữa lớp UI và lớp lõi, tôi sẽ sử dụng BroadcastReceiver -ứng dụng cho phép bạn tách lớp UI của mình khỏi lớp logic.

+0

Tôi đã trao giải thưởng này như một câu trả lời và cho nó tiền thưởng vì đó là câu trả lời hoàn chỉnh nhất .. Cảm ơn tất cả vì những suy nghĩ của bạn! – Cata

+0

Câu trả lời hay. Tôi là một fan hâm mộ của mẫu gọi lại/giao diện nhưng tôi thường kế thừa các dự án với việc sử dụng rộng rãi mô hình mục đích phát sóng và tôi thường tự hỏi liệu tôi có đang đi sai hướng về nó hay không. Thật tuyệt khi biết rằng đó thực sự là vấn đề về sở thích thiết kế hơn là thực hành tốt nhất. Tôi thích loại an toàn và mã rõ ràng được cung cấp bởi giao diện và có lẽ tôi sẽ gắn bó với chúng bây giờ –

+0

ý kiến ​​rất thú vị và chi tiết +1 – Jorgesys

6

Tôi không thấy những gì bạn đạt được bằng cách sử dụng BroadcastReceiver trong trường hợp của bạn. Callbacks hoặc, tốt hơn có lẽ, Handlers sẽ là cách để làm điều đó. BroadcastReceiver là điều tốt khi bạn không biết ai là người đăng ký.

+0

Cảm ơn câu trả lời của bạn, mặc dù tôi sẽ đợi một chút để có thêm câu trả lời .. – Cata

2

Trình phát quảng cáo nên được sử dụng Nếu bạn cần gửi chương trình phát sóng trên các ứng dụng trong khi Callbacks (hoặc Trình xử lý theo đề xuất của Alex) thì tốt hơn để sử dụng trong trường hợp của bạn.

Nếu bạn muốn sử dụng khác hơn hai, hãy cân nhắc sử dụng Observer (Giao diện được bao gồm trong android) và ủy quyền.

Đối với đại biểu, vui lòng xem xét this SO post.

3

tôi sẽ chỉ cần thêm một tùy chọn cho câu trả lời tuyệt vời khác mà bạn đã nhận đã ...

Bạn không cần phải tạo ra một máy thu phát sóng để nhận Intents. Trong file manifest android của bạn, bạn có thể đăng ký bất kỳ hoạt động để nhận được ý đồ:

<activity android:name=".MyActivity"> 
     <intent-filter > 
       <action android:name="intent.you.want.to.receive" /> 
       <category android:name="android.intent.category.DEFAULT" /> 
     </intent-filter>  
.... 
</activity> 

Sau đó ghi đè lên các phương pháp onNewIntent(Intent) trong hoạt động của bạn để nhận được nó.

Để gửi Mục đích, hãy sử dụng phương thức Context.startActivity(Intent). Nhiều khả năng bạn sẽ muốn thêm cờ FLAG_ACTIVITY_SINGLE_TOP vào Mục đích của bạn để nó không tạo ra một phiên bản mới của hoạt động của bạn nếu một hoạt động đang chạy.

EDIT: Tôi vừa nhận thấy bạn đang chạy trong một ứng dụng duy nhất. Do đó, một cuộc gọi lại đơn giản có lẽ là tốt nhất.Giải pháp trên không hoạt động trong một ứng dụng duy nhất, nhưng phù hợp hơn cho các ứng dụng khác nhau. Tôi sẽ để nó ở đây trong trường hợp nó giúp ai đó. Chúc may mắn!

+0

Mẹo Excel!Tuy nhiên, tôi cũng muốn thêm rằng phương thức '' tương tự cũng áp dụng cho các dịch vụ, nếu bạn muốn tách biệt các triển khai cụ thể không phải UI, trong trường hợp này bạn sẽ gọi 'Context.startService (Ý định) 'thay thế. Nếu dịch vụ đích của bạn mở rộng ['IntentService'] (http://developer.android.com/reference/android/app/IntentService.html), bạn sẽ có một" hàng đợi thông điệp không đồng bộ "miễn phí vì' IntentService' sẽ chỉ thực hiện trên một 'Intent' tại thời điểm đó và phương thức' onHandleIntent' sẽ được thực hiện trên một chuỗi công nhân. – dbm

1

không chắc chắn mục đích là gì, nhưng nếu bạn muốn giữ ý tưởng tương tự về cách sử dụng ý định và BroadcastReceiver và muốn có hiệu suất và bảo mật tốt hơn bình thườngnhận truyền, bạn có thể thử bản demo này, có sẵn trong thư viện hỗ trợ Android:

http://developer.android.com/resources/samples/Support4Demos/src/com/example/android/supportv4/content/LocalServiceBroadcaster.html

nếu không, bạn luôn có thể sử dụng AsyncTask, dịch vụ, xử lý, vv ...

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