web-dev-qa-db-fra.com

WebViewClient n'appelant pas shouldOverrideUrlLoading

Le problème est plutôt simple. Dans l'application, nous voulons garder une trace de l'URL actuelle affichée. Pour cela, nous utilisons shouldOverrideUrlLoading callback à partir de WebViewClient en enregistrant l'url dans un champ de classe pour chaque mise à jour. Voici le code pertinent:

    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);

Cependant, il existe au moins un scénario dans lequel le rappel n'est jamais déclenché et le champ mCurrentUrl n'est pas mis à jour.

L'URL: https://m.Pandora.net/es-es/products/bracelets/5560

Dernière URL mise à jour (shouldOverrideUrlLoading n'est jamais appelé lorsque vous cliquez sur le produit): https://m.Pandora.net/es-es/products/bracelets

J'ai essayé avec des rappels comme onPageStarted (), mais l'url est également filtrée et il ne semble pas y en avoir une accessible en amont depuis son code protégé.

Lecture Android documentation sur WebView j'ai trouvé ceci:

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

Le nouveau WebView applique des restrictions supplémentaires lors de la demande de ressources et de la résolution de liens qui utilisent un schéma d'URL personnalisé. Par exemple, si vous implémentez des rappels tels que shouldOverrideUrlLoading () ou shouldInterceptRequest (), WebView les appelle uniquement pour les URL valides.

Mais cela n'a toujours pas de sens puisque l'URL ci-dessus est générique et devrait répondre à la norme.

Une alternative ou une solution à cela?

14
SuppressWarnings

Lorsque vous cliquez sur un produit sur cette page Web, il charge le nouveau contenu avec JavaScript et met à jour l'URL visible dans la barre d'adresse à l'aide de HTML5 History APIs .

De l'article MDN ci-dessus:

Cela provoquera l'affichage de la barre d'URL http://mozilla.org/bar.html , mais n'entraînera pas le navigateur à charger bar.html ni même à vérifier que bar.html existe.

Celles-ci sont parfois appelées applications d'une seule page . Étant donné que la page chargée réelle ne change pas, le rappel WebView pour le chargement des pages n'est pas appelé.

Si vous savez précisément quel type de requête HTTP vous souhaitez intercepter, vous pouvez utiliser le callback shouldInterceptRequest qui est appelé pour chaque requête. Il est probable que l'application Web charge certaines données d'une API, par exemple lorsqu'un produit est affiché, que vous pourriez ensuite détecter.

Si la détection n'est pas possible, mais que vous contrôlez l'application Web, vous pouvez utiliser interface JavaScript Android pour appeler des méthodes dans l'application Android directement à partir de la page Web.

Si vous ne contrôlez pas la page chargée, vous pouvez toujours essayer injecter un fichier JavaScript local dans la page Web et observer quand les API d'historique sont utilisées , puis appeler des méthodes dans votre Android via l'interface JS. J'ai essayé d'observer ces événements dans Chrome avec la méthode décrite dans le lien précédent et cela semble bien fonctionner) .

20
Leo Nikkilä

Veuillez omettre mWebView.getSettings().setDomStorageEnabled(true);

Ensuite, essayez à nouveau, si une nouvelle URL trouvée, alors invoquera shouldOverrideUrl()

6
Jijo

J'ai eu le même problème que vous et j'ai fini d'étendre WebViewChromeClient en écoutant le rappel de

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
            }
        }

    }; 
5
Michael Katkov

Une autre approche que vous pouvez essayer: Attrapez l'URL par côté javascript . Initialisez votre webView avec ceci:

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

Après que la page soit complètement chargée (vous pouvez utiliser un algorithme pour vérifier ceci comme ceci https://stackoverflow.com/a/6199854/41986 ), puis:

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);");
}

Et déclarez votre 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
            }
        });

    }

}

Vous pouvez faire quelque chose comme ça pour obtenir l'URL, ou toute autre chose à l'intérieur de la page.

2
PMateus
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;
    }
}
}

essaye celui-là...

1
Ganesh Pokale

Ajouter webView.getSetting().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); puis shouldOverrideUrl sera déclenché.

1
Saurabh Padwekar

Pour moi, le problème était en dessous de la ligne -

mWebView.getSettings().setSupportMultipleWindows(true);

Après l'avoir supprimé, shouldOverrideUrlLoading était appelé.

1
Hrishikesh Kadam

après être tombé sur ce problème et chercher des solutions, j'ai trouvé celui qui fonctionnait parfaitement pour moi

https://stackoverflow.com/a/56395424/10506087

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

onProgressChanged est toujours déclenché lors du rechargement, du chargement d'une nouvelle page avec userclick ou XmlHttpRequest. Comparez l'URL de la charge précédente et la charge actuelle, vous saurez qu'il s'agit de recharger ou de charger une nouvelle page. Cela fonctionne parfaitement dans mon application Web d'une seule page.

Déclarez d'abord une variable globale pour stocker la dernière URL.

String strLastUrl = null;

Remplacez ensuite onProgressChanged (vue 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);
        }
});

J'ai également essayé les solutions ci-dessus, mais la plupart d'entre elles ont des problèmes dans mon cas:

  1. doUpdateVisitedHistory ne peut parfois pas retourner l'URL correcte après "#" faite par XmlHttpRequest.
  2. onReceivedTitle ne fonctionne pas dans mon cas car la réponse récupérée par XMLHttpRequest n'a pas <title></title> tag.
  3. La méthode JavascriptInterface fonctionne également, mais je crains qu'elle ne cause des problèmes de sécurité avec javascript.
0
Lynch Chen