I am showing a programmatically created webview in a bottomsheet fragment. The webview is persisted on a singleton that outlives the fragment
The webview is created like so:
webView = WebView(context).let { webView ->
webView.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
webView.clearCache(true)
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.measure(100, 100)
webView.settings.useWideViewPort = true
webView.settings.loadWithOverviewMode = true
webView.addJavascriptInterface(AndroidJavascriptInterface(environment), webViewHook)
webView.webViewClient = webViewClient
webView.webChromeClient = webViewChromeClient
webView.loadUrl("file:///android_asset/snippet.html")
webView
}
and then present the webview in a bottomsheet fragment like so:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (webController == null) {
// activity restarted without original context
dismiss()
} else {
webController?.webView?.visibility = View.VISIBLE
webController?.let {
fragment_web_view?.addView(webController?.webView)
}
}
}
I observe these undesirable behaviors:
The webview's network requests all get stuck at pending when being backgrounded for around 2 min
If I persist the webview using a singleton, the webview stops making network request after the webview is presented and then dismissed
If I never present the fragment, the webview works fine no matter how long I wait
Requests start normal but stays pending after a while
But in these two scenarios the webview works fine.
If I keep the webview on screen all requests work fine.
If I keep presenting and dismissing the webview, all requests works fine.
Is there something that I don’t understand about the android webview that may have caused this issue? In order to refresh the webview, I need to reinitialize it to make the requests work again.
Looks like this is an issue with lates version of WebView. Can you open play market on the device find Android System WebView and try to downgrade it. Form me everything works fine on version 74 and stuck in pending on version 99. Let me know if it works same for you or you already found a reason?
Related
I have a problem I have not yet been able to understand, nor solve.
On my android project, I have a BaseActivity. There, among other functions, I decided to add a function to show or hide a loading view when necessary. It works as intended, but sometimes an error happens.
I will try to raise some important info about my project I think can be useful. My app is integrated with an external login app. I call it when the services I call need to refresh it's token. When the user logs in on the app, it calls a listener and give me back control on mine.
The problem is the next one:
I come to an activity that needs to call a service and I have the token all right, but then I lock the phone. After a long period of time, I unlock the phone and, from the same activity, I call again the service. My activity shows de loader as intended and, as my token is expired, I call the login app from it's SDK.
When I come back to my app, and I call the service I wanted successfully, the app tries to hide the loader. This is where the fail comes, as I can't change the visibility to GONE. I looked for it on the view hierarchy and find it, but with visibility = VISIBLE.
Here is the piece of code from the loader, hope someone can find where I'm making the mistake!
abstract class BaseActivity : DaggerAppCompatActivity(){
// These are the IDS of the Views I'm adding to the activity, so I can track them and change their visibility
var imgLoadingID = -1
var rvLoadingID = -1
fun showLoading() {
// If the views are added I show them
if (imgLoadingID > 0 && rvLoadingID > 0) {
val imageView = findViewById<ImageView>(imgLoadingID)
val relativeLayout = findViewById<RelativeLayout>(rvLoadingID)
relativeLayout.visibility = View.VISIBLE
imageView.visibility = VISIBLE
imageView.isClickable = false
imageView.isFocusable = false
} else {
// else I create them and show them
val imgLoading = ImageView(this)
imgLoading.id = View.generateViewId()
imgLoadingID = imgLoading.id
val maxpx = CustomUtils.ViewUtils.converIntToDps(65, this)
Glide.with(this).asGif().load(R.mipmap.loading).into(imgLoading)
val relativeLayout = RelativeLayout(this)
relativeLayout.id = View.generateViewId()
rvLoadingID = relativeLayout.id
var params = RelativeLayout.LayoutParams(maxpx, WRAP_CONTENT)
params.addRule(RelativeLayout.CENTER_IN_PARENT)
relativeLayout.addView(imgLoading, params)
relativeLayout.background = getDrawable(R.color.pure_white_97)
relativeLayout.isClickable = true
relativeLayout.isFocusable = true
findViewById<ViewGroup>(android.R.id.content).addView(relativeLayout, RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
imgLoading.visibility = VISIBLE
}
// I lock the back button so people don't cancel my requests
esBackPressedBlocked = true
}
// Here I find the views and change their visibility.
fun hideLoading(){
if(imgLoadingID > 0 && rvLoadingID > 0) {
val imageView = findViewById<ImageView>(imgLoadingID)
val relativeLayout = findViewById<RelativeLayout>(rvLoadingID)
relativeLayout.visibility = View.GONE
imageView.visibility = View.GONE
}
esBackPressedBlocked = false
}
}
I deleted some logs I added to the whole function, but when it fails, it enters on the hideLoading() function, even on the relativeLayout.visibility = View.GONE part.
The function used to be with the Views as an whole object instead of their ids, but I found it more reliable this way, and saving the views instead of their Id's had the same problem.
My main concern is how Android manages my application while the phone is locked for this period of time (the fails happened after 8-10 hours of inactivity). I think something there can be creating this issue. I thought also about the external Login app, since it's sdk is launching their app's intent and calling me from a listener when coming back, but, since my code is being executed, I think Android it's managing my views on an strange way. Or maybe I try to hide the loading view before I'm on the resumed activity... I don't really know.
BTW, I know there are easier solutions on showing a loader, but I wanted to create it the cleanest way. If you have any cleaner approach I'm open to any solution.
If there is anything unclear let me know in the comments, and I hope my English was clear enough to express myself, it's a tricky problem that I can't understand, so it's difficult for me to explain it.
Thanks!!
My Website(use laravel) call window.open method on this code
window.open('My_URL','_blank');
and my Android Webview open new window on dialog. code is
override fun onCreateWindow(view:WebView, isDialog:Boolean,
isUserGesture:Boolean, resultMsg:Message):Boolean {
// set dialog webview
val dialog = Dialog(context)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setContentView(R.layout.activity_webview)
val newWebView = dialog.findViewById<WebView>(R.id.WebView)
newWebView.settings.javaScriptEnabled = true
newWebView.loadUrl(view?.url)
// open dialog full screen
val window = dialog.window
val wlp : WindowManager.LayoutParams = window.attributes
wlp.gravity = Gravity.CENTER
wlp.flags = WindowManager.LayoutParams.FLAG_BLUR_BEHIND
window.attributes = wlp
dialog.window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT)
dialog.show()
(resultMsg.obj as WebView.WebViewTransport).setWebView(newWebView)
resultMsg.sendToTarget()
newWebView.setWebViewClient(object: WebViewClient() {
})
newWebView.setWebChromeClient(object:WebChromeClient() {
override fun onCloseWindow(window:WebView) {
dialog.dismiss()
window.destroy()
newWebView.destroy()
//webview.removeView(newWebView)
}
})
return true
}
it was work successful just one time.
what should I do?
Not sure if it will help you or not, but as far as I know the android application should provide handling of opening and closing window requests from javascript. I mean it does work out of the box, but very often not how it is expected/needed.
It is possible to show separate web window as a dialog, but as I know dialogs can't stack and appear on top of each other. Such approach fits case when you need to open (and then close) only one separate window. Please read more about dialogs here.
What I suggest is to show separate WebView on every window.open() request. Please take a look at this answer, where similar solution was solved. In your case (where any amount of opened windows possible) you can use a LinkedList of WebViews, pushing new one on window.open() and popping top ones on window.close.
when I load some data in web view many times that white page appears ...just scroll it the data is return back..Am notice this issues on android 4.x ...I test it in android 2.3.3 webview work fine
what I can do to prevent this issues ?
I am facing a similar issue and finally solved but I am not sure why. Things that make it work was:
1)
When I finish with a WebView onDestroy of activity/fragment I have:
#Override
protected void onDestroy() {
super.onDestroy();
if (isFinishing()) {
if (myWebView != null) {
myWebView.destroy();
myWebView = null;
}
}
}
2)
I render the code via myWebView .loadDataWithBaseURL("file:///android_asset/", html_sourse, "text/html", "UTF-8", null); instead of myWebView.loadData(Uri.encode(html_sourse), "text/html", "utf-8");. When I am using the second line with an embedded css stylish inside html code page does not appear, changing to the first one (loadDataWithBaseURL) and adding to the html code a css from asset folder page appeared. I cannot understand why but works for me!
3) (optional)
Sometimes it is helpful to use clears if you do not care about keeping history:
myWebView.clearHistory();
myWebView.clearCache(true);
I've done alot of Googling on this and haven't found an answer. I have a webview as part of an overall layout with other controls. I use the webview to show formatted text (recipe descriptions with bold and italic fonts) and URLs. The URLs point to Youtube and are fully qualified (ie. http://www.youtube.com/watch?v=fyzyhuVgzRE). When I click on the link in my web view, the web view control itself disappears from the layout (leaving the rest of the controls on the page intact) and the default web browser does not launch.
It behaves as if I've set the visibility of the web view to GONE, but I do not manipulate the visibility of any of the controls on the layout.
One interesting clue in the LogCat output is an INFO level message from the OS:
MotionRecognitionManager .unregisterListener : / listener count = 0->0,
listener=android.webkit.ZoomManager$1#41ac2fc0
Any ideas what would cause this behavior?
EDIT
Here is how I setup the webview in the onCreate() method of the activity:
webView1 = (WebView)findViewById(R.id.webView1);
webView1.getSettings().setJavaScriptEnabled(true);
webView1.getSettings().setAllowFileAccess(true);
webView1.setWebViewClient(new MyWebViewClient());
And the MyWebViewClient class:
private class MyWebViewClient extends WebViewClient {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url != null && url.startsWith("http://")) {
view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
} else {
return false;
}
}
}
Oddly enough the shouldOverrideUrlLoading is never called.
Well, I'm a dope. It turns out that the double quotes in the href attribute of the anchor were doubled. My system integrates with a MySQL db over the web and I have to do alot of transformation of data between MySQL/PHP and Android/SQLite. During one of these transformations I double-escaped the quotes in the href. So what should have been:
Link Text
was instead rendered as:
Link Text
In the web view the resulting URL looked fine. It was underscored properly and looked like any other URL. It is odd though that simple doubling the quotes causes the behavior. I'll see if there is a known bug like this for Android. At the very least the WebView should through some kind of exception, not simply disappear from the screen.
I faced this type of behavior when webview tries to load content it cant handle. Intercept ref clicks using WebViewClient and send intent to open this ref in external application. Check this thread.
I have an android application that loads web pages in an activity with a WebView. I am using the retrieving the page manually and using WebView's loadDataWithBaseURL to display it on screen. Everything there is fine.
Now, i am trying to override the Back button press to simulate going back in the WebView history stack. I am able to override the Back button press, i can see that there is a history stack in the WebView, i can see that the history url is correct, but when i call WebView's goBack() method, it displays a blank page.
Anyone encountered this before or give me a couple of suggestions to proceed from this?
Edit: If i use WebView's loadUrl method, the Back button with an override works as intended. But why.... If i need to handle this manually, how do i start messing with history pages?
I got the same problem also. I found that the problem went away if I set the historyUrl parameter on the call to loadDataWithBaseURL.
You should check if the canGoBack() method returns true before calling goBack()
The only solution I've found is to create a Stack<String> and manually manage history
The way I deal with this is keeping a local stack pointer to the number of loaded pages after the root page loaded using loadDataWithBaseURL . When going back, if my pointer hits 1 I am at root level and reload the root page with loadDataWithBaseURL.
FYI, I use this code in Activities with fragments, so the fragments implement the interface IBackButtonListener which helps me to capture the back button in the main activity and propagate the event to the current fragment. If the fragment returns true it means it has taken care of the event.
IBackbuttonListener.java
public interface IBackButtonListener {
public boolean onBackButtonPressed();
}
Fragment that implements IBackButtonListener and has a webview loaded from html data.
private int historyStackPointer = 0;
...
#Override
public boolean onBackButtonPressed() {
boolean rtn = false;
if (webView.canGoBack()) {
if(historyStackPointer > 1) {
webView.goBack();
historyStackPointer--;
rtn = true;
}else{
if(historyStackPointer == 1) {
// Reload the html data
webView.loadDataWithBaseURL("file:///android_asset/", html_data, "text/html", "UTF-8", null);
historyStackPointer = 0;
rtn = true;
}else{
webView.loadUrl("about:blank");
rtn = false;
}
}
} else {
rtn = false;
}
return rtn;
}
html_data is a String with the page's html.
What I noticed is that if the url ends in .html, that white screen appears when back button is pressed.
On the other hand, if you remove that .html from your url - obviously only if this is supported by that website (i.e. the redirection and all is handled properly at the server side and that it doesn't trigger the 404 Page Not Found error), that url will act as your base this time and when you press the back button, that white screen should not appear this time.
for example: you have to replace
http://example.com/page.html to:
http://example.com/page