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?
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) .
Veuillez omettre mWebView.getSettings().setDomStorageEnabled(true);
Ensuite, essayez à nouveau, si une nouvelle URL trouvée, alors invoquera shouldOverrideUrl()
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
}
}
};
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.
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à...
Ajouter webView.getSetting().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
puis shouldOverrideUrl
sera déclenché.
Pour moi, le problème était en dessous de la ligne -
mWebView.getSettings().setSupportMultipleWindows(true);
Après l'avoir supprimé, shouldOverrideUrlLoading était appelé.
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)
}
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:
doUpdateVisitedHistory
ne peut parfois pas retourner l'URL correcte après "#" faite par XmlHttpRequest. doUpdateVisitedHistory
renvoie http://example.com/myapp//myapp/onReceivedTitle
ne fonctionne pas dans mon cas car la réponse récupérée par XMLHttpRequest n'a pas <title></title>
tag.