2011-10-07 11 views
47

특정 사이트의 전체 화면 웹보기를 표시하는 Blackberry 응용 프로그램을 만들고 있습니다. 올바르게 작동하는 브라우저 필드가 있지만 페이지 간 탐색은 기본 브라우저보다 느립니다. 브라우저 필드에로드 시간이 느려지도록 내장 된 캐시가없는 것 같습니다. 캐시를 관리하기 위해 다음 코드를 추가하면 사이트가 더 이상 올바르게 표시되지 않습니다.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 :

0,123,516
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; 
    } 
} 

. 이것은 정말로 나를 곤란하게 만든다. 감사.

업데이트 : 캐싱은 특정 수준의 블랙 베리 라이브러리에서만 작동합니다. 현재 소프트웨어 수준을 확인하고 장치의 현재 소프트웨어 수준에서 지원되는 경우 캐싱을 설정하는 논리를 추가했습니다. 이것은 좋은 해결 방법을 제공하지만 모든 장치에서 캐싱을 수행 할 수있는 더 좋은 방법이 있는지 알고 싶습니다.

업데이트 날짜 : 2 설명 : 더 이상 적절하게 표시되지 않는 사이트는 적절한 레이아웃, 이미지 및 텍스트가 표시되지 않는 사이트와 관련이 있습니다. 기본적으로 흰색 배경에 링크 및 글 머리 기호 목록으로 표시되며 모든 서식이 제거됩니다.

+0

"사이트가 더 이상 올바르게 표시되지 않는다"는 것은 무엇을 의미합니까? 캐시를 사용하면 어떻게됩니까? – Tamar

+0

자세한 내용이 도움이 될 것입니다.* 더 이상 올바르게 표시되지 않습니다. * * 캐싱은 특정 레벨에서만 작동합니다. * * 작동 *은 무엇을 의미합니까? 제대로 작동하지 않으면 정확히 무엇이 발생합니까? – nloko

+0

캐시 내용을 잃어 버리면 메인 스크린을 계속 볼 때 또는 화면을 닫았다가 다시 열 때 탐색 중이라는 것을 의미합니까? 로드하는 모든 페이지에 대해 새 브라우저 화면을 만드십니까? –

답변

3

코드를 살펴 봤는데 오류가 있다는 것을 발견 한 경우 response.getLength();이 (CacheManagerImpl.createCache())보다 작은 값을 반환 할 가능성을 완전히 무시하고있는 것입니까? 이것이 stackoverflow.com 페이지에서 나에게 일어나지는 않았지만, 일부 페이지는 Transfer-Encoding: chunked을 사용합니다. 이는 Content-Length이 없음을 의미합니다. 그러나 이는 잘 처리되므로 캐시가 실패하지 않아야합니다 (캐시가 덜 효과적 일뿐입니다).

작은 문제에 대한 코드를 한 번에 한 단계 씩 테스트하는 것이 좋습니다. 먼저 HTML 태그가없는 텍스트 (예 : "hello") 만 포함하는 캐시 가능 페이지를 만듭니다. 그렇게하면 잘 작동 할 것입니다. 그렇지 않은 경우 데이터가 손실되는 위치를 파악하는 것이 어려워서는 안됩니다. 또는 만료되지 않고 스타일 시트 나 이미지가없는 웹 페이지가 포함 된 캐시 항목을 수동으로 생성 해보고 시도하는 방식으로 BrowserField에 전달할 수 있는지 확인하십시오. 그런 다음 빌드하고 이미지를 추가하고 문제를 해결할 수 있도록 스타일 시트를 추가하십시오.

코드는 매우 훌륭하게 작성되었지만 코드에 결함이 없으며 자신을 잘 설명하지 못하기 때문에이 시점에서 오류를 어떻게 나타낼 지 명확하지 않습니다. , 매번 또는 임의의 경우, ... 만약 내가 블랙 베리 장치가 있었다면, 나는 아마도 코드를 자신을 위해 실행 해 볼 수는 있었지만 나는 그렇지 않다.

+0

매회있는 것 같습니다. 나는 현재 그것이 5 미만의 버전을위한 블랙 베리 API와 관련이 있다고 믿고있다. –

+0

좋아, 그래서 간단한 HTML 코드로 캐시를 위조하고 그것이로드되는지 확인해 보라. –

관련 문제