WebView fails to render until touched Android 4.2.2 [duplicate] - android

This question already has answers here:
Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy
(9 answers)
Closed 9 years ago.
I am developing an open-source browser called Lightning Browser, and I have run into some trouble with the most recent update to Android (4.2.2).
WebViews will fail to render completely until the view is touched. It only occurs on the latest Android version. On 4.2.1, the WebView rendered completely fine. I use my Nexus 7 for development and right after I received the 4.2.2 update, the browser stopped rendering. Other users experienced this as well, and it was confirmed multiple times to happen ONLY on 4.2.2. It is happening targeting API level 16 and 17, but I have seen a WebKit browser that targets API level 9 without this problem.
I have attempted to remedy this using the solution to a problem I found here on Stack Overflow (Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy). HOWEVER, setting the WebView's RenderPriority to high alone does not solve it... the only way to get it to render without being touched was to place the invalidate() command inside the OnDraw() method of WebView. This causes the WebView to re-draw continuously. This works (sort of), animations are smooth, the page loads very quickly, but it causes the WebView's performance to drop otherwise.
I have also seen this question (very similar to mine), but there is no good answer for it. Android WebView Fails to Completely Render Content Until User Interaction
By performance drop, I mean input. Text input lags, and the WebView itself cannot handle what is going on as well as before. Benchmarking the browser with the invalidate() method call drops Benchmarking performance by around 8%. I know benchmarks aren't everything, but it's telling me that the continuous drawing is straining the system and causing the system to ignore other tasks.
In Conclusion...
The WebView in Android 4.2.2 will not render until touched. The only way I know to fix this is to call invalidate() in the onDraw() method of WebView. This is bad for performance and I am looking for a different way to fix this.
The Magic Code (I'm currently using)...
class MyWebView extends WebView
{
#Override
onDraw(Canvas canvas)
{
invalidate();
super.OnDraw(canvas);
}
}
remove
invalidate();
and it doesn't render until touched.
Does anyone have a suggestions on how make the WebView render (other than what I've done)? By the way, this is my first question I've asked here on Stack, so forgive me if I haven't been clear or if I've done something wrong.
EDIT:
I found this question here about a similar issue and it was resolved, the problem is, I don't understand what the answer even means. If anyone could enlighten me it might help.
I get this error in logcat
E/chromium(1243): external/chromium/net/disk_cache/backend_impl.cc:2022: [0705/172030:ERROR:backend_impl.cc(2022)] Corrupt Index file

So I finally found the issue causing the WebView not to render. I am starting and stopping the animation of a drawable inside the WebViewClient of my WebView. This drawable is simply a refresh button that rotates when the page is loading... simple right?
Well in the non-fullscreen mode of the browser, the rotating drawable and the WebView are both children of the same parent, whereas in the fullscreen mode, the drawable becomes a child of the WebView. Somehow, by starting the animation when the page is loading and stopping it when it's done (in fullscreen), the application decides that the rotating drawable is what needs all the drawing power and the WebView never draws. When in fullscreen mode and the drawable becomes a child of the WebView, the WebView then has a higher rendering priority than the drawable and it draws fine.
The moral of the story is... WebViews like to be the highest priority view to be drawn. If there are other views that get greater priority, they won't draw correctly.
I'm no expert on animations, so I need to rethink how I'm animating the drawable now so as to not interrupt the WebView.
Hopefully that made sense. Thanks for reading.

Disable hardware acceleration on your manifest:
android:hardwareAccelerated="false"

please see the last answer by #Olivier on Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy , he triggers a couple delayed invalidates on WebView's OnTouchEvent rather than continuosly on onDraw ... In my case it worked because (most) of my problems were after a user touching webview and I changing some CSS in response. Of course it doesn't apply at all on the case of automatic / timeout'ed css
In my case it also helped to export a JavaScript function from Java to manually trigger invalidate with a delay, so if in JavaScript you more or less know where the disaster might happen you can manually trigger it, something like this inner class inside your WebView:
public class MyWebView extends WebView {
private class InvalidateExtension {
private Handler handler=new Handler(); // you might already have a handler
private Runnable mInvalidater=new Runnable() {
#Override
public void run() {
MyWebView.this.invalidate();
}
}
public void trigger(int delay) {
handler.postDelayed(mInvalidater, delay);
handler.postDelayed(mInvalidater, 2*delay); // just in case
handler.postDelayed(mInvalidater, 4*delay); // just in case just in case :)
}
}
/** Call this function on this view's init, BEFORE loading a page so it is available to JS */
private void call_me_on_init_to_enable_hack() {
addJavascriptInterface(new InvalidateExtension(), "Invalidater");
}
}
So, from JavaScript you can do:
Invalidater.trigger(100);
And play with the milliseconds value ....
Hope this helps someone !

There is a problem with the zoom scale when rotating the phone and that will cause this problem, parts or the whole page will not be drawn. To solve this override onScaleChanged in your WebViewClient implementation and invalidate the view. That should force a redraw when needed.
#Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
if (view != null) {
view.invalidate();
}
}

Related

Unity canvas scaler hiding animations in high resolution display

I instantiate the following gameObject, which contains an Animator with the mode "always animate" on, the animation goes for 340ms, after that time I destroy the gameObject.
The gameObject Inspector properties:
I instantiate it using the following code:
instancia = (Instantiate(cardAnimation, new Vector3(0, 0, 0), Quaternion.identity) as GameObject).GetComponent<Image>();
instancia.rectTransform.SetParent(transform);
StartCoroutine(KillOnAnimationEnd());
Here is the Coroutine:
private IEnumerator KillOnAnimationEnd()
{
yield return new WaitForSeconds(0.34f);
DestroyImmediate(instancia);
}
Here is how the animation looks like when simulating in Unity (PC-Windows):
But on android after I open the chest it waits 340ms with nothing happening and then show the information above, does this have something to do with the plataform or is some unity or perhaps code related issue?
NOTE: I also have another animation in another scene that is just a already instantiated gameObject in the Hierarchy with always animated on and it works on Android.
--EDIT--
So I have ran the newest version of the app in a emulator which is almost about 1080x480 and the animation showed just as the PC, also running on a 720p smartphone did the job, the only problem I'm still having is with my QuadHD resolution from Galaxy S6, everything else shows but the animation, I have even tried making the animation run without any script so it runs in a loop, but it doesn't show up in galaxy screen.
Given the news about the issue I think this might change a little bit the perspective of answers and perhaps help someone else solve the same problem in the future.
Okay, figured out the problem, its something to do with "rotation" in animations using Unity3D in 2D mode, gonna be reporting it form Unity so it is fixed.
The solution: Animate your UI only using scale/position, if used rotation it will not show on high resolution display.
I am pretty sure your WaitForSeconds(0.34f) is not working properly because there is no thing such as yield keyword in Java. I recommend you to use a invoke method instead to call your method that destroys your GameObject.

Android OpenGL gameloop outside onDrawFrame

I've got a problem with creating gameloop for my first game. I've read a lot about it but still can't figure it out. It's based on OpenGL so I've used onDrawFrame as a game loop and it works fine on my phone. Problem is that onDrawFrame is refresh time depends on hardware so it runs way too fast on some devices. So what I want is adding a separate game loop that will refresh itself at constant period of time on all smartphones. (and onDrawFrame will only take care of graphics as it should)
As for now I have:
myGameRenderer class with all openGl stuff an onDrawFrame
myGLSurfaceView that supports touch events
myGameActivity
onDrawFrame activates myGameUpdate function that controls changing positions of all objects in game depending on info from myGLSurfaceView
I've tried with creating new Runnable but it doesn't seem to work, I can't figure out how to start that runnable and where i should place it (I've tried to place it in myGameRenderer class, but it didn't seem to work, nothing was moving:
private final Runnable mUpdateDisplay = new Runnable() {
#Override
public void run() {
update();
}};
private void update() {
//some update stuff blablabla
//some update stuff blablabla
mHandler.postDelayed(mUpdateDisplay,40); //to refresh at 25 fps
}
but I guess I don't get the idea of it - I mean I create this runnable.
I've tried to place it in onCreateSurface to start it but no effect.
So - is the generall idea ok? And how to start the loop? Where to place it? Or should I use any other way?
Ok it was simple - I was just missing r.run();
But as allways there's something. Now it works as i wanted - I mean frames doesn't depend on hardware, but everything is not as smooth as it was - and part of objects in 3d are flickering. Seems like some objects visibly are drawn faster, some later and it looks ugly.
So what am I doing wrong? Is there a better way?

Zooming in/out on Android Webview without a set limit

In my current project I need my users to be able to scroll over and zoom in on large SVG
Images. A major problem i encountered though, is the limit the android WebView class puts on zooming in and out. Is there any way I can remove or change these limits to my own likings?
The standard zoom controls do not seem to support releasing these boundries.
If my question is unclear, or if I need to elaborate on my question do not hesitate to ask.
Greets,
Wottah
Since no one seems to have come up with a different solution than using reflection - I'm not aware of any alternatives at this point - I wrote up a quick code snippet that illustrates how to bypass the upper limit on the zoom-in action.
Note that the code below will only work on ICS, and possibly Honeycomb, but I currently don't have a tablet lying around to inspect if the inner workings rely on the same ZoomManager class. Gingerbread, Froyo and Eclair all appear to implement the zooming functionality more or less directly in the WebView class. With the example below it should be fairly easy to add some code to also take those operating systems into account.
// just set an Activity's content view to a single WebView for this test
WebView mWebview = new WebView(this);
setContentView(mWebview);
// retrieve the ZoomManager from the WebView
Class<?> webViewClass = mWebview.getClass();
Field mZoomManagerField = webViewClass.getDeclaredField("mZoomManager");
mZoomManagerField.setAccessible(true);
Object mZoomManagerInstance = mZoomManagerField.get(mWebview);
// modify the "default max zoom scale" value, which controls the upper limit
// and set it to something very large; e.g. Float.MAX_VALUE
Class<?> zoomManagerClass = Class.forName("android.webkit.ZoomManager");
Field mDefaultMaxZoomScaleField = zoomManagerClass.getDeclaredField("mDefaultMaxZoomScale");
mDefaultMaxZoomScaleField.setAccessible(true);
mDefaultMaxZoomScaleField.set(mZoomManagerInstance, Float.MAX_VALUE);

Progress bar while loading Textures in AndEngine

when I start my game black screen comes for a while because resources are being loaded.
I went thorough a tutorial which show how to show progress bar while leading resources I followed it and now I can see progress bar. But the problem is this when progress bar is visible every thing else is stopped. And nothing happens. Only a black screen and a progress bar on it. Can any one tell me why every thing is paused and why loadresources and loadscene methods are not working? Please provide a solution.
You need to load your resources in a worker thread. A nice utility for doing this is AsyncTask. The guide topic Processes and Threads has an explanation of why you need something like this, as well as sample code showing how to do a simple AsyncTask that might be just what you need.
From Engine.java:
public void onDrawFrame(final GLState pGLState) throws InterruptedException {
final EngineLock engineLock = this.mEngineLock;
engineLock.lock();
try {
engineLock.waitUntilCanDraw();
this.mVertexBufferObjectManager.updateVertexBufferObjects(pGLState);
this.mTextureManager.updateTextures(pGLState);
this.mFontManager.updateFonts(pGLState);
this.onUpdateDrawHandlers(pGLState, this.mCamera);
this.onDrawScene(pGLState, this.mCamera);
engineLock.notifyCanUpdate();
} finally {
engineLock.unlock();
}
}
And that's why the engine hangs and the UI gets stuck. It's possible to display a loading screen while the textures are being loaded into hardware, without freezing, let's say, a ProgressBar. It's not easy and requires a lot of code, but it's possible and doesn't require crazy hacks, just some logic.
You need to have a Resources Manager (RM) and a Scene Manager (SM) who work together (with AsyncTasks) and are responsible for loading the textures for the current scene. Since you've a BaseGameActivity you can use this Activity instance to show a fullscreen Dialog with the progress bar. The logic is:
SM is asked to show Scene A
SM shows the loading Dialog
SM asynchronously tells the RM to load all Scene A resources into hardware (for each texture for Scene A, texture.load)
RM "onSceneTexturesLoadComplete" tells SM that all textures are loaded
Since texture.load doesn't guarantee the texture is actually loaded you'll need to have a TryToDismissDialog that extends TimerTask. This TryToDismissDialog from time to time will query the Scene A textures and check if they are actually loaded:
if (texturePack.getTexture().isLoadedToHardware()) {
texturesLoaded++;
}
If all textures are loaded you dismiss the Dialog and voilá you'll see the Scene ready.
Hope it helps
ps: this actually involves some lines of code, I've just posted here a quick steps/guide/pseudo-code. I'll not post the final solution because it's quite heavy and "project related".

Preventing "flickering" when calling Drawable.draw()

I have a little experimentation app (essentially a very cut-down version of the LunarLander demo in the Android SDK), with a single SurfaceView. I have a Drawable "sprite" which I periodically draw into the SurfaceView's Canvas object in different locations, without attempting to erase the previous image. Thus:
private class MyThread extends Thread {
SurfaceHolder holder; // Initialised in ctor (acquired via getHolder())
Drawable sprite; // Initialised in ctor
Rect bounds; // Initialised in ctor
...
#Override
public void run() {
while (true) {
Canvas c = holder.lockCanvas();
synchronized (bounds) {
sprite.setBounds(bounds);
}
sprite.draw(c);
holder.unlockCanvasAndPost(c);
}
}
/**
* Periodically called from activity thread
*/
public void updatePos(int dx, int dy) {
synchronized (bounds) {
bounds.offset(dx, dy);
}
}
}
Running in the emulator, what I'm seeing is that after a few updates have occurred, several old "copies" of the image begin to flicker, i.e. appearing and disappearing. I initially assumed that perhaps I was misunderstanding the semantics of a Canvas, and that it somehow maintains "layers", and that I was thrashing it to death. However, I then discovered that I only get this effect if I try to update faster than roughly every 200 ms. So my next best theory is that this is perhaps an artifact of the emulator not being able to keep up, and tearing the display. (I don't have a physical device to test on, yet.)
Is either of these theories correct?
Note: I don't actually want to do this in practice (i.e. draw hundreds of overlaid copies of the same thing). However, I would like to understand why this is happening.
Environment:
Eclipse 3.6.1 (Helios) on Windows 7
JDK 6
Android SDK Tools r9
App is targetting Android 2.3.1
Tangential question:
My run() method is essentially a stripped-down version of how the LunarLander example works (with all the excess logic removed). I don't quite understand why this isn't going to saturate the CPU, as there seems to be nothing to prevent it running at full pelt. Can anyone clarify this?
Ok, I've butchered Lunar Lander in a similar way to you, and having seen the flickering I can tell you that what you are seeing is a simple artefact of the double-buffering mechanism that every Surface has.
When you draw anything on a Canvas attached to a Surface, you are drawing to the 'back' buffer (the invisible one). And when you unlockCanvasAndPost() you are swapping the buffers over... what you drew suddenly becomes visible as the "back" buffer becomes the "front", and vice versa. And so your next frame of drawing is done to the old "front" buffer...
The point is that you always draw to seperate buffers on alternate frames. I guess there's an implicit assumption in graphics architecture that you're always going to be writing every pixel.
Having understood this, I think the real question is why doesn't it flicker on hardware? Having worked on graphics drivers in years gone by, I can guess at the reasons but hesitate to speculate too far. Hopefully the above will be sufficient to satisfy your curiousity about this rendering artefact. :-)
You need to clear the previous position of the sprite, as well as the new position. This is what the View system does automatically. However, if you use a Surface directly and do not redraw every pixel (either with an opaque color or using a SRC blending mode) you must clear the content of the buffer yourself. Note that you can pass a dirty rectangle to lockCanvas() and it will do the union for you of the previous dirty rectangle and the one you are passing (this is the mechanism used by the UI toolkit.) It will also set the clip rect of the Canvas to be the union of these two rectangles.
As for your second question, unlockAndPost() will do a vsync, so you will never draw at more than ~60fps (most devices that I've seen have a display refresh rate set around 55Hz.)

Categories

Resources