2012-06-09 25 views
21

Wiem, że do interakcji z JavaScript do Java trzeba wstrzyknąć obiekt Java za pomocą metody addjavascriptInterface w widoku webowym.Zrozumienie webview przeglądarki Android addjavascriptinterface

Oto problem, przed którym stoję.

  1. zarejestrować obiektu Java z wykorzystaniem addJavascriptInterface metoda będzie dostępna w moich JS.

  2. I wstrzyknąć kilka JS w WebView użyciu webview.loadURL("javascript:XXX");

  3. wysłać zdarzenie JS kiedy mam zrobić z wstrzykiwaniem JS.

Problem polega na tym, że jeśli natychmiast po kroku 1, jeśli mogę wykonać następujące JavaScript:

mWebView.loadUrl("javascript:if(window.myobject) console.log('myobject found---------'); else {console.log('myobject not found----');}"); 

otrzymuję „MyObject nie znaleziono” w dzienniku mojego konsoli.

Chcę wiedzieć, że jeśli jest jakiś czas, zanim będę mógł uzyskać dostęp do mojego obiektu, a jeśli tak, to jak mogę się dowiedzieć, ile czasu powinienem czekać na wywołanie mojego obiektu?

Odpowiedz

44

Chcę wiedzieć, że jeśli jest jakiś czas, zanim mogę uzyskać dostępu do obiektu

Tak, myślę, że to opóźnienie, ponieważ WebView.addJavascriptInterface będzie działał w wewnętrzny gwint pracownikowi Webview za. Być może zastanawiałeś się nad tym i zdałeś sobie sprawę, że WebView musi utrzymywać co najmniej jeden wątek roboczy do wykonania asynchronicznej sieciowej operacji wejścia/wyjścia. Być może zauważyłeś te wątki w DDMS podczas korzystania z WebView.

Okazuje się, że używa wątku do pracy z wieloma innymi metodami publicznymi. Naprawdę chciałbym, aby dokumenty od Google uczyniły to jaśniejszym! Mam jednak nadzieję, że pomogę i pokażę ci, jak próbowałem to potwierdzić dla siebie.

Śledź mnie, gdy przyjrzę się źródłu dla WebView. Jest to dość czytelne, nawet jeśli nie możesz dokładnie śledzić tego, co się dzieje, możliwe jest prześledzenie odpowiedzi na kilka pytań dotyczących wątków.

Źródło systemu Android można pobrać za pomocą narzędzia menedżera SDK, ale jest ono również odzwierciedlone na Githubie, więc to jest to, do czego mam tutaj link. Zgadłem i wybrałem tag zbliżony do jakiejś wersji ICS. Nie trudno jest znaleźć . Właśnie szukałem w Google "strony WebView.java: github.com/android".

Sposób WebView.addJavascriptInterface wysyła wiadomość do instancji WebViewCore:

mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg); 

W WebViewCore.java istnieje kilka przeciążonych metod zwanych sendMessage, ale naprawdę nie trzeba wiedzieć, jakie dokładnie jest nazywany, ponieważ robią prawie to samo. Jest nawet miły komentarz, który daje nam wskazówkę, że jesteśmy we właściwym miejscu! Wszyscy oni delegują do instancji EventHub, która jest wewnętrzną klasą. This method okazuje się być zsynchronizowane i wysyła wiadomość do instancji Handler, co jest dobrym wskazaniem, że prawdopodobnie działa w innym wątku, ale dla kompletności, dowiedzmy się!

Utworzono Handler w EventHub.transferMessages, która jest wywoływana z WebViewCore.initialize. Jest tu kilka dodatkowych przeskoków, ale w końcu dowiedziałem się, że jest to wywoływane z run w WebCoreThread (podklasa Runnable), które jest tworzone wraz z nowym Thread prawym here.

Co za przygoda! Tak więc, chociaż naprawdę nie mogę powiedzieć na pewno, co się dzieje z tymi ruchomymi częściami, jestem całkiem pewien, że mogę powiedzieć, że ta metoda nie jest synchroniczna i wysyła wiadomość do wątku roboczego WebView. Mam nadzieję, że ma to sens!

jeśli tak, to jak mogę się dowiedzieć, ile czasu powinienem czekać na telefon do mojego obiektu?

Niestety, nie znam odpowiedzi na to. Badałem dokładnie ten problem i znalazłem to pytanie w StackOverflow w trakcie mojego Googling. Myślę, że masz następujące opcje, z których niektóre są ładniejsze lub łatwiejsze niż inne:

1) Tylko Thread.sleep przez 100 ms lub coś pomiędzy addJavascriptInterface i loadUrl("javascript:..."). Blech, nie podoba mi się to, ale jest to potencjalnie najłatwiejsze.

2) Inna możliwość polega na tym, że można zadzwonić pod numer WebView.loadUrl z fragmentem kodu JavaScript, który dokładnie sprawdza, czy interfejs jest ustawiony, i przechwytuje wywołanie ReferenceError, jeśli nie jest jeszcze ustawione. Jednak, jak można się domyślić, ten rodzaj polega na dodaniu interfejsu JavaScript do WebView!

3) Zamiast tego należy zadzwonić pod numer WebView.setWebChromeClient i zastąpić kod JavaScript w wersji alert() lub console.log. Z moich eksperymentów ta metoda jest synchroniczna, więc nie ma opóźnienia. (Potwierdziłem to w źródle, ale szczegóły pozostawiam jako ćwiczenie dla czytelnika) Powinieneś prawdopodobnie wymyślić specjalny ciąg, który zadzwonisz pod numer alert i sprawdzisz go wewnątrz onJsAlert, więc nie łapiesz po prostu wszystkich alert() s.

Przepraszamy za długość tej odpowiedzi, mam nadzieję, że pomaga. Powodzenia!

+0

dobra odpowiedź opracować odpowiednie referencje. –

+0

fajne. miła odpowiedź. Ale sprawdzanie alertów i komunikatów konsoli jest tak samo złe, jak robienie Thread.sleep() :) – Akshat

+0

Hej, ostrzegłem, że opcje są nieoptymalne: P – spacemanaki

1

Upewnij się, że twoje obiekty JavaScript zadeklarowane w twoim kodzie HTML/Javascript, do których chcesz uzyskać dostęp z Java, są deklarowane jako globalne, w przeciwnym razie najprawdopodobniej zostaną zebrane. Mam kod, który wykonuje to (gdzie jest mój interfejs Androida dodaje addJavascriptInterface):

metoda
<script> 
    var cb = function(location) { 
    alert('location is ' + location); 
    } 
    Android.getLocation('cb'); 
</script> 

getLocation wywołuje LocationManager.requestSingleUpdate Androida, które następnie wywołuje wywołania zwrotnego Gdy wyzwalana LocationListener.

Bez "var" stwierdzam, że do czasu wywołania funkcji wywołania zwrotnego funkcja wywołania zwrotnego została zebrana.

0

(skopiowane z mojej odpowiedzi na a similar question)

Mam podjętej Jason Shah i realizacja pana S. jako budulec dla mojej poprawki i poprawione to znacznie.

Jest zbyt dużo kodu do umieszczenia w tym komentarzu, po prostu link do niego.

Kluczowe punkty to:

  • Dotyczy wszystkich wersji Gingerbread (2.3.x)
  • połączeń od JS na Androida są teraz synchroniczne
  • Nie ma już t O nakreślić metody interfejsu ręcznie
  • Poprawiono możliwość separatorów smyczkowych łamanie kodu
  • Znacznie łatwiej zmienić JS podpis i nazwy interfejsu