Webview - onPageFinished is not called - android

In my App i want to cache a webapage in the background with a hidden webview (at the same time another webview is visible to the user and loads another url).
I start the caching in onResume. Here are the code snippets:
1.) onResume:
#Override
public void onResume() {
new LibraryCacher().startCaching(this);
//some more code...
}
2.) LibraryCacher:
public class LibraryCacher extends BasicClass {
public LibraryCacher () {}
public void startCaching(Context context) {
getLogger().debug("startCaching()");
if (NetworkHandler.isOnline(context) == false) {
getLogger().info("We are offline, no caching");
return;
}
final String URL_TO_CACHE = "http://myUrl.com";
WebView w = new WebView(context);
w.setVisibility(View.GONE);
WebSettings webset = w.getSettings();
webset.setJavaScriptEnabled(true);
webset.setDomStorageEnabled(true);
webset.setDatabaseEnabled(true);
webset.setJavaScriptCanOpenWindowsAutomatically(true);
webset.setSupportMultipleWindows(true);
webset.setAppCacheEnabled(true);
webset.setLoadsImagesAutomatically(true);
webset.setAppCachePath(w.getContext().getCacheDir().getAbsolutePath());
webset.setAllowFileAccess(true);
webset.setCacheMode(WebSettings.LOAD_DEFAULT);
w.setWebViewClient(new WebViewClient() {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
getLogger().debug(url + " caching...");
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
getLogger().debug(url + "cached");
}
});
w.setWebChromeClient(new WebChromeClient());
getLogger().debug("load Url");
w.loadUrl(URL_TO_CACHE);
}
}
At the FIRST-CACHE-TRY (app starts the first time) logcat looks like this:
startCaching()
load Url
http://myUrl.com caching...
After that i put the app in the background and resume it. So, at the SECOND-CACHE-TRY logcat looks like this:
startCaching()
loadUrl
http://myUrl.com caching...
http://myUrl.com cached
My question:
Why doesn't the webview invoke its method onPageFinished at the first try?
EDIT :
It works, if i invoke
new LibraryCacher().startCaching(this);
in onPageFinished of the other webview, which is visible to the user and loads simultaneously another url.
Does anyone know why?

Webviews in android seem to have some shared state. In my case, onPageFinished was never called, because i used:
#Override
public void onPause() {
webView.pauseTimers();
}
On a completely different webview, in a different activity.

Related

Android Webview: Disable confirm navigation

Is it possible to disable a confirm navigation pop up in android webview from a website that I am viewing?
I tried this one and I what I did is just return 'true' without showing any pop-up, but the navigation pop up still shows up. I want to disable it and I would like to just automatically navigate without any warning.
Here's the CustomWebChromeClient for my webview
public class CustomWebChromeClient extends WebChromeClient {
#Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return true;
}
}
You can override onJsBeforeUnload to always return true.
onJsBeforeUnload is the callback that gets invoked before the confirm navigation dialog pops up.
https://developer.android.com/reference/android/webkit/WebChromeClient.html#onJsBeforeUnload(android.webkit.WebView, java.lang.String, java.lang.String, android.webkit.JsResult)
Edit: As mentioned by AndyW, confirm or cancel methods should be invoked on jsResult argument otherwise the app might freeze because of the pending javascript dialog.
For me overriding onJsConfirm as followed did it:
#Override
public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
result.confirm();
return true;
}
Why don't you create a webView in xml and use that webview in activity instead of using a class with webChromeClient, the below steps may help you.
step 1:
Create a webview in your xml file and call this view in Java
webView = (WebView) findViewById(R.id.exampleWebView);
step 2:
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
step 3:
webView.loadUrl(getResources().getString(R.string.olo_non_loggedin_url));
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageStarted(WebView view, String url,
Bitmap favicon) {
super.onPageStarted(view, url, favicon);
pBar.setVisibility(View.VISIBLE);
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
pBar.setVisibility(View.GONE);
}
});

Android WebView onPageCommitVisible not fired

I have a WebView, which loads an html page from server, but the page is invisible on the WebView.
The html is loaded properly (I've debugged with chrome://inspect and the html, including all javascripts exist), but it is invisible on the phone screen.
There was no changes in my code, when this bug appeared. The bug appeared when I installed updates to Android System WebView on my phone.
If I uninstall the updates, all works properly again.
In addition, I've checked the callbacks of the WebViewClient and noticed that onPageCommitVisible is not called. So somehow, the page is not loaded properly. Only if I press the Back button, to exit the WebView, I see that the onPageCommitVisible is called for my webpage (buat it doesn't help, as the back button exists the WebView, as expected).
Here is my code for the webview:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout rlMain = new RelativeLayout(getActivity());
rlMain.setContentDescription(Constants.STARTAPP_AD_MAIN_LAYOUT_CONTENT_DESCRIPTION);
rlMain.setId(Constants.MAIN_LAYOUT_ID);
getActivity().setContentView(rlMain);
// Create WebView and set its parameters
try{
webView = new WebView(getActivity().getApplicationContext());
webView.setBackgroundColor(0xFF000000);
getActivity().getWindow().getDecorView().findViewById(android.R.id.content).setBackgroundColor(0x00777777);
webView.setVerticalScrollBarEnabled(false);
webView.setHorizontalScrollBarEnabled(false);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
// set software acceleration
if (softwareAcceleration) {
ApiUtil.setWebViewLayerTypeSoftware(webView, null);
}
webView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
return true;
}
});
webView.setLongClickable(false);
webView.addJavascriptInterface(createJsInterface(), Constants.INTERFACE);
setWebViewSpecificParameters(webView);
webView.loadDataWithBaseURL("http://www.xxxxxx.com", getHtml(), "text/html", "utf-8", null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
webView.setWebViewClient(new MyWebViewClient());
RelativeLayout.LayoutParams webviewPrms = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
);
rlMain.addView(webView, webviewPrms);
}
public void setWebViewSpecificParameters(final WebView webView) {
webView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return (event.getAction() == MotionEvent.ACTION_MOVE);
}
});
}
private class MyWebViewClient extends WebViewClient {
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
Logger.log(TAG, Log.DEBUG, "!!!!!shouldInterceptRequest" );
return super.shouldInterceptRequest(view, url);
}
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
Logger.log(TAG, Log.DEBUG, "!!!!!shouldInterceptRequest" );
return super.shouldInterceptRequest(view, request);
}
#Override
public void onPageFinished(WebView view, String url) {
setWebViewBackground(view);
runJavascript(Constants.JAVASCRIPT_SET_MODE_SERVER, getPosition());
runJavascript(Constants.JAVASCRIPT_ENABLE_SCHEME, "externalLinks");
InterstitialMode.this.onWebviewPageFinished();
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return clicked(url);
}
}
In an app that navigates from one website to another based on user input, there's a high chance (>50%) that the second website won't display at all after calling WebView.loadUrl and the previous website stays visible - it's even interactive, i.e. scrolling works. The problem is usually resolved after calling WebView.loadUrl again. There's no obvious indication of the bug occurring other than the user not seeing the second website. Relying on the user to reload the page manually is not a satisfying solution since the bug occurs quite often.
I was able to workaround this issue by using a custom WebViewClient to detect that the second website was not loaded properly and triggering a reload:
setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
buggyWebViewHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (!wasCommitCalled) {
loadUrl(url);
}
}
}, 2500);
}
}
#Override
public void onPageCommitVisible(WebView view, String url) {
wasCommitCalled = true;
}
});
Where buggyWebViewHandler is a Handler or any other class that allows deferring a piece of code for some time. Additionally, you'll need to set wasCommitCalled = false; whenever WebView.loadUrl is called - for example by overriding the method.
Note that this only works for Android 23 onwards because that's when onPageCommitVisible was added. See here for a full implementation: https://github.com/TomTasche/OpenDocument.droid/blob/8c2eec5c57e5962e9ac4c46549be2241b259eb32/app/src/main/java/at/tomtasche/reader/ui/widget/PageView.java#L72-L96
If anyone is brave enough to dig deeper into why this is happening: while debugging it seemed that onPageStarted is not called whenever this bug occurs. Maybe that helps...

WebView doesn't show content after onPause

I have a WebView inside a RecyclerView
I configured the WebViewClient to run onPuase() when page finished loading.
The problem is that some websites (like IMDB) are not viewed, unless I scroll the page down/up, or if the page in stored in cache.
Not working code:
getWebview().setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
view.onPause();
}
});
If I delay the onPause, it works (delay time differs between different devices)
getWebview().setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(final WebView view, String url) {
super.onPageFinished(view, url);
getWebview().postDelayed(new Runnable() {
#Override
public void run() {
getWebview().onPause();
}
}, 5000);
}
});
I also tried getWebview().postInvalidateDelayed() and getWebview().requestLayout().
Is there anyway to force the webview to display the loaded content, or simulate whatever happens when I scroll the page?
I use Lollipop with Android System WebView 43.0.2357.121
If you try to debug or put some logs in onPagefinshed() method, you will come to know that Webview's onPauuse() will call 2-3 times before site the
loads completely in case of URL redirecting.

Android: How to check for successful load of url when using webview.loadUrl

In webview android I am trying to load a url and in order to check if the load of this url is done successfully (internet connection was available, the server was up etc) I was under the impression that webview.loadUrl would throw exceptions, but wrong! as it explicitly is stated in here "an exception will NOT be thrown".
So how can I check to see if webview.loadUrl did not fail ?
Unfortunately, currently there is no easy way in WebView to ensure that everything on the page has been loaded successfully. We are hoping for a better API to come up in future version. Let me explain what you can do now.
First of all, in order to detect any problems that prevent WebView from making a connection to the server for loading your main page (e.g. bad domain name, I/O error, etc.), you should use WebViewClient.onReceivedError callback as other people correctly suggest:
public class MyWebViewClient extends WebViewClient {
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
// Make a note about the failed load.
}
}
myWebView.setWebViewClient(new MyWebViewClient());
If the server connection was successful, and the main page was retrieved and parsed, you will receive WebView.onPageFinished callback, so you also need to have this in your WebViewClient subclass:
public class MyWebViewClient extends WebViewClient {
...
#Override
public void onPageFinished(WebView view, String url) {
// Make a note that the page has finished loading.
}
...
}
The caveat here is that if you have received an HTTP error from the server (e.g. a 404 or a 500 error), this callback will be called anyway, it's just the content that you will get in your WebView will be a server error page. People suggest different ways of how to deal with it, see the answers here: How can I check from Android WebView if a page is a "404 page not found"? Basically, it really depends on what you expect to be a "good" page and a "error" page. Unfortunately, there is currently no way for the app to get the HTTP response code from WebView.
The callbacks WebViewClient.onPageStarted and WebViewClient.onProgressChanged are only useful if you want to draw a progress bar as you are loading the page.
Also note that the way of overriding WebViewClient.shouldOverrideUrlLoading that people usually suggest is not correct:
public class MyWebViewClient extends WebViewClient {
...
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// !!! DO NOT DO THIS UNCONDITIONALLY !!!
view.loadUrl(url);
return true;
}
...
}
What few developers realize is that the callback is also called for subframes with non-https schemes. If you'll encounter something like <iframe src='tel:1234'>, you will end up executing view.loadUrl('tel:1234') and your app will show an error page, since WebView doesn't know how to load a tel: URL.
It is recommended to simply return false from the method, if you want WebView to do the loading:
public class MyWebViewClient extends WebViewClient {
...
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Returning 'false' unconditionally is fine.
return false;
}
...
}
This doesn’t mean you should not call WebView.loadUrl from shouldOverrideUrlLoading at all. The specific pattern to avoid is doing so unconditionally for all URLs.
public class AppWebViewClient extends WebViewClient {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// TODO Auto-generated method stub
super.onPageStarted(view, url, favicon);
setProgressBar(true);
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
#Override
public void onPageFinished(WebView view, String url) {
//Page load finished
super.onPageFinished(view, url);
setProgressBar(false);
}
}
and then you can do
webView.setWebViewClient(new AppWebViewClient());
For the error part you can override the onReceivedError method
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
// TODO Auto-generated method stub
super.onReceivedError(view, errorCode, description, failingUrl);
}
Here is what I came up with, it works like a charm.
Boolean failedLoading = false;
WebView webView = view.findViewById(R.id.webView);
webView.loadUrl("www.example.com");
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (!failedLoading) {
webView.setVisibility(View.VISIBLE);
webView.setAlpha(0f);
ObjectAnimator anim = ObjectAnimator.ofFloat(webView, "alpha",1f);
anim.setDuration(500);
anim.start();
}
}
#Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
failedLoading = true;
}
});
It will also work great if you add some kind of a refresh button and then you can call the code above inside a function to try again.
You can check if a URL is loaded successfully by using onProgressChanged()
mWebView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
progressBar.setProgress(progress);
if (progress == 100) {
//your url is loaded successfully
}
}
});

Android detect webview URL change

I have a webview in my android app and would like to detect when the url changes.
I want to use this to hide the info button in the top bar when the user is on the info.php page and show it again when he is not on the info.php page.
I googled but can't find any working code, can anybody help me?
I know I'm late to the game but I ran into this issue over and over ... I finally found a sloution which is pretty straight forward. Just override WebViewClient.doUpdateVisitedHistory
override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
// your code here
super.doUpdateVisitedHistory(view, url, isReload)
}
It works with all url changes even the javascript ones!
If this does not make you happy then I don't know what will :)
You can use WebViewClient.shouldOverrideUrlLoading to detect every URL changes on your WebViewClient
For those load url by javascript. There is a way to detect the url change by JavascriptInterface. Here I use youtube for example. Use JavaScriptInteface has async issue, luckily its just a callback here so that would be less issue. Notice that #javascriptinterface annotation must be existed.
{
youtubeView.getSettings().setJavaScriptEnabled(true);
youtubeView.setWebViewClient(mWebViewClient);
youtubeView.addJavascriptInterface(new MyJavaScriptInterface(),
"android");
youtubeView.loadUrl("http://www.youtube.com");
}
WebViewClient mWebViewClient = new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:window.android.onUrlChange(window.location.href);");
};
};
class MyJavaScriptInterface {
#JavascriptInterface
public void onUrlChange(String url) {
Log.d("hydrated", "onUrlChange" + url);
}
}
if you override this method of WebViewClient you will be able to handle url changes, and javascript url changes:
public class YXWebViewClient extends WebViewClient {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.i("Listener", "Start");
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.i("Listener", "Finish");
}
}
and then in your WebView set the WebViewClient
yxWebViewClient = new YXWebViewClient();
webview.setWebViewClient(yxWebViewClient);
I used this method:
webView.setWebChromeClient(new WebChromeClient()
{
#Override public void onProgressChanged(WebView view, int newProgress) {
if (view.getUrl().equals(mUrl))
{
}
else
{
mUrl = view.getUrl();
// onUrlChanged(mUrl) // url changed
}
super.onProgressChanged(view, newProgress);
}
};
);
mUrl is a fields as String...
Try to use onLoadResource. It will be called at least 1 time even if you are using
JS to change your url. But it may be called more than one time, so be careful.
Method onPageFinished seems to work at least in modern WebViews.
An implementation of rinkal-bhanderi's answer would look like this:
class MyWebClient(
private val myCustomMethod: () -> Unit) : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?,
url: String?): Boolean {
myCustomMethod()
return true
}
}
}
And you would use it like:
webview.webViewClient = MyWebClient(::hideButton)
Where webview is the actual webview you get from calling FindViewById or the object reference you get from binding. And hideButton is a method accessible in the same scope last expression is evaluated.
I had an interesting problem that brought me to this question and happy to provide a solution that may help someone else if they come this way due to a googlebingyahoo search.
mwebVew.reload() would not pick up the new URL I wanted to load once I returned from a preference screen where I either logged on the user or logged off the user. It would update the shared preferences well enough but not update and replace the url web page. So after a few hours of not seeing an answer I tried the following and it worked a real treat.
mWebView.resumeTimers();
Here is the link to the doco if you want more info.
Hope it saves someone else the pain I went through, and happy to have someone tell me I should of implemented it another way.
I had the same problem. So i've solved this problem by overriding public void onPageStarted(WebView view, String url, Bitmap favicon) method as follows:
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
{
// Here you can check your new URL.
Log.e("URL", url);
super.onPageStarted(view, url, favicon);
}
});
Also you can overridepublic void onPageFinished(WebView view, String url) method to get a new URL at the end of page load process.
My requirement was to handle the visibility of a floating button in my page based on the url change.
if url is changing from https/ooooo//login to https/ooooo//homepage
it may not be registered in onPageFinished or shouldOverrideUrlLoading.
This was the issue I was facing.
I fixed that issue by following method:
#Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
super.doUpdateVisitedHistory(view, url, isReload);
String currentUrl = view.getUrl();
}
*please don't use url which we are getting with doUpdateVisitedHistory method.That will be the basic url.so go with view.getUrl().
:)
Use WebViewClient.shouldOverrideUrlLoading() when the base/host url changes. Use Use WebViewClient.doUpdateVisitedHistory() method of webview if the base url doesnt change like it happens in single page applications and it detects URL change by javascript which does not lead to a web page reload. #Helin Wang
This will help to detect redirect caused by javascript.
WebViewClient mWebViewClient = new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:window.android.onUrlChange(window.location.href);"); }
};
myWebView.setWebViewClient(mWebViewClient);
}
#JavascriptInterface
public void onUrlChange(String url) {
Toast.makeText(mContext, "Url redirected",Toast.LENGTH_SHORT).show();
}

Categories

Resources