Android WebView 呈现空白/白色,视图不会在 css 更改或 HTML 更改时更新,动画不连贯

Posted

技术标签:

【中文标题】Android WebView 呈现空白/白色,视图不会在 css 更改或 HTML 更改时更新,动画不连贯【英文标题】:Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy 【发布时间】:2012-11-10 03:05:13 【问题描述】:

每当我更改 css 类时,更改并不总是出现。当我有一个向按钮添加类似向下类名称的触摸事件时,这似乎会发生。该按钮不会更新,页面上的其他任何内容也不会更新。它的工作时间非常不稳定。我还注意到,有时我的元素看起来是白色的,没有任何内容或任何东西。这很令人沮丧!

【问题讨论】:

【参考方案1】:

注意: android 4.4+ 有更好的解决方案。这是一个名为 CrossWalk 的插入式 WebView 替代品。它使用最新的 Chromium 套件,非常棒。你可以在这里阅读:crosswalk-project.org

此外,从 Android 4.4 开始,invalidate() 解决方案似乎不再需要,您可以使用其他一些更安全的答案。我只会将这种invalidate() 方法用作最后的努力。


我正在回答我自己的问题,希望能帮助人们解决与我相同的问题。

我已经尝试了几种方法来让事情变得更好,即使是臭名昭著的 -webkit-transform: translate3d(0,0,0); 也没有那么好用。

让我和你分享一下什么是有效的。

首先,使用最新的 API。我正在使用 API 15。在您的 AndroidManifest.xml 中,确保启用硬件加速。如果您的 API 版本不支持此功能,请继续进行下一步。

如果您的版本支持它,您可以通过修改清单来启用它:

<application
   ...
   android:hardwareAccelerated="true">

此外,请确保您的清单具有与您正在使用的 API 相比受支持的最低 API。由于我使用的是 API 15,这就是我的清单所具有的:

<uses-sdk
    android:minSdkVersion="15"
    android:targetSdkVersion="15" />

更新:您现在应该修改 build.gradle 中的值)

现在,在您的主要 CSS 文件中,将在 WebView 中显示的内容添加如下内容:

body div 
    -webkit-transform: translate3d(0,0,0);

使用您拥有的任何其他元素类型添加到 body div 位;您可能可以排除图像,ulli 等。将这种 CSS 样式应用于所有内容的原因只是通过反复试验,我发现将其应用于所有内容似乎效果最好。对于较大的 DOM 树,您可能需要更具体。不过,我不确定规范是什么。

当您实例化您的 WebView 时,您需要设置一些设置:

@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    super.loadUrl("file:///android_asset/www/index.html");
    appView.getSettings().setRenderPriority(RenderPriority.HIGH);
    appView.getSettings()
            .setPluginState(WebSettings.PluginState.ON_DEMAND);

倒数第二,但至关重要的一点:我正在阅读WebView 类的源代码,发现这个关于强制重绘的小评论。有一个static final boolean,当设置为true 时,将强制视图始终重绘。我对 Java 语法不是很了解,但我认为您不能直接更改类的静态最终属性。所以我最终做的是像这样扩展课程:

import org.apache.cordova.CordovaWebView;

import android.content.Context;
import android.graphics.Canvas;

public class MyWebView extends CordovaWebView 
    public static final String TAG = "MyWebView";

    public MyWebView(Context context) 
        super(context);
    

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
        // Warning! This will cause the WebView to continuously be redrawn
        // and will drain the devices battery while the view is displayed!
        invalidate();
    


请记住,我使用的是 Cordova/PhoneGap,所以我必须从 CordovaWebView 扩展。如果你在 onDraw 方法中看到,有一个对invalidate 的调用。这将导致视图不断重绘。不过,我强烈建议添加逻辑,以便仅在需要时重绘。

如果您使用的是 Cordova,还有最后一步。您必须告诉 PhoneGap 使用您的新 WebView 类而不是他们自己的 WebView 类。在你的 MainActivity 类中,添加:

public void init()
    CordovaWebView webView = new MyWebView(MainActivity.this);
    super.init(webView, new CordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));

就是这样!尝试运行您的应用程序,看看是否一切都更加顺畅。在进行所有这些更改之前,页面会显示为白色,直到再次点击屏幕后才会应用 CSS 更改,动画非常不稳定甚至不明显。我应该提一下,动画仍然不连贯,但比以前不那么不连贯了。

如果有人对此有任何补充,请在下方评论。我总是愿意进行优化;而且我完全意识到可能有更好的方法来做我刚才推荐的事情。

如果我的上述解决方案对您不起作用,您能否描述一下您的具体情况以及您在使用 AndroidWebView 时看到的结果?

最后,我已将这个答案启用为 “社区 wiki”,因此任何人和每个人都可以随时进行调整。

谢谢!


编辑:

使用最新的 PhoneGap,您需要让您的 init() 方法看起来更像这样:

public void init()
    CordovaWebView webView = new MyWebView(MainActivity.this);
    super.init(webView, new IceCreamCordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));

【讨论】:

我要让 Joe 看看你的答案,也许将其包含在 CordovaWebView 代码中是个好主意。 顺便说一句......这一切都将再次改变,谷歌即将改变 WebView 组件以使用 Chromium,所以......我真的很期待看看会发生什么再次被打破......对我来说,从 HoneyComb 到 ICS 的变化绝对是一场灾难。我很害怕 :S :S :S 请注意,在 onDraw 上调用 invalidate 意味着您的 webview 将永远不断地重绘。不是最好的主意。 @Vee,当时,我不知道 invalidate 实际上做了什么。你有一个你认为会更好的解决方案吗?请分享。 -webkit-transform: translate3d(0,0,0);节省一天..即使在 Chromium 上。这更正了打开硬件加速后变为空白的设备的渲染问题。我使用 setLayerType(View.LAYER_TYPE_NONE, null);所以这不会在某些设备上发生。【参考方案2】:

我实施了 kyle 的解决方案,它解决了问题。但是,当应用程序打开时,我注意到 android 4.0.4 上的电池消耗很大。同样在更改之后,我有用户抱怨 swiftKey 键盘不再适用于我的应用程序。

我的应用程序中的每个更改都是由用户操作触发的,所以我想出了一个修改版本,它只在 touchEvent 之后触发 invalidate():

    Handler handler = new Handler();
    public boolean onTouchEvent (MotionEvent event)
        super.onTouchEvent(event);
        handler.postDelayed(triggerInvalidate, 60);
            handler.postDelayed(triggerInvalidate, 300);
        return true;
    

    private Runnable triggerInvalidate=new Runnable()
        public void run()
            invalidate();
        
    ;

从未使用 Java 进行过任何编程,因此可能有更好的解决方案。

【讨论】:

非常好的方法!!!我认为它真的很到位而且很干净!我能想到的唯一更好的解决方案是谷歌发布一个正常工作的 WebView 并且永远不要再碰它。对于像我这样的 indy 开发人员来说,每隔一个星期天看到应用程序就因为这个问题而崩溃,真是令人沮丧。而且,你知道,准备好迎接下一个 Android 版本的巨大变化,他们威胁我们基于 Chromium 的总 webview 变化......而且根据我的经验,Android 上的 Chrome 比股票 webview 慢...... 电池消耗是由于视图不断重绘。该解决方案完全缓解了这种情况。因此,我强烈建议不要使用所选答案。 @VicVu 我已将其更改为所选答案而不是我自己的答案。【参考方案3】:

re:重绘问题,可以通过读取元素的属性来强制重绘

所以说你这样做:

$('#myElement').addClass('foo'); // youre not seeing this take effect

如果你之后这样做:

$('#myElement').width();

它将强制重绘。

这样你就可以有选择性,而不是一直重绘整个页面,这很昂贵

【讨论】:

哇,我很高兴我遇到了这个问题,其他解决方案无缘无故耗尽了我的电池,即使限制了每秒重绘。这个答案应该在那里!【参考方案4】:

对我来说,这个问题只发生在三星设备上。我可以通过禁用 WebView 的硬件加速来修复它:

webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

希望对你有帮助。

【讨论】:

谢谢,它有效。我在科尔多瓦 webview 中使用画布。【参考方案5】:

正如上面和其他地方所指出的 - 覆盖 View.onDraw() 并调用 View.invalidate 会导致电池不满意/应用程序性能会下降。你也可以像这样手动invalidate call ever x ms

/**
 * Due to bug in 4.2.2 sometimes the webView will not draw its contents after the data has loaded. 
 * Triggers redraw. Does not work in webView's onPageFinished callback for some reason
 */
private void forceWebViewRedraw()

    mWebView.post(new Runnable() 
        @Override
        public void run()
        
            mWebView.invalidate();
            if(!isFinishing())
                mWebView.postDelayed(this, 1000);
        
    );

我尝试在WebViewClient.onPageLoaded() 中进行无效调用,但这似乎不起作用。虽然我的解决方案可能会更好,但它很简单并且对我有用(我只是显示了一个 twitter 登录)

【讨论】:

你在哪里调用了 forceWebViewRedraw() exaclty 方法? 您可以在onPageFinished 回调中调用它。方法注释是指onPageFinished中不能调用mWebView.invalidate(); 这里“isFinishing()”的作用是什么? @Dori 所以视图一直在绘制,直到活动完成,在这种情况下,我认为 finish() 是手动调用的。你不想在一个永远运行的活动中启动一个可运行对象,否则当视图最终分离时你会得到一个异常...... 为我工作,谢谢。我只调用一次重绘,不需要延迟重绘,就足够了。【参考方案6】:

也请在WebView fails to render until touched Android 4.2.2 上查看我的回答,这个想法是将@Olivier 的函数导出到javascript,这样您就可以在合理的部分触发JS 的无效...我的上帝,又是一个黑客! :)

【讨论】:

【参考方案7】:

我遇到了这个问题,因为 onPageStared 和 onPageFinished 我显示和隐藏了加载动画。

由于注入 JS,我从原始网站执行我的菜单和内容,并注意到在 onPageFinished 上注入 JS 会强制 webview 绘制内容。这是您可以在 onPageFinished 末尾添加的示例

view.loadUrl("javascript:(function()  var select = document.getElementsByClassName('something')[0]\r\n" + 
                            "                     if(select)" +
                            "                       select.style.display = 'none';)()");

【讨论】:

【参考方案8】:

只需在 oncreate() 中清除 webView 的缓存,它就对我有用。

webView.clearCache(true);

【讨论】:

【参考方案9】:

您应该通过修改清单来启用硬件:

<application
   ...
   android:hardwareAccelerated="true">

在你的活动中 onResume()

webView.postDelayed(() -> 
    if (!isFinishing() && webView != null) 
        webView.setLayerType(View.LAYER_TYPE_NONE, null);
    
, 1000);

在你的 onPause() 中

webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

【讨论】:

以上是关于Android WebView 呈现空白/白色,视图不会在 css 更改或 HTML 更改时更新,动画不连贯的主要内容,如果未能解决你的问题,请参考以下文章

Android webview周围有白色边框,我该如何摆脱它?

Android:如何在webview中呈现html而不显示webview

去掉WebView中的白色背景

iOS WebView 空白

android webview 在 PWA 框架中变为空白

android webview显示空白页