当支持Web应用程序时,GWT-app排列脚本不会缓存在iOS设备上

时间:2013-04-24 09:53:00

标签: gwt web-applications ios6 linker cache-manifest

最近,我发现在我的GWT应用程序中使用iOS 6.x设备在web-app(全屏)模式下运行时出现了缓存问题。问题是 iOS似乎忽略了我对大型排列文件的缓存策略指令<hash>.cache.html。)

我有一个servlet过滤器设置静态资源上的缓存头,包括*.cache.html个文件,例如:

# Response header snippet
Expires: Fri, 26 Jul 2013 09:58:28 GMT
Cache-Control: public, max-age=8640000
ETag: W/"322107-1359629388000"
Last-Modified: Thu, 31 Jan 2013 10:49:48 GMT

然而,只要我提供了网络应用程序支持并将应用程序添加到我的主屏幕,iOS设备就会在每次加载上请求置换文件并且既不发送If-None-Match也不发送If-Modified-Since个请求标头。通过<meta>标记添加了网络应用支持:

<meta name="apple-mobile-web-app-capable" content="yes" />

我无法在任何地方找到此问题并且不确定它是否是一个错误。然而,,我所经历的。缓存在我的桌面浏览器中按预期工作,我可以检查收到的标头。我无处嗅探用户代理并根据此信息进行区分,因此所有客户端都将收到相同的标题。


我能够通过此Google I/O talk: "Google I/O 2010 - GWT Linkers target HTML5 WebWorkers & more"中显示的HTML5缓存清单文件“解决”当前问题,其中自定义GWT Linker生成包含所有生成的排列的清单文件,例如:

CACHE MANIFEST
<hash1>.cache.html
<hash2>.cache.html
...
<hashN>.cache.html

并直接在主页(<module>.html)中添加清单:

<!doctype html>
<html manifest="[module-path]/offline.manifest">
...

我的情况是18个排列,每个大约5MB,通过3G或Edge :(这真的是最好的解决方案吗?

1 个答案:

答案 0 :(得分:0)

我最终做的是进一步调查并发现而不是在主机页面中添加清单引用,我可以将其添加到所有排列脚本。 manifest本身可以是一个通用的(实际上是空的)文件:

  

鼓励作者在清单中包含主页面,但实际上,即使未明确提及,也会自动缓存引用清单的页面。 (source

因此,我写了一个简单的Linker

  1. 创建脱机清单文件
  2. 在所有排列脚本manifest)的<html>标记中添加*.cache.html属性。
  3. Linker被称为ManifestLinker并且相当简单:

    package com.example.linker;
    
    // Imports
    
    @LinkerOrder(Order.POST)
    public class ManifestLinker extends AbstractLinker {
        private static final String MANIFEST_FILE = "manifest.nocache.appcache";
        private static final String HTML_FIND = "<html>";
        private static final String HTML_REPLACE = "<html manifest=\"" + MANIFEST_FILE + "\">";
    
        /* (non-Javadoc)
         * @see com.google.gwt.core.ext.Linker#getDescription()
         */
        @Override
        public String getDescription() {
            return "`Manifest Linker`: Adds AppCache support for static `.cache.html` resources.";
        }
    
        @Override
        public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException {
            ArtifactSet output = new ArtifactSet(artifacts);
    
            output.add(buildManifest(logger));
    
            for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) {
                if (artifact.getVisibility() == Visibility.Public && artifact.getPartialPath().endsWith(".cache.html")) {
                    logger.log(TreeLogger.TRACE, "Processing file: " + artifact.getPartialPath());
    
                    String cacheHtml = Util.readStreamAsString(artifact.getContents(logger));
                    if (cacheHtml.startsWith(HTML_FIND)) {
                        cacheHtml = HTML_REPLACE + cacheHtml.substring(HTML_FIND.length()); // Replace `<html>` tag.
    
                        output.replace(copyArtifact(logger, artifact, cacheHtml));
                    }
                }
            }
    
            logger.log(TreeLogger.INFO, "Manifest created and linked successfully.");
    
            return output;
        }
    
        private EmittedArtifact copyArtifact(TreeLogger logger, EmittedArtifact original, String contents) throws UnableToCompleteException {
            EmittedArtifact copy = emitString(logger, contents, original.getPartialPath());
            copy.setVisibility(original.getVisibility());
    
            return copy;
        }
    
        private EmittedArtifact buildManifest(TreeLogger logger) throws UnableToCompleteException {
            StringBuilder builder = new StringBuilder();
            builder.append("CACHE MANIFEST\n")
                   .append("# Generated by ")
                   .append(getClass().getSimpleName())
                   .append(": ")
                   .append(System.currentTimeMillis())
                   .append(".\n\n")
                   .append("NETWORK:\n")
                   .append("*\n");
    
            SyntheticArtifact manifest = emitString(logger, builder.toString(), MANIFEST_FILE);
    
            return manifest;
        }
    }
    

    在我的<module>.gwt.xml文件中,我定义并添加链接器:

    <?xml version="1.0" encoding="UTF-8"?>
    <module>
        ...
        <define-linker name="manifest" class="com.example.linker.ManifestLinker" />
        <add-linker name="manifest" />
        ...
    

    另外,我确保通过web.xml

    设置了正确的内容类型
    ...
    <mime-mapping>
        <extension>appcache</extension>
        <mime-type>text/cache-manifest</mime-type>
    </mime-mapping>
    ...
    

    发出的manifest.nocache.appcache也很简单:

    CACHE MANIFEST
    # Generated by ManifestLinker: 1366702621298.
    
    NETWORK:
    *