2011-10-07 26 views
47

Tôi đang tạo ứng dụng Blackberry để hiển thị chế độ xem web toàn màn hình của một trang web nhất định. Tôi có một trình duyệt đang hoạt động hiển thị chính xác nhưng điều hướng từ trang này sang trang khác chậm hơn so với trình duyệt gốc. Trường trình duyệt dường như không có bộ nhớ cache tích hợp khiến thời gian tải chậm. Khi tôi thêm mã sau để quản lý bộ nhớ cache, trang web không còn hiển thị chính xác nữa.Cách cache trong Blackberry 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; 
    } 
} 

CacheItem.java:

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; 
    } 
} 

Bất kỳ sự giúp đỡ có thể được đưa ra về phía này sẽ được đánh giá rất nhiều. Điều này thực sự khiến tôi bối rối. Cảm ơn.

CẬP NHẬT: Dường như bộ nhớ đệm chỉ hoạt động ở một mức nhất định của thư viện Blackberry. Tôi đã thêm logic để kiểm tra mức Phần mềm hiện tại và bật bộ nhớ đệm nếu nó được hỗ trợ bởi cấp phần mềm hiện tại của thiết bị. Điều này cung cấp cho tôi một công việc tốt xung quanh, nhưng tôi vẫn muốn biết nếu có một cách tốt hơn cho bộ nhớ đệm để làm việc với tất cả các thiết bị.

CẬP NHẬT 2 Dựa trên nhận xét: Trang web không còn hiển thị đúng liên quan đến trang web không hiển thị bố cục, hình ảnh và văn bản phù hợp. Về cơ bản nó cung cấp cho một nền trắng với các liên kết và văn bản hiển thị như một danh sách có dấu đầu dòng, tất cả các định dạng được loại bỏ.

+0

Khi bạn nói "trang web không còn hiển thị chính xác", ý của bạn là gì? Điều gì xảy ra khi bạn sử dụng bộ nhớ cache? – Tamar

+0

Thêm chi tiết sẽ hữu ích.* không còn hiển thị đúng cách * * bộ nhớ đệm chỉ hoạt động ở mức nhất định ... * * làm việc * nghĩa là gì? và những gì chính xác xảy ra khi nó không hoạt động? – nloko

+0

Khi bạn zsay bạn mất nội dung bộ nhớ cache, bạn có nghĩa là trong khi điều hướng giữ hiển thị màn hình chính, hoặc khi bạn đóng và mở lại màn hình? Bạn có tạo một Màn hình trình duyệt mới cho mỗi trang bạn tải không? –

Trả lời

3

Tôi đã xem mã của bạn và điều duy nhất tôi thấy có sai với nó, là bạn hoàn toàn bỏ qua khả năng response.getLength(); trả lại ít hơn 0 (trong CacheManagerImpl.createCache()). Mặc dù điều này không xảy ra với tôi ở trang stackoverflow.com, một số trang sử dụng Transfer-Encoding: chunked, có nghĩa là Content-Length không có mặt. Tuy nhiên, điều này được xử lý tốt và không nên làm cho bộ nhớ cache thất bại (nó sẽ chỉ kém hiệu quả hơn).

Tôi khuyên bạn nên kiểm tra mã của mình về các vấn đề nhỏ hơn, từng bước một. Đầu tiên, tạo trang có thể lưu vào bộ nhớ cache chỉ chứa một số văn bản (như "hello") mà không có bất kỳ thẻ HTML nào. Điều đó sẽ hoạt động khá tốt, và trong trường hợp không, nó sẽ không khó để xác định nơi dữ liệu đang bị mất. Hoặc cố gắng tạo mục bộ nhớ cache theo cách thủ công không hết hạn và chứa trang web không có biểu định kiểu (bên ngoài) cũng như hình ảnh và xem liệu có thể chuyển nó sang BrowserField cách bạn thực hiện hay không. Sau đó, xây dựng trên, thêm một hình ảnh, thêm một phong cách trang để bạn có thể góc vấn đề.

Mã này được viết rất độc đáo, nhưng tại thời điểm này, không thể giúp bạn bởi vì không có lỗ hổng rõ ràng trong mã và bạn không giải thích cho chính mình rất tốt, không rõ cách lỗi hiển thị chính nó , nếu nó là mỗi lần hoặc ngẫu nhiên, ... Nếu tôi có một thiết bị Blackberry, tôi có lẽ có thể thử chạy mã cho bản thân mình, nhưng tôi không.

+0

Dường như mọi lúc. Tôi hiện đang tin rằng nó phải làm với các API Blackberry cho các phiên bản ít hơn 5. –

+0

Ok, vì vậy hãy thử giả mạo bộ nhớ cache với một số mã html đơn giản và xem nó có tải không. –

Các vấn đề liên quan