WebViewClient не вызывает shouldOverrideUrlLoading

Проблема довольно проста. В приложении мы хотим отслеживать текущий отображаемый URL. Для этого мы используем обратный вызов shouldOverrideUrlLoading из WebViewClient, сохраняя URL-адрес в поле класса для каждого обновления. Вот соответствующий код:

    mWebView.getSettings().setJavaScriptEnabled(true);
    mWebView.getSettings().setDomStorageEnabled(true); 
    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            mCurrentUrl = url;

            // If we don't return false then any redirect (like redirecting to the mobile
            // version of the page) or any link click will open the web browser (like an
            // implicit intent).
            return false;
        }



        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            ...
        }
    });
    mWebView.loadUrl(mInitialUrl);

Однако существует, по крайней мере, один сценарий, когда обратный вызов никогда не запускается, и поле mCurrentUrl не обновляется.

URL: https://m.pandora.net/es-es/products/bracelets/556000

Последний обновленный url (shouldOverrideUrlLoading никогда не вызывается при нажатии на продукт): https://m.pandora.net/es-es/products/bracelets

Я пробовал с callbacks, например onPageStarted(), но URL-адрес также фильтруется и, похоже, не существует доступного вверх по течению с момента его защищенного кода.

Чтение документации по Android для WebView Я нашел это:

https://developer.android.com/guide/webapps/migrating.html#URLs

Новый WebView применяет дополнительные ограничения при запросе ресурсов и разрешении ссылок, использующих настраиваемую схему URL. Например, если вы реализуете обратные вызовы, такие как shouldOverrideUrlLoading() или shouldInterceptRequest(), то WebView вызывает их только для действительных URL-адресов.

Но все еще не имеет смысла, поскольку указанный выше URL-адрес является общим и должен соответствовать стандарту.

Любая альтернатива или решение этого?

Ответ 1

При нажатии продукта на этой веб-странице он загружает новое содержимое с помощью JavaScript и обновляет видимый URL-адрес в адресной строке с помощью API истории HTML5.

Из приведенной выше статьи MDN:

Это приведет к тому, что панель URL отобразит http://mozilla.org/bar.html, но не заставит браузер загружать bar.html или даже проверить, что bar.html существует.

Они иногда называются одностраничными приложениями. Поскольку фактическая загруженная страница не изменяется, обратный вызов WebView для загрузки страницы не называется.

Если вы точно знаете, какой HTTP-запрос вы хотите перехватить, вы можете использовать обратный вызов shouldInterceptRequest, который вызывается для каждого запроса. Вероятно, веб-приложение загружает некоторые данные из API, например, когда отображается продукт, который вы могли бы обнаружить.

Если обнаружить это невозможно, но вы контролируете веб-приложение, вы можете использовать Android JavaScript interface для вызова методов в Android приложение непосредственно с веб-страницы.

Если вы не контролируете загруженную страницу, вы можете попробовать вставить локальный файл JavaScript на веб-страницу и наблюдать, когда используются API истории, а затем вызывать методы в приложении Android через интерфейс JS. Я пробовал наблюдать за этими событиями в Chrome с помощью метода, описанного в предыдущей ссылке, и, похоже, он работает нормально.

Ответ 2

Пожалуйста, опустите mWebView.getSettings().setDomStorageEnabled(true);

Затем снова попробуйте, если новый найденный URL-адрес будет вызывать shouldOverrideUrl()

Ответ 3

У меня была такая же проблема, как и вы, и я закончил с расширением WebViewChromeClient с прослушиванием обратного вызова

public void onReceivedTitle(WebView view, String title)

mWebView.setWebChromeClient(mSWWebChromeClient);

private WebChromeClient mSWWebChromeClient = new WebChromeClient() {

        @Override
        public void onReceivedTitle(WebView view, String title) {
            super.onReceivedTitle(view, title);
            if (!view.getUrl().equals(mCurrentUrl)) {
                mCurrentUrl = view.getUrl();
                //make something
            }
        }

    }; 

Ответ 4

Другой подход, который вы можете попробовать: Поймать URL-адрес со стороны javascript. Инициализируйте свой webView следующим образом:

webView.addJavascriptInterface(new WebAppInterface(getActivity()), "Android");

После полной загрузки страницы (вы можете использовать алгоритм для проверки этого типа fooobar.com/questions/123878/...), затем:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    webView.evaluateJavascript("(function() {return window.location.href;})", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String url) {
            //do your scheme with variable "url"
        }
    });
} else {
    webView.loadUrl("javascript:Android.getURL(window.location.href);");
}

И объявите свой WebAppInterface:

public class WebAppInterface {
    Activity mContext;

    public WebAppInterface(Activity c) {
        mContext = c;
    }

    @JavascriptInterface
    public void getURL(final String url) {
        mContext.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //do your scheme with variable "url" in UIThread side. Over here you can call any method inside your activity/fragment
            }
        });

    }

}

Вы можете сделать что-то подобное, чтобы получить URL-адрес или что-то еще внутри страницы.

Ответ 5

public class AndroidMobileAppSampleActivity extends Activity {
 /** Called when the activity is first created. */
String mCurrentUrl="";
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    WebView mWebView = (WebView) findViewById(R.id.mainWebView);

    WebSettings webSettings = mWebView.getSettings();
    webSettings.setJavaScriptEnabled(true);

    mWebView.setWebViewClient(new MyCustomWebViewClient());
    mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);

    mWebView.loadUrl("https://m.pandora.net/es-es/products/bracelets/556000");

}

private class MyCustomWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        mCurrentUrl = url;
        Log.i("mCurrentUrl",""+mCurrentUrl);  
        view.loadUrl(url);

        return true;
    }
}
}

попробуйте этот...

Ответ 6

Добавьте webView.getSetting().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); тогда должен быть запущен shouldOverrideUrl.

Ответ 7

Для меня проблема была ниже линии -

mWebView.getSettings().setSupportMultipleWindows(true);

После удаления его должен был вызываться долженOverrideUrlLoading.

Ответ 8

после того, как я наткнулся на эту проблему и искал решения, я нашел тот, который отлично сработал для меня

fooobar.com/questions/588876/...

 override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
    // your code here
    super.doUpdateVisitedHistory(view, url, isReload)
}

Ответ 9

onProgressChanged всегда запускается при перезагрузке, загрузке новой страницы с помощью userclick или XmlHttpRequest. Сравните URL-адрес предыдущей загрузки и текущей загрузки, вы узнаете, что она перезагружается или загружается новая страница. Это прекрасно работает в моем одностраничном веб-приложении.

Сначала объявите глобальную переменную для хранения последнего URL.

String strLastUrl = null;

Затем переопределите onProgressChanged (представление WebView, int progress)

mWebView.setWebChromeClient(new MyWebChromeClient(){

   @Override
   public void onProgressChanged(WebView view, int progress) {
            if (progress == 100) {
                //A fully loaded url will come here
                String StrNewUrl = view.getUrl();
                if(TextUtils.equals(StrNewUrl,strLastUrl)){
                   //same page was reloaded, not doing anything
                }else{
                   //a new page was loaded,write this new url to variable
                   strLastUrl = StrNewUrl;
                   //do your work here
                   Log.d("TAG", "A new page or xhr loaded, the new url is : " + strLastUrl);
                }
            }
            super.onProgressChanged(view, progress);
        }
});

Я также пробовал описанные выше решения, но большинство из них имеют проблемы в моем случае:

  1. doUpdateVisitedHistory иногда не может вернуть правильный URL после "#", сделанного XmlHttpRequest.
    • Мой случай - это одностраничное веб-приложение. Веб-приложение использует JavaScript с xhr для отображения новой страницы, когда пользователь щелкает элемент. Например, пользователь в настоящее время на http://example.com/myapp/index.php, после нажатия URL браузера становится http://example.com/myapp/index.php#/myapp/query.php?info=1, но в в этом случае doUpdateVisitedHistory возвращает http://example.com/myapp//myapp/
  2. onReceivedTitle не работает в моем случае, потому что ответ, полученный XMLHttpRequest, не имеет тега <title></title>.
  3. Метод JavascriptInterface также работает, но я боюсь, что это приведет к связанные с безопасностью вопросы с JavaScript.