WebView not working consistently - android

Edit: Strangely works when duplicating the loadUrl() line
I'm working with WebViews to gather data from a webpage. Sometimes, the webview just does nothing. I tried many proposes, but actually, none of them works....
Strange: webview.loadUrl("url...") doesn't work, but as soon as I call it twice, it works...
checkLogin()
void checkLogin(final Context context) {
Log.d("checkLogin()", "Begin of checkLogin()");
WebView webview = returnNewWebView(context, false);
webview.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url) {
Log.d("url", url);
// GOOGLE.COM ACTUALLY WORKS!
if(url.equals("https://www.google.com/"))
return;
Log.d("checkLogin()", "Loading of checkpage finished");
if(url.contains("loginto.php")) {
Log.d("checkLogin()", "User is logged out");
OnLoginCheckListener.onLoginCheckLoggedOut();
} else {
Log.d("checkLogin()", "User is logged in");
OnLoginCheckListener.onLoginCheckLoggedIn();
}
}
});
String id = sharedPref.getString("id", null);
String transid = sharedPref.getString("transid", null);
if(TextUtils.isEmpty("id") || TextUtils.isEmpty("transid")) {
Log.d("checkLogin()", "No login found, starting LoginActivity()");
Intent myIntent = new Intent(context, LoginActivity.class);
context.startActivity(myIntent);
} else {
Log.d("checkLogin()", "Probably logged in, checking by loading startpage");
webview.loadUrl("https://www.google.com");
// google actually works -.-
webview.loadUrl("https://my.login-page.com/index.php?pageid=1&id=" + id + "&transid=" + transid);
}
}
returnNewWebView()
private WebView returnNewWebView(Context context, Boolean JSInterface) {
// Prepare a webview
WebView WebView = new WebView(context);
if(JSInterface) {
WebView.getSettings().setJavaScriptEnabled(true);
WebView.addJavascriptInterface(new JavaScriptInterface(), "AndroidInterface");
}
// We don't need images for data scraping...
WebView.getSettings().setLoadsImagesAutomatically(false);
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
// Allow cookies
CookieManager.getInstance().acceptCookie();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().acceptThirdPartyCookies(WebView);
}
// Prevent webview not loading
// or try it...
WebView.clearCache(true);
WebView.destroyDrawingCache();
return WebView;
}
As you see, I already tried some stuff in order to prevent it but still, it doesn't work. In that cases, as soon I'm going to put the loadUrl() / postUrl()twice, it works.
What can I do that it works in every single case?
I would appreciate any tip!
Thank you very much in advance
logcat (non-filtered)
I actually see this message then..
03-09 07:36:19.061 4111-24251/? E/ctxmgr: [ProducerActiveIntervalImpl]closeActiveInterval: Error: ongoing, trying to close
and this one:
03-09 07:36:28.864 29862-3249/? E/accs.GcmPush: initializeApp occur error!
java.lang.IllegalStateException: FirebaseApp name [DEFAULT] already exists!
at iv.a(Unknown Source)
at com.google.firebase.FirebaseApp.a(Unknown Source)
at com.google.firebase.FirebaseApp.a(Unknown Source)
at org.android.agoo.gcm.GcmRegister$1.run(GcmRegister.java:32)
and this one too:
E/libEGL: validate_display:99 error 3008 (EGL_BAD_DISPLAY)
But I can't tell you if they are fired by the app....

I have gone through the full code for so long and pointed out this :
You are using this -
webview.loadUrl("https://www.google.com");
// google actually works -.-
webview.loadUrl("https://my.login-page.com/index.php?pageid=1&id=" + id + "&transid=" + transid);
in your checkLogin() method which is simply wrong as because one webview can not display the two urls at same time (as you mentioned it you want to achieve that).
Now according to your code the loading of the first url is overridden by the second url.
In order to achieve the loading of two urls in same webview try to implement this using :
Threads or some sessionTimeout methods.
OR
You can achieve this by using okhttp package.
Hope this helps!

onPageFinished is called when the webpage finished loading, but not after all the javascript on the page finished running. Depending on what the page is doing and what you are trying to achieve, it might be that your code is sometimes being executed before some essential javascript is executed on that page.
It's not a pretty solution but try adding some delay (like a Handler's postDelayed() or SystemClock.sleep() - but never on the UI thread!) to your code to see if that is indeed your problem.

Related

Get ANR dialog when open links from WebView in Chrome custom tabs. How do I debug this?

I want to use Chrome custom tabs to properly handle URLs out of my domain.
Here is the code
webView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request){
String url = request.getUrl().toString();
if(url.startWith("http://my.domain.name"))
return false;
else{
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setToolbarColor(getResources().getColor(R.color.colorPrimary));
builder.setStartAnimations(getActivity(), R.anim.slide_in_right, R.anim.slide_out_left);
builder.setExitAnimations(getActivity(), R.anim.slide_in_left, R.anim.slide_out_right);
Intent actionIntent = new Intent(
getApplicationContext(), ActionBroadcastReceiver.class);
actionIntent.setData(Uri.parse(url));
PendingIntent menuItemPendingIntent =
PendingIntent.getBroadcast(getApplicationContext(), 0, actionIntent, 0);
builder.addMenuItem(getString(R.string.action_share), menuItemPendingIntent);
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(getActivity(), Uri.parse(url));
return true;
}
}
});
However when I click on the out-of-domain URLs, occasionally I get ANR dialog and UI of the app is freezing.
I try to debug the anr traces but I found no suspect thread.
The ANR traces file is quite long, so I posted it here:
https://gist.github.com/hoavt-54/42f1109c0619eed81e82a9a8d1128a6d
If you have any suggestion on how to debug the app or how to understand the trace file, I would really appreciate that.
The 5 seconds rule is not relevant - Even 1 second blocking the main thread will appear to the user as a stuck app.
The amount of work the system does in the background and in the foreground for your app all the time is huge, and it's all being done on the main thread. So when you don't return immediately from any callback (onXyz()) - the whole world is blocking waiting for you, nothing will be drawn, no touch event will arrive, etc.
So never do any network call on the main thread. It will always cause ANR at least on some devices some of the time, for example when their network is off.
Never do any of these on the main thread:
read/write to the local file system, including properties and database
heavy calculations
network
long running/busy loops of any kind
All the above will cause ANR's randomly for users.
I would suggest you take an advantage of StrictMode atleast in Debug mode. Use below code to get logs of any issue which slows down your App on main thread.
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
You can set different penalties -
penaltyLog() // to print log
penaltyDeath() // This will crash you App(so costly penalty)
penaltyDialog() // Show alert when something went lazy on Main thread
There is so much about https://developer.android.com/reference/android/os/StrictMode.html
As per your code you are overriding Webview url to move to custom chrome tabs.
As per my understanding of webview it gives a callback in chrome thread, so ideally you should run your code in activity using callback and handler with activity main thread.
#Override
public void retainOldWebView() {
if (mmtWebViewPop != null) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (mmtWebViewPop != null) {
webViewFrameLayout.removeView(mmtWebViewPop);
mmtWebViewPop = null;
}
//TODO: write your own implementation here
}
}, 100);
}
}
To debug webview I would suggest to verify logs with chromium tags or while doing such experiments enable remote debugging
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
I as a developer would not want to mix up the implementation of both in one single activity, even if I want to I would rather create separate fragments for customChromeTabs and WebView and replace fragments whenever necessary. Because both of them work as a separate entity and shouldn't mix up the implementation for both.
Please share the code or implement something like below :-
WebViewActivity intercept requests and if url pattern doesn't match give a callback and start new ChromeTabActivity with url passed in intent bundle.
ChromeTabActivity where your code for customChromeTab should be present and work independently of each other and finish to resume webview from where you left of.
But I never used ChromeTabActivity for the similar cases, I have a use cases where fb and google login weren't working for me so I opened the same in another webview in frameLayout, which is working fine for my usecases. As soon as the pageload is completed I call this function.
if (null != mmtWebView) {
mmtWebView.setWebChromeClient(new WebChromeClientImpl(getApplicationContext()));
}
class WebChromeClientImpl extends WebChromeClient {
private final Context appContext;
public WebChromeClientImpl(Context appContext) {
this.appContext = appContext;
}
#Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
mmtWebViewPop = new WebView(view.getContext());
mmtWebViewPop.setVerticalScrollBarEnabled(false);
mmtWebViewPop.setHorizontalScrollBarEnabled(false);
mmtWebViewPop.setWebViewClient(new WebViewClientImpl(appContext, null));
mmtWebViewPop.getSettings().setJavaScriptEnabled(true);
mmtWebViewPop.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
webViewFrameLayout.addView(mmtWebViewPop);
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(mmtWebViewPop);
resultMsg.sendToTarget();
return true;
}
#Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
return super.onConsoleMessage(consoleMessage);
}
#Override
public void onCloseWindow(WebView window) {
super.onCloseWindow(window);
retainOldWebView();
}
}

Android webview - shouldOverrideUrlLoading() not called in some jsp pages

I've been developing hybrid apps for many companies with mobile websites.
And as a matter of fact, there are some websites made with using jsp.
I already had the knowledge that iframes and javascripts xhr requests will not fire webViewClient's shouldOverrideUrlLoading override function. I'm fine with that.
But today I learned that SOME actions such as:
JSP Page Redirects
Link Clicks within a JSP page
JSP/JS induced URL Loads
will not ALWAYS fire this function.
Hence, shouldOverrideUrlLoading() does not fire, when the webView is asked to load a page that it cannot load(i.e. "intent://...",) it shows an error page.
Has anyone encountered this kind of behaviour and is there any solution to work around it ?
Below is the code I'm using to invoke activities, where urls with 'intent:' protocol (which will fail because this function never gets called when above actions are performed)
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// ... omitted ...
if ( url.startsWith("intent:") ) {
Intent intent = null;
try {
intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
// The following flags launch the app outside the current app
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
try {
getActivity().startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
return true;
}
}
ps. please notice that every other websites' page loads will perfectly call shouldOverrideUrlLoading().
I couldn't find any JSP related bugs on android webViews so I'm asking one.
ps. I am happily willing to provide sample websites that some gracious readers will try on.. but the website's written in Korean so I doubt it will help.
Thank you!
Your problem might not related to JSP, the real problem may be shouldOverrideUrlLoading() itself. In this case, using shouldOverrideUrlLoading() may not be a good idea, so why not try another perspective?
I've encountered many problems when using
shouldOverrideUrlLoading() loading XmlHttpRequest. At the end, I
came up with the idea using onProgressChanged() and it solved all
my problems. I've written a similar answer here.
I tried adding your code into my own webview project and tested it with some JSP sites, and looks like it always work. I also added loadUrl() after other activities are invoked, so after pressing the back button, the loading error page will not be displayed again. So try this one :
First declare a global variable to store last URL.
String strLastUrl = null;
Then override onProgressChanged(WebView view, 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{
String strOldUrl = null;
//save old url to variable strOldUrl before overwriting it
strOldURL = strLastUrl;
//a new page was loaded,overwrite this new url to variable
strLastUrl = StrNewUrl;
if ( strLastUrl.startsWith("intent:") ) {
Log.d("TAG", "intent triggered");
Intent intent = null;
try {
intent = Intent.parseUri(strLastUrl, Intent.URI_INTENT_SCHEME);
// The following flags launch the app outside the current app
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
//reload the page before invoking other activities
view.loadUrl(strOldURL);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
}
super.onProgressChanged(view, progress);
}
});

What is considered onPageFinished in the Android WebViewClient?

I am trying to perform an action on an Android WebView after my webpage finishes loading in it. I setup my WebView to have a WebViewClient to make use of the onPageFinished event callback. However, after some testing, it does't seem to wait until all the JS on the page is done loading before my onPageFinished code fires.
The Google documentation says this:
public void onPageFinished (WebView view, String url)
Added in API level 1
Notify the host application that a page has finished loading. This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet. To get the notification for the new Picture, use onNewPicture(WebView, Picture).
Parameters
view The WebView that is initiating the callback.
url The url of the page.
1) Does onPageFinished only wait for the DOM to load?
2) Is there a way to detect when any JS on the page finishes? If so, what should I use?
I don't see anything in WebViewClient that would be for that purpose. I don't want to add a delay since my users can be on EDGE or on LTE.
You need to implement the callbacks from WebChromeClient. The onPageFinished() is an API that is provided by WebViewClient. There is yet another interface named WebChromeClient that provides the progress information you are seeking:
http://developer.android.com/reference/android/webkit/WebChromeClient.html#onProgressChanged(android.webkit.WebView, int)
Open the link above and look for onProgressChanged(WebView view, int newProgress) - the 'newProgress' variable gives you the percentage of page load that was completed. When it reaches 100 you have a valid page. onPageFinished() cannot be reliably used for this (due to server side redirections etc)
I don't know what you mean by "when JS on the page is finished". Maybe you can clarify what you meant?
From:
https://chromium.googlesource.com/chromium/src.git/+/master/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
#Override
public void didFinishNavigation(final String url, boolean isInMainFrame, boolean isErrorPage,
boolean hasCommitted, boolean isSameDocument, boolean isFragmentNavigation,
Integer pageTransition, int errorCode, String errorDescription, int httpStatusCode) {
...
if (client != null && isFragmentNavigation) {
client.getCallbackHelper().postOnPageFinished(url);
}
}
#Override
public void didFailLoad(
boolean isMainFrame, int errorCode, String description, String failingUrl) {
AwContentsClient client = mAwContentsClient.get();
if (client == null) return;
String unreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl();
boolean isErrorUrl =
unreachableWebDataUrl != null && unreachableWebDataUrl.equals(failingUrl);
if (isMainFrame && !isErrorUrl && errorCode == NetError.ERR_ABORTED) {
// Need to call onPageFinished for backwards compatibility with the classic webview.
// See also AwContents.IoThreadClientImpl.onReceivedError.
client.getCallbackHelper().postOnPageFinished(failingUrl);
}
}
#Override
public void didStopLoading(String validatedUrl) {
if (validatedUrl.length() == 0) validatedUrl = ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL;
AwContentsClient client = getClientIfNeedToFireCallback(validatedUrl);
if (client != null && validatedUrl.equals(mLastDidFinishLoadUrl)) {
client.getCallbackHelper().postOnPageFinished(validatedUrl);
mLastDidFinishLoadUrl = null;
}
}
We can easily see that onPageFinished is not pretty much what you're expecting.
To answer your first question: I have found that onProgressChanged will not reach 100, and onPageFinished will not be called until all of the assets (css/js/images) have finished loading for that page.
I can not, however, find any official documentation that states that.

WebView.loadUrl(url) does nothing

I recently wrote up a simple Twitter app for Android to learn the ropes of the Twitter API and OAuth.
The app's main activity simply asks for a username to follow. It then calls another activity which handles the OAuth & Twitter API calls. It redirects the user to an authorization page, which then returns to the app after the user finishes.
It used to work just fine, but now for some reason when I call webview.loadUrl(authorizationURL), NOTHING happens. I never changed anything that would affect the WebView stuff though... Here's my code:
#Override
public void onResume() {
super.onResume();
try {
if(weNeedCredentials()) {
obtainCredentials();
}
follow(mUsername);
} catch (OAuthException oae) {
// omitted
}
}
private boolean weNeedCredentials() {
// assume the method returns true
}
private void obtainCredentials() {
final Token requestToken = mOauthService.getRequestToken();
String authUrl = mOauthService.getAuthorizationUrl(requestToken);
// I verified that authUrl is the correct url (and != null)
final WebView oauthView = (WebView) findViewById(R.id.oauthview);
oauthView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(someCondition) {
oauthView.setVisibility(View.GONE);
doOtherStuff();
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
oauthView.getSettings().setJavaScriptEnabled(true);
oauthView.loadUrl(authUrl);
}
Here's my layout xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<WebView
android:id="#+id/oauthview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
and also I included the proper Internet permissions in the Manifest.
A WebViewCoreThread is running for the life of the app, and a WebViewWorkerThread pops in later, but no WebView ever comes up (not even a white screen). The app never blocks either. It continues running as if the loadUrl() line were simply commented out.
I've tested on my phone (Droid X2) as well as an emulator, both with the same results. Any help would be greatly appreciated.
Thanks in advance!
It continues running as if the loadUrl() line were simply commented out.
It will always "continue running as if the loadUrl() line were simply commented out". loadUrl() is asynchronous and does not block.
Off the cuff, either:
weNeedCredentials() is returning false, or
follow() is replacing the UI, or
Twitter is doing a redirect, and someCondition is true, so you are making the WebView be GONE right away
there are issues with the URL that you are loading

WebView.capturePicture failing with url parameters

[Android Newbie alert]
I need to capture the contents of a WebView in a BitMap and I've run into a strange problem. My approach is to register a WebViewClient with the WebView, and in onPageFinished I call capturePicture. With a simple URL (e.g. http://www.yahoo.com), it works fine. In other cases, capturePicture returns a Picture with height and width values = 0. The page loads fine, either way. The actual url I have to use has quite a few url parameters and I initially thought having any parameters was the problem, but that's not the case. Here's a few sample urls with comments indicating whether it works or not:
w.loadUrl("http://www.yahoo.com"); //yes
w.loadUrl("http://search.yahoo.com/search?p=android"); // usually not???
w.loadUrl("http://www.yahoo.com?foo=bar"); // nope
w.loadUrl("http://www.google.com"); // yep
w.loadUrl("http://www.google.com?q=android"); // yep
w.loadUrl("http://www.google.com?foo=bar"); // yes
The second case is particularly frustrating as it appears to not work. However, if I run the test app with #5 first, then switching the url to #2 and running it then works.
Here's a snippet of an actual simplified test I created:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
w = new WebView(this);
w.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView webview, String url) {
Picture picture = webview.capturePicture();
Log.d("Height", "" + picture.getHeight());
Log.d("Width", "" + picture.getWidth());
Bitmap b = Bitmap.createBitmap(picture.getWidth(), picture
.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
picture.draw(c);
}
});
w.getSettings().setJavaScriptEnabled(true);
setContentView(w);
//w.loadUrl("http://www.yahoo.com"); //yes
w.loadUrl("http://search.yahoo.com/search?p=android"); // usually not???
//w.loadUrl("http://www.yahoo.com?foo=bar"); // nope
//w.loadUrl("http://www.google.com"); // yep
//w.loadUrl("http://www.google.com?q=android"); // yep
//w.loadUrl("http://www.google.com?foo=bar"); // yes
}
Has anyone run into this issue? Hopefully I'm just being an idiot and there's a simple solution or workaround?
I just visited the documentation page again.
[here][1]
"Notify the host application that a page has finished loading. This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet. To get the notification for the new Picture, use onNewPicture(WebView, Picture)."
Used Picture Listener, I tried with your sample and it works.
Hope this helps.
I just added below code to your example and removed WebViewClient
w.setPictureListener(new PictureListener(){
public void onNewPicture(WebView view, Picture picture) {
Log.d(TAG, "onNewPicture- Height"+ picture.getHeight());
Log.d(TAG, "onNewPicture- Width"+ picture.getWidth());
}
});
[1]: http://developer.android.com/reference/android/webkit/WebViewClient.html#onPageFinished(android.webkit.WebView, java.lang.String)

Categories

Resources