2011-10-07 20 views
47

Estoy creando una aplicación Blackberry para mostrar una vista web de pantalla completa de un sitio determinado. Tengo un BrowserField trabajo que se muestra correctamente pero la navegación de una página a otra es más lenta que la del navegador nativo. El campo de navegación no parece tener una memoria caché integrada, lo que hace que el tiempo de carga sea lento. Cuando agrego el siguiente código para administrar la memoria caché, el sitio ya no se muestra correctamente.Cómo almacenar en caché en 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; 
    } 
} 

Cualquier ayuda que pueden estar dando hacia este será muy apreciada. Esto realmente me tiene perplejo. Gracias.

ACTUALIZACIÓN: Parece que el almacenamiento en caché solo funciona en un cierto nivel de las bibliotecas de Blackberry. He agregado la lógica para verificar el nivel de software actual y activar el almacenamiento en caché si es compatible con el nivel de software actual del dispositivo. Esto me proporciona un buen trabajo, pero aún me gustaría saber si hay una mejor manera de que el almacenamiento en caché funcione con todos los dispositivos.

ACTUALIZACIÓN 2 Basado en comentarios: El sitio que ya no se muestra corresponde al sitio que no muestra el diseño, las imágenes y el texto correctos. Básicamente da un fondo blanco con enlaces y texto que se muestra como una lista con viñetas, todo el formato eliminado.

+0

Cuando dices "el sitio ya no se muestra correctamente", ¿a qué te refieres? ¿Qué sucede cuando usas el caché? – Tamar

+0

Más detalles serían útiles.* ya no se muestra correctamente * * el almacenamiento en caché solo funciona en cierto nivel ... * ¿qué significa * trabajo *? y qué ocurre exactamente cuando no funciona? – nloko

+0

Cuando dices que pierdes el contenido de la memoria caché, ¿quieres decir mientras navegas manteniendo visible la pantalla principal o cuando cierras y vuelves a abrir la pantalla? ¿Creas una nueva pantalla de navegador para cada página que cargas? –

Respuesta

3

He estado buscando su código, y lo único que he encontrado es que está equivocado, está ignorando por completo la posibilidad de que response.getLength(); devuelva menos de cero (en CacheManagerImpl.createCache()). Aunque esto no me sucedió en la página de stackoverflow.com, algunas páginas usan Transfer-Encoding: chunked, lo que significa que Content-Length no está presente. Esto es, sin embargo, bien manejado, y no debe causar la caché falle (que sólo sería menos eficaz).

Sugiero probar su código en problemas más pequeños, paso a paso. Primero, crea una página cacheable que solo contenga texto (como "hola") sin etiquetas HTML. Eso debería funcionar bastante bien, y en caso de que no sea así, no debería ser difícil determinar dónde se están perdiendo los datos. O intente crear manualmente un elemento de caché que no caduque y que contenga una página web sin hoja de estilo (externa) ni imágenes, y vea si es posible pasarlo al BrowserField de la manera en que lo hace. Luego construya, agregue una imagen, agregue una hoja de estilo para que pueda arrinconar el problema.

El código está escrito muy bien, pero en este punto, no es posible ayudarle porque no hay fallas evidentes en el código y no se está explicando muy bien, no está claro cómo se manifiesta el error , si es cada vez o al azar, ... Si tuviera un dispositivo Blackberry, probablemente podría intentar ejecutar el código por mi cuenta, pero no es así.

+0

Parece ser todo el tiempo. Actualmente creo que tiene que ver con las API de Blackberry para versiones de menos de 5. –

+0

Ok, intenta falsificar el caché con un código html simple y ver si se carga. –

Cuestiones relacionadas