Wysłanie wiadomości z klasy nie dziedziczącej po Activity

0

Witam, piszę aplikację, która generalnie rozpoznaje jakieś obiekty (zdjęcia). Póki co mam klasę, która przetwarza obraz z kamery telefonu (ImageDetectionFilter) i jeżeli znajdzie szukany obiekt to wyświetli obramowanie wokół niego. Jednak chciałbym żeby po znalezieniu tego obiektu pokazało się nowego okienko z jakimś tekstem dotyczącym tego obiektu i w tym momencie mam problem. Czy dałoby się przekazać jakoś informację do klasy 'MainActivity' z klasy ImageDetectionFilter, że ten obiekt został znaleziony ? Bo jeżeli klasa rozszerzająca Activity wiedziałaby, że obraz został znaleziony to by nie było problemu, tylko z racji, że jestem początkujący nie bardzo wiem jak się do tego zabrać.
Z góry dziękuję za wszelkie sugestie, porady.

0

a czy Twój ImageDetectionFilter to tuż jest aktywnośc ? czy po prostu klasa?

0

Hmm, stwórz interfejs z metodą np. onImageDetected(ImageObject obj). Klasa ImageDetectionFilter niech wywołuje tę metodę na obiekcie implementującym interfejs (który w tym przypadku będzie aktywnością).

1

Nic tam nie trzeba rozszerzać.
Tworzysz klasę wydarzenia:

   class ImageRecognizedEvent {
      /// twoje dane
   }

W activity w metodzie onResume() dajesz:

   BusProvider.getInstance().register(this);

W activity w metodzie onPause dajesz:

  BusProvider.getInstance().unregister(this);

W activity tworzysz metodę:

  @Subscribe
  public void imageRecognized(ImageRecognizedEvent  event) {
  // funkcja wywołana zaraz po tym jak wyślesz zdarzenie metodą post z każdego dowolnego miejsca aplikacji
 }

W klasie ImageDetectionFilter jak masz akcję, którą chcesz wywołać na aktywności to po prostu wysyłasz zdarzenie za pomocą:

  BusProvider.getInstance().post(new ImageRecognizedEvent(/*jakies parametry z danymi, które chcesz przesłać*/));

Prostszy sposób, ale znacznie gorszy, ponieważ wprowadza "sztywne" wiązanie komponentów i dodatkowo nie rozwiązuje problemów związanych z lifecycle activity i fragmentów.
Tworzysz interfejs:

   interface ImageRecognizedListener {
      void onImageRecognized(/*jakies parametry z danymi, które chcesz przesłać*/);
   }

i implementujesz go w aktywności. Następnie do kontruktora klasy ImageDetectionFilter dodajesz paramtetr ImageRecognizedListener.
Później w tej klasie w razie potrzeby wywołujesz metodę onImageRecognized.
Ogólnie z Twojego ostatniego pytania wywnioskowałem, że nie wykazujesz się zbyt dużą wiedzą w programowaniu w Javie. Proponuję przerwać naukę androida na jakiś miesiąc, złapać porządnie podstawy i dopiero potem wrócić. Ogólnie cała platforma jest trudna do opanowania nawet dla doświadczonych programistów.

0

Więc zrobiłem tak jak poradziłeś:

public final class BusProvider {
	
	  private static final Bus BUS = new Bus();
	  
	  public static Bus getInstance() {
	    return BUS;
	  }

	  private BusProvider() {}
}

Stworzyłem prostą klasę:

public class ImageRecognizedEvent {
	
	String message;
	
	public ImageRecognizedEvent(String message){
		this.message = message;
	}	
}

W klasie ImageDetectionFilter dodaję (w funkcji, gdy zajdzie odpowiednie zdarzenie)

BusProvider.getInstance().post(new ImageRecognizedEvent(new String("yes")));

W klasie MainActivity odbieram to zdarzenie:

@Subscribe 
	public void imageRecognized (ImageRecognizedEvent event) {	    
      Log.e(TAG , event.toString());  
}

W metodach onPause() onResume() dodałem odpowiednio

 BusProvider.getInstance().unregister(this); 

i BusProvider.getInstance().register(this);


No i dostaję taki błąd jak wcześniej..
2

wywołujesz BUS z innego wątku "thread" niż MAIN thread.

rozwiązaniem jest użycie paramteru ThreadEnforcer.ANY na konstruktorze Bus-a

Ale lepiej jakbyś skumał po co tak robić i dlaczego lepiej nie :P

1

Działa, ale nie brzmi to zbyt zachęcająco.. Po co tak robić - żeby Bus mógł komunikować się z każdym wątkiem zamiast zmuszać go do użycia głównego wątku (main thread) ? A dlaczego nie to będę musiał gdzieś doczytać. - mikhal dzisiaj, 14:10

Po pierwsze dlatego, że modyfikacja interfejsu może odbywać się tylko z wątku głównego. W 99% po skończonej pracy chcesz jej wyniki pokazać użytkownikowi więc autor biblioteki słusznie założył, że każda odpowiedź powinna odbywać się na wątku głównym. Po drugie jeżeli masz jakąś operację, która składa się z kilku części, a nie stosujesz zaawansowanych rozwiązań pokroju RxJava tylko np. flagi:

   boolean zalogowano = false;
   boolean pobranoDaneUzytkownika = false;

to jeżeli zmieniasz je i odczytujesz na jednym wątku (głównym) to masz zawsze pewność, że nie będziesz miał problemów wynikających z wielowątkowości.

Jeżeli chcesz wysłać wiadomość z innego wątku niż główny robisz to tak:

Handler handler = new Handler(Looper.getMainLooper()) // nie pomyl z Looper.getMyLopper() !!
handler.post(new Runnable() {
   public void run() {
      BusProvider.getInstance().post(/*.......*/);   
   }
});

ewentualnie możesz skorzystać z mechanizmu BroadcastManager albo handler.sendMessage() (ale nie polecam na chwilę obecną, jest to po prostu bardziej skomplikowane).

To o czym wspomniał @wojciechmaciejewski ściąga tą blokadę, wtedy w metodzie odbierającej dane wydarzenie musisz wszystkie zmiany wywołać tak:

boolean czyToJestWatekGlowny = Looper.getMainLooper() == Looper.getMainLooper();

if(czyToJestWatekGlowny) {

   jakasMetodaDoZmianyInterfejsu();

} else {

   Handler handler = new Handler(Looper.getMainLooper()) // nie pomyl z Looper.getMyLopper() !!
   handler.post(new Runnable() {
      public void run() {
         jakasMetodaDoZmianyInterfejsu();
      }
   });

}

public void jakasMetodaDoZmianyInterfejsu() {
}

Ostatnim rozwiązaniem jest skorzystanie z drugiej biblioteki, o której wspominałem. Korzysta się z niej prawie identycznie, różnica jest taka, że metody odbierające wydarzenie musisz nazywać:
onEvent:

public void onEvent(ImageRecognizedEvent event) {
}

public void onEventMainThread(ImageRecognizedEvent event) { // jeżeli nazwiesz metodę onEventMainThread to wywoła się ona zawsze na wątku głównym
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1