NSNotificationCenter와 동등한 Android


95

아이폰 애플리케이션을 안드로이드로 포팅하는 과정에서 앱 내에서 소통하는 가장 좋은 방법을 찾고 있습니다. 인 텐트가가는 길인 것 같습니다. 이것이 최선의 (유일한) 옵션입니까? NSUserDefaults는 성능과 코딩 모두에서 Intents보다 훨씬 가볍습니다.

또한 상태에 대한 애플리케이션 하위 클래스가 있지만 다른 활동에서 이벤트를 인식하도록해야합니다.


3
이 주제를 처음 접하는 사람들에게는 두 번째 답변이 가장 좋습니다. 아래로 스크롤 ...
— Stephan

답변:


6

이것을 시도해 볼 수 있습니다 : http://developer.android.com/reference/java/util/Observer.html


42
아래 Shiki의 답변이 훨씬 좋습니다.
— dsaff

5
@dsaff는 더 완전한 대답 임에도 불구하고 내 대답이 틀린 것은 아니지만 분명히 -1을받을 자격이 없습니다. 당신이 Shiki의 대답을 +1하는 것이 의미가 있습니다.
— Rui Peres

4
시키의 질문에 대한 좋은 답변입니다
— Ramz

4
기술적으로 부정확하고 스팸 답변 만 반대 투표해야합니다.이 답변은 어느 쪽에도 적합하지 않습니다. 보상에 +1하고 Shiki에 대해서도 +1합니다.

351

내가 찾은 가장 좋은 것은 Android 지원 패키지의 일부인 LocalBroadcastManager 입니다 .

LocalBroadcastManager 문서에서 :

프로세스 내의 로컬 개체에 인 텐트 브로드 캐스트를 등록하고 전송하는 도우미입니다. 이것은 sendBroadcast (Intent)를 사용하여 글로벌 브로드 캐스트를 보내는 것보다 많은 이점이 있습니다.

  • 브로드 캐스팅중인 데이터가 앱을 떠나지 않을 것임을 알고 있으므로 개인 데이터 유출에 대해 걱정할 필요가 없습니다.
  • 다른 애플리케이션이 이러한 브로드 캐스트를 앱에 보낼 수 없으므로 악용 할 수있는 보안 허점에 대해 걱정할 필요가 없습니다.
  • 시스템을 통해 글로벌 브로드 캐스트를 보내는 것보다 더 효율적입니다.

이것을 사용할 때 an Intent은 NSNotification. 다음은 예입니다.

ReceiverActivity.java

라는 이벤트에 대한 알림을 감시하는 활동입니다 "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

알림을 보내거나 브로드 캐스트하는 두 번째 활동입니다.

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

버튼마다 상기 코드 R.id.button_send클릭, 텐트를 방송하고 수신한다 mMessageReceiver에서 ReceiverActivity.

디버그 출력은 다음과 같아야합니다.

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
시간을내어 유용하고 상세한 답변을 작성해 주셔서 대단히 감사합니다.
— Chris Lacy 2012

14
onCreate 메서드에서 registerReceiver를 호출하면 활동이 유출되고 onDestroy 메서드가 호출되지 않으므로 아마 호출해서는 안됩니다. onResume은 registerReceiver를 호출하는 것이 더 나은 선택이고, onPause는 unregisterReceiver를 호출하는 것 같습니다.
— 스테판 Jais에

4
에 완벽하게 해당하는 NSNotificationCenter답이 허용되어야합니다!
— Leon Storey

전역 알림을 사용하면 디자인이 엉망이 될 수 있음을 지적하고 싶습니다. 쉬운 방법으로 이동하기 전에 구성 요소 간의 최상의 결합이 무엇인지 생각해보십시오. 때로는 리스너 또는 iOS 델리게이트 패턴과 유사한 것을 사용하는 것이 더 낫습니다.
— saulobrito 2014 년

감사합니다. @Shiki 당신이이 질문에 저에게 당신의 의견을 수 있다고 생각 해 주시기 바랍니다 stackoverflow.com/questions/25598696/...
— 악셀

16

다음은 @Shiki 답변과 비슷하지만 iOS 개발자 및 알림 센터의 관점에서 볼 수 있습니다.

먼저 일종의 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);
 }
}

그런 다음 문자열로 코딩 할 때 실수를 방지하기 위해 일부 열거 형 유형 (NotificationType)도 필요합니다.

public enum NotificationType {

   LoginResponse;
   // Others

}

예를 들어 활동에서 사용 (관찰자 추가 / 제거)은 다음과 같습니다.

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

마지막으로 콜백 또는 휴식 서비스 등에서 NotificationCenter에 알림을 게시하는 방법은 다음과 같습니다.

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

그게 다야, 건배!


솔루션에 감사드립니다! Bundle params대신 사용 하는 HashMap것이 다른 유형의 매개 변수를 전달하는 데 더 편리 하다는 것을 알았습니다 . 이 사이 좋은 연결은 Intent과 Bundle:intent.putExtras(params)
— zubko

4

비슷한 동작을 제공하는 http://developer.android.com/reference/android/content/BroadcastReceiver.html을 사용할 수 있습니다 .

Context.registerReceiver (BroadcastReceiver, IntentFilter)를 통해 프로그래밍 방식으로 수신자를 등록 할 수 있으며 Context.sendBroadcast (Intent)를 통해 전송 된 인 텐트를 캡처합니다.

하지만 수신자는 활동 (컨텍스트)이 일시 중지 된 경우 알림을받지 않습니다.


빠른 디자인 노트 : BroadcastReceivers 및 NSNotificationCenter는 둘 다 이벤트 수집기로 작동 할 수 있습니다. 델리게이트 또는 옵저버에 비해 장점은 발신자와 수신자가 분리된다는 것입니다 (실제로 메시지 또는 데이터 결합이 있지만 가장 약한 결합 유형 중 하나입니다). 수정하여 편집했습니다.
— AngraX 2011 년

4

Guava lib의 EventBus를 사용하는 것이 구성 요소가 서로 명시 적으로 등록 할 필요없이 구성 요소 간의 게시-구독 스타일 통신을위한 가장 간단한 방법이라는 것을 알았습니다.

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

build.gradle에 종속성을 추가하여 Android Studio에서 간단히이 lib를 추가 할 수 있습니다.

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

플랫폼에 덜 의존 할 수있는 '모델'측 코드에 더 적합합니다.
— karmakaze

2

Kotlin : 조각에서 약간의 리팩터링이있는 Kotlin의 @Shiki 버전이 있습니다.

  1. 조각에 관찰자를 등록합니다.

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. 어디서나 알림을 게시합니다. 컨텍스트가 필요합니다.

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

추신 :

  1. 알림을 잘 구성하기 위해 저와 같은 Constant.kt를 추가 할 수 있습니다. Constant.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. 단편의 컨텍스트에 대해 activity(때로는 null) 사용하거나 conext내가 사용한 것과 비슷할 수 있습니다 .

0

약한 참조를 사용할 수 있습니다.

이렇게하면 메모리를 직접 관리하고 원하는대로 관찰자를 추가 및 제거 할 수 있습니다.

addObserver가 이러한 매개 변수를 추가 할 때-추가하려는 활동에서 해당 컨텍스트를 빈 인터페이스에 캐스트하고 알림 이름을 추가하고 인터페이스를 실행하는 메소드를 호출하십시오.

인터페이스를 실행하는 메소드에는 다음과 같이 전달하는 데이터를 반환하기 위해 run이라는 함수가 있습니다.

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

빈 인터페이스로 참조를 호출하는 관찰 클래스를 만듭니다. 또한 addobserver에서 전달되는 컨텍스트에서 Themethodtorun 인터페이스를 구성하십시오.

데이터 구조에 관찰을 추가합니다.

이를 호출하려면 동일한 방법이지만 데이터 구조에서 특정 알림 이름을 찾기 만하면됩니다. Themethodtorun.run (notification_name, data)을 사용하세요.

이렇게하면 특정 알림 이름으로 관찰자를 생성 한 위치로 콜백이 전송됩니다. 완료되면 제거하는 것을 잊지 마십시오!

이것은 약한 참조에 대한 좋은 참조입니다.

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

이 코드를 github에 업로드하는 중입니다. 계속 눈을 뜨십시오!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.