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.
a czy Twój ImageDetectionFilter to tuż jest aktywnośc ? czy po prostu klasa?
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ą).
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.
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..
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
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
}