Итак, я рад сообщить, что моя проблема решена.По сути, это известная ошибка в Gingerbread, и она присутствует на моем устройстве 2.3.4.После некоторых царапин на голове я нашел этот обходной путь , придуманный Джейсоном Шахом в PhoneGap.Настоящая благодарность за это принадлежит ему, так как мое решение - слегка измененная версия кода в этом посте.
WebView
В моем методе onLoad я вызываю следующую функцию.
private void configureWebView() {
try {
if (Build.VERSION.RELEASE.startsWith("2.3")) {
javascriptInterfaceBroken = true;
}
} catch (Exception e) {
// Ignore, and assume user javascript interface is working correctly.
}
threadView = (WebView) findViewById(R.id.webViewThread);
threadView.setWebViewClient(new ThreadViewClient());
Log.d(APP_NAME, "Interface Broken? " + javascriptInterfaceBroken.toString());
// Add javascript interface only if it's not broken
iface = new JavaScriptInterface(this);
if (!javascriptInterfaceBroken) {
threadView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
}
}
Здесь происходит несколько вещей.
В отличие от метода PhoneGap, я использую startsWith
сравнение со строкой версии.Это потому, что на моем эталонном устройстве установлен Build.VERSION.RELEASE 2.3.4.Вместо того чтобы тестировать все выпуски серии 2.3, мне удобно рисовать все устройства одним мазком.
javascriptInterface - это bool
, инициализированный false
.JavaScriptInterface, созданный как iface, - это класс, который обычно обрабатывает события JS в моем WebView.
ThreadViewClient - основа моей реализации.Это где вся логика обработки обходного пути происходит.
WebViewClient
В классе ThreadViewClient (который расширяет WebViewClient) я сначала объясняю тот факт, что jsОбработчик, который обычно подключается к Android, отсутствует здесь.Это означает, что, если я хочу использовать те же вызовы JavaScript из моего WebView, мне нужно продублировать интерфейс.Это достигается путем вставки пользовательских обработчиков в контент вашего веб-сайта после его загрузки ...
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (javascriptInterfaceBroken) {
final String handleGingerbreadStupidity =
"javascript:function shortSignature(id) { window.location='http://MyHandler:shortSignature:'+id; }; "
+ "javascript: function longSignature(text, username, forumnumber,threadnumber,pagenumber,postid) { var sep='[MyHandler]';"
+ "window.location='http://MyHandler:longSignature:' + encodeURIComponent(text + sep + username + sep + forumnumber + sep + threadnumber + sep + pagenumber + sep + postid);};"
+ "javascript: function handler() { this.shortSignature = shortSignature; this.longSignature = longSignature;}; "
+ "javascript: var Android = new handler();";
view.loadUrl(handleGingerbreadStupidity);
}
}
Там есть что обработать.В javascript я определяю объект handler
, который содержит функции, которые отображаются на мой интерфейс js.Экземпляр этого затем привязывается к «Android», это то же имя интерфейса, которое используется в реализации не 2.3.Это позволяет повторно использовать код, отображаемый в вашем контенте веб-просмотра.
Функции используют тот факт, что Android позволяет перехватывать всю навигацию, которая происходит в WebView.Для связи с внешней программой они меняют расположение окна на одно со специальной подписью.Я немного углублюсь в это.
Еще одна вещь, которую я делаю, - это объединение параметров функций с более чем одним параметром.Это позволяет мне уменьшить сложность кода в обработчике местоположения.
Обработчик местоположения также помещается в ThreadViewClient ...
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Method sMethod = null;
Log.d(APP_NAME, "URL LOADING");
if (javascriptInterfaceBroken) {
if (url.contains("MyHandler")) {
StringTokenizer st = new StringTokenizer(url, ":");
st.nextToken(); // remove the 'http:' portion
st.nextToken(); // remove the '//jshandler' portion
String function = st.nextToken();
String parameter = st.nextToken();
Log.d(APP_NAME, "Handler: " + function + " " + parameter);
try {
if (function.equals("shortSignature")) {
iface.shortSignature(parameter);
} else if (function.equals("longSignature")) {
iface.longSignature(parameter);
} else {
if (sMethod == null) {
sMethod = iface.getClass().getMethod(function, new Class[] { String.class });
}
sMethod.invoke(iface, parameter);
}
}
//Catch & handle SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
return true;
}
}
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
}
Здесь я перехватываю все события загрузки URL, которые происходят ввеб-просмотр.Если целевой URL содержит волшебную строку, приложение пытается проанализировать ее, чтобы извлечь вызов метода.Вместо того, чтобы использовать токенизатор для извлечения отдельных параметров, я передаю его версии моего longSignature
метода, который может анализировать и обрабатывать его.Это подробно описано в заключительной части этого поста.
Если к моменту выхода из блока «javascriptInterfaceBroken» выполнение не было возвращено вызывающей стороне, этот метод обрабатывает действие загрузки URL-адреса как нормальное.событие кликнуло ссылку.В случае моего приложения я не хочу использовать WebView для этого, поэтому я передаю его операционной системе через намерение ACTION_VIEW.
Это очень похоже на реализацию в блоге Джейсона.Однако я обхожу размышления по большей части.Я пытался использовать метод в блоке с отражением для обработки всех моих связанных функций, но из-за того, что JavaScriptInterface был вложенным классом, я не смог посмотреть на него из другого.Однако, поскольку я определил интерфейс в основной области действия Activity, его методы можно вызывать напрямую.
Обработка объединенных параметров
Наконец, в моем JavaScriptInterface я создал обработчик для работы со случаемсвязанного параметра ...
public void longSignature(String everything) {
try {
everything = URLDecoder.decode(everything, "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.e(APP_NAME, e);
}
final String[] elements = everything.split("\\[MyHandler\\]");
if (elements.length != 6) {
Toast.makeText(getApplicationContext(), "[" + elements.length + "] wrong number of parameters!", Toast.LENGTH_SHORT).show();
}
else {
longSignature(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]);
}
}
Ура, полиморфизм!
И это мое решение!Есть много возможностей для улучшения, но пока этого достаточно.Извините, если некоторые из моих соглашений вызвали у вас хаки - это мое первое приложение для Android, и я не знаком с некоторыми из лучших практик и соглашений.Удачи!