Java 如何在Blackberry BrowserField中缓存

Java 如何在Blackberry BrowserField中缓存,java,caching,blackberry,browserfield,Java,Caching,Blackberry,Browserfield,我正在创建一个Blackberry应用程序来显示某个站点的全屏web视图。我有一个可以正常显示的浏览器字段,但页面之间的导航速度比本机浏览器慢。browserfield似乎没有内置缓存,导致加载时间变慢。当我添加以下代码来管理缓存时,站点不再正确显示 BrowserFieldScreen.java: import net.rim.device.api.browser.field2.*; import net.rim.device.api.script.ScriptEngine; import n

我正在创建一个Blackberry应用程序来显示某个站点的全屏web视图。我有一个可以正常显示的浏览器字段,但页面之间的导航速度比本机浏览器慢。browserfield似乎没有内置缓存,导致加载时间变慢。当我添加以下代码来管理缓存时,站点不再正确显示

BrowserFieldScreen.java:

import net.rim.device.api.browser.field2.*;
import net.rim.device.api.script.ScriptEngine;
import net.rim.device.api.system.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import org.w3c.dom.Document;

class BrowserFieldScreen extends MainScreen
{
    BrowserField browserField;
    LoadingScreen load = new LoadingScreen();;

    public BrowserFieldScreen()
    {   
        browserField = new BrowserField();
        browserField.getConfig().setProperty(
            BrowserFieldConfig.JAVASCRIPT_ENABLED, 
            Boolean.TRUE);
        browserField.getConfig().setProperty(
            BrowserFieldConfig.NAVIGATION_MODE, 
            BrowserFieldConfig.NAVIGATION_MODE_POINTER);
        browserField.getConfig().setProperty(
            BrowserFieldConfig.CONTROLLER, 
            new CacheProtocolController(browserField));

        browserField.requestContent("http://www.stackoverflow.com");
        add(browserField);
    }
}
CacheProtocolController.java:

import javax.microedition.io.HttpConnection;
import javax.microedition.io.InputConnection;

import net.rim.device.api.browser.field2.BrowserField;
import net.rim.device.api.browser.field2.BrowserFieldRequest;
import net.rim.device.api.browser.field2.ProtocolController;

public class CacheProtocolController extends ProtocolController{

    // The BrowserField instance
    private BrowserField browserField;

    // CacheManager will take care of cached resources 
    private CacheManager cacheManager;

    public CacheProtocolController(BrowserField browserField) {
        super(browserField);
        this.browserField = browserField;
    }

    private CacheManager getCacheManager() {
        if ( cacheManager == null ) {
            cacheManager = new CacheManagerImpl();
        }
        return cacheManager;
    }

    /**
     * Handle navigation requests (e.g., link clicks)
     */
    public void handleNavigationRequest(BrowserFieldRequest request) 
        throws Exception 
    {
        InputConnection ic = handleResourceRequest(request);
        browserField.displayContent(ic, request.getURL());
    }

    /**
     * Handle resource request 
     * (e.g., images, external css/javascript resources)
     */
    public InputConnection handleResourceRequest(BrowserFieldRequest request) 
        throws Exception 
    {
        // if requested resource is cacheable (e.g., an "http" resource), 
            // use the cache
        if (getCacheManager() != null 
            && getCacheManager().isRequestCacheable(request)) 
            {
                InputConnection ic = null;
                // if requested resource is cached, retrieve it from cache
                if (getCacheManager().hasCache(request.getURL()) 
                    && !getCacheManager().hasCacheExpired(request.getURL())) 
                {
                    ic = getCacheManager().getCache(request.getURL());
                }
                // if requested resource is not cached yet, cache it
                else 
                {
                ic = super.handleResourceRequest(request);
                    if (ic instanceof HttpConnection) 
                    {
                        HttpConnection response = (HttpConnection) ic;
                        if (getCacheManager().isResponseCacheable(response)) 
                        {
                        ic = getCacheManager().createCache(request.getURL(), 
                             response);
                        }
                }
            }
            return ic;
        }
        // if requested resource is not cacheable, load it as usual
        return super.handleResourceRequest(request);
    }

}
CacheManager.java:

import javax.microedition.io.HttpConnection;
import javax.microedition.io.InputConnection;

import net.rim.device.api.browser.field2.BrowserFieldRequest;

public interface CacheManager {
    public boolean isRequestCacheable(BrowserFieldRequest request);
    public boolean isResponseCacheable(HttpConnection response);
    public boolean hasCache(String url);
    public boolean hasCacheExpired(String url);
    public InputConnection getCache(String url);
    public InputConnection createCache(String url, HttpConnection response);
    public void clearCache(String url);
}
CacheManagerImpl.java:

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Hashtable;

import javax.microedition.io.HttpConnection;
import javax.microedition.io.InputConnection;

import net.rim.device.api.browser.field2.BrowserFieldRequest;
import net.rim.device.api.browser.field2.BrowserFieldResponse;
import net.rim.device.api.io.http.HttpHeaders;


public class CacheManagerImpl implements CacheManager {

    private static final int MAX_STANDARD_CACHE_AGE = 2592000;
    private Hashtable cacheTable;

    public CacheManagerImpl() {
        cacheTable = new Hashtable();
    }

    public boolean isRequestCacheable(BrowserFieldRequest request) {
        // Only HTTP requests are cacheable
        if (!request.getProtocol().equals("http")) {
            return false;
        }

        // Don't cache the request whose method is not "GET".
        if (request instanceof HttpConnection) {
            if (!((HttpConnection) request).getRequestMethod().equals("GET")) 
            {
                return false;
            }
        }

        // Don't cache the request with post data.
        if (request.getPostData() != null) {
                return false;
        }

        // Don't cache authentication request.
        if (request.getHeaders().getPropertyValue("Authorization") != null) {
            return false;
        }        

        return true;        
    }

    public boolean isResponseCacheable(HttpConnection response) {
        try {
            if (response.getResponseCode() != 200) {
                return false;
            }
        } catch (IOException ioe) {
            return false;
        }

        if (!response.getRequestMethod().equals("GET")) {
            return false;
        }

        if (containsPragmaNoCache(response)) {
            return false;
        }

        if (isExpired(response)) {
            return false;
        }

        if (containsCacheControlNoCache(response)) {
            return false;
        }

        if ( response.getLength() <= 0 ) {
            return false;
        }

        // additional checks can be implemented here to inspect
        // the HTTP cache-related headers of the response object

        return true;
    }

    private boolean isExpired(HttpConnection response) {
        try 
        {
            // getExpiration() returns 0 if not known
            long expires = response.getExpiration(); 
            if (expires > 0 && expires <= (new Date()).getTime()) {
                return true;
            }    
            return false;
        } catch (IOException ioe) {
            return true;
        }
    }

    private boolean containsPragmaNoCache(HttpConnection response) {
        try 
        {
            if (response.getHeaderField("pragma") != null 
                && response.getHeaderField("pragma")
                           .toLowerCase()
                           .indexOf("no-cache") >= 0) 
            {
                return true;
            } 

            return false;
        } catch (IOException ioe) {
            return true;
        }
    }

    private boolean containsCacheControlNoCache(HttpConnection response) {
        try {
            String cacheControl = response.getHeaderField("cache-control");
            if (cacheControl != null) {
                cacheControl = removeSpace(cacheControl.toLowerCase());
                if (cacheControl.indexOf("no-cache") >= 0 
                    || cacheControl.indexOf("no-store") >= 0 
                    || cacheControl.indexOf("private") >= 0 
                    || cacheControl.indexOf("max-age=0") >= 0) {
                    return true;        
                }

                long maxAge = parseMaxAge(cacheControl);
                if (maxAge > 0 && response.getDate() > 0) {
                    long date = response.getDate();
                    long now = (new Date()).getTime();                    
                    if (now > date + maxAge) {
                        // Already expired
                        return true;
                    }
                }
            } 

            return false;
        } catch (IOException ioe) {
            return true;
        }
    }    

    public InputConnection createCache(String url, HttpConnection response) {

        byte[] data = null;
        InputStream is = null;
        try {
            // Read data
            int len = (int) response.getLength();
            if (len > 0) {
                is = response.openInputStream();
                int actual = 0;
                int bytesread = 0 ;
                data = new byte[len];
                while ((bytesread != len) && (actual != -1)) {
                    actual = is.read(data, bytesread, len - bytesread);
                    bytesread += actual;
                }
            }       
        } catch (IOException ioe) {
            data = null;
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ioe) {
                }
            }
            if (response != null) {
                try {
                    response.close();
                } catch (IOException ioe) {
                }
            } 
        }

        if (data == null) {
            return null;
        } 

        // Calculate expires
        long expires = calculateCacheExpires(response);

        // Copy headers
        HttpHeaders headers = copyResponseHeaders(response);

        // add item to cache
        cacheTable.put(url, new CacheItem(url, expires, data, headers));

        return new BrowserFieldResponse(url, data, headers);
    }

    private long calculateCacheExpires(HttpConnection response) {
        long date = 0;
        try {
            date = response.getDate();
        } catch (IOException ioe) {
        }

        if (date == 0) {
            date = (new Date()).getTime();
        }

        long expires = getResponseExpires(response);

        // If an expire date has not been specified assumes the maximum time
        if ( expires == 0 ) {
            return date + (MAX_STANDARD_CACHE_AGE * 1000L);
        }

        return expires;
    }

    private long getResponseExpires(HttpConnection response) {
        try {
            // Calculate expires from "expires"
            long expires = response.getExpiration();
            if (expires > 0) {
                return expires;
            }

            // Calculate expires from "max-age" and "date"
            if (response.getHeaderField("cache-control") != null) {
                String cacheControl = removeSpace(response
                                               .getHeaderField("cache-control")
                                               .toLowerCase());
                long maxAge = parseMaxAge(cacheControl);
                long date = response.getDate();

                if (maxAge > 0 && date > 0) {
                    return (date + maxAge);
                }
            }
        } catch (IOException ioe) {
        }

        return 0;
    }

    private long parseMaxAge(String cacheControl) {
        if (cacheControl == null) {
            return 0;
        }

        long maxAge = 0;
        if (cacheControl.indexOf("max-age=") >= 0) {
            int maxAgeStart = cacheControl.indexOf("max-age=") + 8;
            int maxAgeEnd = cacheControl.indexOf(',', maxAgeStart);
            if (maxAgeEnd < 0) {
                maxAgeEnd = cacheControl.length();
            }

            try {
                maxAge = Long.parseLong(cacheControl.substring(maxAgeStart,
                                                               maxAgeEnd));
            } catch (NumberFormatException nfe) {
            }
        }

                // Multiply maxAge by 1000 to convert seconds to milliseconds
                maxAge *= 1000L;
        return maxAge;
    }

    private static String removeSpace(String s) {
        StringBuffer result= new StringBuffer();
        int count = s.length();
        for (int i = 0; i < count; i++) {
            char c = s.charAt(i);
            if (c != ' ') {
                result.append(c);
            }
        }

        return result.toString();
    }

    private HttpHeaders copyResponseHeaders(HttpConnection response) {
        HttpHeaders headers = new HttpHeaders();
        try {
            int index = 0;
            while (response.getHeaderFieldKey(index) != null) {
                headers.addProperty(response.getHeaderFieldKey(index),
                                    response.getHeaderField(index));
                index++;
            }
        } catch (IOException ioe) {
        }

        return headers;
    }    

    public boolean hasCache(String url) {
        return cacheTable.containsKey(url);
    }

    public boolean hasCacheExpired(String url) {
        Object o = cacheTable.get(url);

        if (o instanceof CacheItem) {
            CacheItem ci = (CacheItem) o;
            long date = (new Date()).getTime();
            if (ci.getExpires() > date) {
                return false;
            } else {
                // Remove the expired cache item
                clearCache(url);
            }
        }

        return true;
    }

    public void clearCache(String url) {
        cacheTable.remove(url);
    }    

    public InputConnection getCache(String url) {
        Object o = cacheTable.get(url);        
        if (o instanceof CacheItem) {
            CacheItem ci = (CacheItem) o;
            return new BrowserFieldResponse(url, 
                                            ci.getData(), 
                                            ci.getHttpHeaders());
        }        
        return null;
    }
}
我们将非常感谢您为此提供的任何帮助。这真让我难堪。谢谢

更新:看起来缓存只在Blackberry库的某个级别起作用。我添加了检查当前软件级别的逻辑,并在设备当前软件级别支持的情况下打开缓存。这为我提供了一个很好的解决方法,但我仍然想知道是否有更好的方法让缓存与所有设备一起工作


更新2基于评论:网站不再正确显示属于未显示正确布局、图像和文本的网站。它基本上是白色背景,链接和文本显示为项目符号列表,所有格式都已删除。

我一直在查看您的代码,我发现唯一的问题是,您完全忽略了
response.getLength()的可能性返回的值小于零(在
CacheManagerImpl.createCache()
中)。虽然我在stackoverflow.com页面上没有遇到这种情况,但有些页面使用了
传输编码:chunked
,这意味着
内容长度
不存在。但是,这是可以很好地处理的,并且不会导致缓存失败(这只会降低效率)

我建议在较小的问题上测试代码,一步一个脚印。首先,创建只包含一些文本(如“hello”)而没有任何HTML标记的可缓存页面。这应该可以很好地工作,如果不行,那么确定数据丢失的位置应该不难。或者尝试手动创建未过期且包含没有(外部)样式表或图像的网页的缓存项,并查看是否可以按您的方式将其传递给
BrowserField
。然后构建,添加图像,添加样式表,以便解决问题


代码写得很好,但在这一点上,它不可能帮助您,因为代码中没有明显的缺陷,您也没有很好地解释自己,不清楚错误是如何表现出来的,如果是每次还是随机的。。。如果我有黑莓设备,我可能会尝试自己运行代码,但我没有。

当你说“网站不再正常显示”时,你是什么意思?使用缓存时会发生什么情况?更多详细信息会有所帮助。不再正确显示缓存仅在特定级别工作。。。工作意味着什么?当它不工作时会发生什么?当你说你丢失了缓存内容时,你的意思是在导航时保持主屏幕可见,或者当你关闭并重新打开屏幕时?是否为加载的每个页面创建新的浏览器屏幕?网站缓存的图像和样式等内容必须为出现的每个页面重新加载。当缓存工作时,主页之后的所有页面加载速度会更快,因为这些图像在缓存中可用。@ScottBoettger,您能检查在处理HTTP请求时是否指定了正确的用户代理吗?这可能是奇怪渲染的一个原因。似乎每次都是这样。我目前认为这与5以下版本的Blackberry API有关。好吧,那么试着用一些简单的html代码假装缓存,看看它是否加载。
import net.rim.device.api.io.http.HttpHeaders;

public class CacheItem {

    private String  url;    
    private long    expires;    
    private byte[] data;
    private HttpHeaders httpHeaders;

    public CacheItem(String url, 
                     long expires, 
                     byte[] data, 
                     HttpHeaders httpHeaders)
    {
        this.url = url;
        this.expires = expires;
        this.data = data;
        this.httpHeaders = httpHeaders;
    }

    public String getUrl() {
        return url;
    }

    public long getExpires() {
        return expires;
    }

    public byte[] getData() {
        return data;
    }

    public HttpHeaders getHttpHeaders() {
        return httpHeaders;
    }
}