Android tương đương với NSNotificationCenter


95

Trong quá trình chuyển một ứng dụng iPhone sang Android, tôi đang tìm cách tốt nhất để giao tiếp trong ứng dụng. Ý định dường như là con đường để đi, đây có phải là lựa chọn tốt nhất (duy nhất)? NSUserDefaults có vẻ nhẹ hơn nhiều so với Intents về cả hiệu suất và mã hóa.

Tôi cũng nên thêm Tôi có một lớp con ứng dụng cho tiểu bang, nhưng tôi cần thực hiện một hoạt động khác nhận biết một sự kiện.


3
Đối với những người mới đến chủ đề này, câu trả lời thứ hai là câu trả lời tốt nhất. Cuộn xuống ...
Stephan

Câu trả lời:


5

42
Câu trả lời của Shiki dưới đây hay hơn nhiều.
dsaff

5
@dsaff mặc dù là một câu trả lời đầy đủ hơn, nhưng câu trả lời của tôi không sai, rõ ràng là tôi không đáng bị -1. Điều có ý nghĩa là bạn +1 câu trả lời của Shiki.
Rui Peres

4
Shiki là câu trả lời tốt hơn cho câu hỏi
Ramz

4
Lưu ý rằng chỉ những câu trả lời không chính xác về mặt kỹ thuật và spam mới nên bị loại bỏ - câu trả lời này không phù hợp. +1 để bồi thường và +1 cho Shiki nữa vì đó là một câu trả lời tuyệt vời.

350

Tương đương tốt nhất mà tôi tìm thấy là LocalBroadcastManager , là một phần của Gói hỗ trợ Android .

Từ tài liệu LocalBroadcastManager:

Người trợ giúp đăng ký và gửi các chương trình phát sóng Ý định đến các đối tượng cục bộ trong quy trình của bạn. Điều này có một số lợi thế so với việc gửi các chương trình phát sóng toàn cầu với sendBroadcast (Intent):

  • Bạn biết rằng dữ liệu bạn đang phát sẽ không rời khỏi ứng dụng của bạn, vì vậy không cần phải lo lắng về việc rò rỉ dữ liệu riêng tư.
  • Các ứng dụng khác không thể gửi các chương trình phát sóng này đến ứng dụng của bạn, vì vậy bạn không cần phải lo lắng về việc có lỗ hổng bảo mật mà chúng có thể khai thác.
  • Nó hiệu quả hơn gửi một chương trình phát sóng toàn cầu thông qua hệ thống.

Khi sử dụng điều này, bạn có thể nói rằng an Intentlà một tương đương với một NSNotification. Đây là một ví dụ:

ReceiverActivity.java

Một hoạt động theo dõi thông báo cho sự kiện có tên "custom-event-name".

@Override
public void onCreate(Bundle savedInstanceState) {

  ...
  
  // Register to receive messages.
  // This is just like [[NSNotificationCenter defaultCenter] addObserver:...]
  // We are registering an observer (mMessageReceiver) to receive Intents
  // with actions named "custom-event-name".
  LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
      new IntentFilter("custom-event-name"));
}

// Our handler for received Intents. This will be called whenever an Intent
// with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Get extra data included in the Intent
    String message = intent.getStringExtra("message");
    Log.d("receiver", "Got message: " + message);
  }
};

@Override
protected void onDestroy() {
  // Unregister since the activity is about to be closed.
  // This is somewhat like [[NSNotificationCenter defaultCenter] removeObserver:name:object:] 
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
  super.onDestroy();
}

SenderActivity.java

Hoạt động thứ hai gửi / phát thông báo.

@Override
public void onCreate(Bundle savedInstanceState) {
  
  ...
  
  // Every time a button is clicked, we want to broadcast a notification.
  findViewById(R.id.button_send).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      sendMessage();
    }
  });
}

// Send an Intent with an action named "custom-event-name". The Intent sent should 
// be received by the ReceiverActivity.
private void sendMessage() {
  Log.d("sender", "Broadcasting message");
  Intent intent = new Intent("custom-event-name");
  // You can also include some extra data.
  intent.putExtra("message", "This is my message!");
  LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

Với đoạn mã trên, mỗi khi R.id.button_sendnhấp vào nút, một Ý định sẽ được phát đi và được nhận mMessageReceivervào ReceiverActivity.

Đầu ra gỡ lỗi sẽ giống như sau:

01-16 10:35:42.413: D/sender(356): Broadcasting message
01-16 10:35:42.421: D/receiver(356): Got message: This is my message! 

11
Cảm ơn bạn rất nhiều vì đã dành thời gian để viết một câu trả lời hữu ích và chi tiết.
Chris Lacy

14
Bạn có thể không nên gọi registerReceiver trong phương thức onCreate của mình vì điều này sẽ làm rò rỉ Activity của bạn và phương thức onDestroy của bạn sẽ không bao giờ được gọi. onResume có vẻ là lựa chọn tốt hơn để gọi registerReceiver và onPause để gọi unregisterReceiver.
Stephane JAIS

4
Hoàn hảo tương đương với NSNotificationCenter, nên là câu trả lời được chấp nhận!
Leon Storey

Tôi muốn chỉ ra rằng việc sử dụng thông báo toàn cầu có thể dẫn bạn đến một thiết kế lộn xộn. Hãy suy nghĩ về cách kết hợp tốt nhất giữa các thành phần của bạn trước khi chuyển sang cách dễ dàng. Đôi khi, tốt hơn là sử dụng trình nghe hoặc thứ gì đó tương tự như mẫu đại biểu của iOS, v.v.
saulobrito

Cảm ơn điều này đã làm việc cho tôi. @Shiki làm ơn bạn có nghĩ rằng bạn có thể cho tôi ý kiến ​​của bạn về câu hỏi này stackoverflow.com/questions/25598696/…
Axel

16

Đây là một cái gì đó tương tự như câu trả lời @Shiki, nhưng từ góc độ của nhà phát triển iOS và Trung tâm thông báo.

Trước tiên, hãy tạo một số loại dịch vụ NotificationCenter:

public class NotificationCenter {

 public static void addObserver(Context context, NotificationType notification, BroadcastReceiver responseHandler) {
    LocalBroadcastManager.getInstance(context).registerReceiver(responseHandler, new IntentFilter(notification.name()));
 }

 public static void removeObserver(Context context, BroadcastReceiver responseHandler) {
    LocalBroadcastManager.getInstance(context).unregisterReceiver(responseHandler);
 }

 public static void postNotification(Context context, NotificationType notification, HashMap<String, String> params) {
    Intent intent = new Intent(notification.name());
    // insert parameters if needed
    for(Map.Entry<String, String> entry : params.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        intent.putExtra(key, value);
    }
    LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
 }
}

Sau đó, bạn cũng sẽ cần một số kiểu enum để đảm bảo tránh những sai lầm khi mã hóa với chuỗi - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

Đây là cách sử dụng (thêm / bớt người quan sát) ví dụ trong các hoạt động:

public class LoginActivity extends AppCompatActivity{

    private BroadcastReceiver loginResponseReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           // do what you need to do with parameters that you sent with notification

           //here is example how to get parameter "isSuccess" that is sent with notification
           Boolean result = Boolean.valueOf(intent.getStringExtra("isSuccess"));
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        //subscribe to notifications listener in onCreate of activity
        NotificationCenter.addObserver(this, NotificationType.LoginResponse, loginResponseReceiver);
    }

    @Override
    protected void onDestroy() {
        // Don't forget to unsubscribe from notifications listener
        NotificationCenter.removeObserver(this, loginResponseReceiver);
        super.onDestroy();
    }
}

và cuối cùng đây là cách chúng tôi đăng thông báo lên NotificationCenter từ một số dịch vụ gọi lại hoặc nghỉ ngơi hoặc bất cứ điều gì:

public void loginService(final Context context, String username, String password) {
    //do some async work, or rest call etc.
    //...

    //on response, when we want to trigger and send notification that our job is finished
    HashMap<String,String> params = new HashMap<String, String>();          
    params.put("isSuccess", String.valueOf(false));
    NotificationCenter.postNotification(context, NotificationType.LoginResponse, params);
}

vậy đó, chúc mừng!


Cảm ơn giải pháp của bạn! Tôi thấy rằng sử dụng Bundle paramsthay vì HashMapthuận tiện hơn để chuyển các tham số của các loại khác nhau. Có một kết nối tốt đẹp giữa IntentBundle:intent.putExtras(params)
zubko

4

Bạn có thể sử dụng cái này: http://developer.android.com/reference/android/content/BroadcastReceiver.html , cho một hành vi tương tự.

Bạn có thể đăng ký người nhận theo chương trình thông qua Context.registerReceiver (BroadcastReceiver, IntentFilter) và nó sẽ nắm bắt các ý định được gửi qua Context.sendBroadcast (Intent).

Tuy nhiên, lưu ý rằng người nhận sẽ không nhận được thông báo nếu hoạt động (ngữ cảnh) của người đó đã bị tạm dừng.


Lưu ý thiết kế nhanh: BroadcastReceivers và NSNotificationCenter đều có thể hoạt động như một bộ tổng hợp sự kiện. Ưu điểm so với Đại biểu hoặc Người quan sát là người gửi và người nhận được tách rời (chúng thực sự có ghép nối thông điệp hoặc dữ liệu nhưng đó là một trong những kiểu ghép nối yếu nhất). Đã chỉnh sửa có đính chính.
AngraX

4

Tôi thấy rằng việc sử dụng EventBus của Guava lib là cách đơn giản nhất để giao tiếp kiểu đăng ký xuất bản giữa các thành phần mà không yêu cầu các thành phần đăng ký rõ ràng với nhau

xem mẫu của họ trên https://code.google.com/p/guava-libraries/wiki/EventBusExplained

// Class is typically registered by the container.
class EventBusChangeRecorder {
  @Subscribe public void recordCustomerChange(ChangeEvent e) {
    recordChange(e.getChange());
  }

// somewhere during initialization
eventBus.register(this);

}

// much later
public void changeCustomer() {
  eventBus.post(new ChangeEvent("bla bla") );
} 

bạn có thể thêm lib này đơn giản trên Android Studio bằng cách thêm phần phụ thuộc vào build.gradle:

compile 'com.google.guava:guava:17.0'

Phù hợp hơn cho mã bên 'mô hình' có thể ít phụ thuộc vào nền tảng hơn.
karmakaze

2

Kotlin : Đây là phiên bản của @ Shiki trong Kotlin với một chút cấu trúc lại trong một mảnh.

  1. Đăng ký người quan sát trong Fragment.

Fragment.kt

class MyFragment : Fragment() {

    private var mContext: Context? = null

    private val mMessageReceiver = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            //Do something here after you get the notification
            myViewModel.reloadData()
        }
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)

        mContext = context
    }

    override fun onStart() {
        super.onStart()
        registerSomeUpdate()
    }

    override fun onDestroy() {
        LocalBroadcastManager.getInstance(mContext!!).unregisterReceiver(mMessageReceiver)
        super.onDestroy()
    }

    private fun registerSomeUpdate() {
        LocalBroadcastManager.getInstance(mContext!!).registerReceiver(mMessageReceiver, IntentFilter(Constant.NOTIFICATION_SOMETHING_HAPPEN))
    }

}
  1. Đăng thông báo ở bất cứ đâu. Chỉ bạn cần bối cảnh.

    LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(Constant.NOTIFICATION_SOMETHING_HAPPEN))```

Tái bút :

  1. bạn có thể thêm Constant.kt như tôi để tổ chức tốt các thông báo. Constant.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Đối với ngữ cảnh trong một phân đoạn, bạn có thể sử dụng activity(đôi khi null) hoặc conextgiống như những gì tôi đã sử dụng.

0

Bạn có thể sử dụng các tham chiếu yếu.

Bằng cách này, bạn có thể tự quản lý bộ nhớ và thêm bớt các quan sát viên tùy ý.

Khi bạn addObserver thêm các tham số này - truyền ngữ cảnh đó từ hoạt động bạn đang thêm nó vào giao diện trống, thêm tên thông báo và gọi phương thức chạy giao diện.

Phương thức chạy giao diện sẽ có một chức năng được gọi là chạy để trả về dữ liệu mà bạn đang truyền một cái gì đó như thế này

public static interface Themethodtorun {
        void run(String notification_name, Object additional_data);
    }

Tạo một lớp quan sát gọi một tham chiếu với giao diện trống. Đồng thời xây dựng giao diện Themethodtorun của bạn từ ngữ cảnh đang được chuyển vào máy chủ bổ sung.

Thêm quan sát vào cấu trúc dữ liệu.

Để gọi nó sẽ là một phương pháp tương tự, tuy nhiên tất cả những gì bạn cần làm là tìm tên thông báo cụ thể trong cấu trúc dữ liệu, sử dụng Themethodtorun.run (tên_công_dục, dữ liệu).

Thao tác này sẽ gửi một cuộc gọi lại đến nơi bạn đã tạo một người quan sát với một tên thông báo cụ thể. Đừng quên loại bỏ chúng khi bạn hoàn thành!

Đây là tài liệu tham khảo tốt cho các tài liệu tham khảo yếu.

http://learningviacode.blogspot.co.nz/2014/02/weak-references-in-java.html

Tôi đang trong quá trình tải mã này lên github. Hãy mở to mắt!

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.