2011-10-07 19 views
47

Sto creando un'applicazione Blackberry per visualizzare una visualizzazione Web a schermo intero di un determinato sito. Ho un browserfield funzionante che viene visualizzato correttamente ma la navigazione da una pagina all'altra è più lenta di quella del browser nativo. Il campo browser non sembra avere una cache integrata che rallenta il tempo di caricamento. Quando aggiungo il seguente codice per gestire la cache, il sito non viene più visualizzato correttamente.Come memorizzare nella cache in 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; 
    } 
} 

Tutto l'aiuto che si può dare in questa direzione sarà molto apprezzato. Questo mi ha davvero bloccato. Grazie.

AGGIORNAMENTO: Sembra che la memorizzazione nella cache funzioni solo a un certo livello delle librerie di Blackberry. Ho aggiunto la logica per verificare l'attuale livello del software e attivare la memorizzazione nella cache se è supportata dal livello software corrente del dispositivo. Questo mi fornisce un buon lavoro, ma mi piacerebbe comunque sapere se esiste un modo migliore per far funzionare la cache con tutti i dispositivi.

AGGIORNAMENTO 2 In base ai commenti: il sito che non viene più visualizzato correttamente si riferisce al sito che non visualizza il layout, le immagini e il testo corretti. Fondamentalmente fornisce uno sfondo bianco con collegamenti e testo visualizzati come un elenco puntato, tutta la formattazione rimossa.

+0

Quando si dice "il sito non viene più visualizzato correttamente" cosa intendi? Cosa succede quando usi la cache? – Tamar

+0

Ulteriori dettagli sarebbero utili.* non viene più visualizzato correttamente * * la cache funziona solo a determinati livelli ... * cosa significa * lavoro * significa? e cosa succede esattamente quando non funziona? – nloko

+0

Quando perdi il contenuto della cache, intendi mentre navighi mantenendo visibile la schermata principale o quando chiudi e riapri lo schermo? Crei una nuova schermata del browser per ogni pagina che carichi? –

risposta

3

Ho guardato il tuo codice e l'unica cosa che ho trovato è che non funziona, stai ignorando completamente la possibilità di response.getLength(); di ritornare a meno di zero (in CacheManagerImpl.createCache()). Sebbene ciò non mi sia accaduto nella pagina stackoverflow.com, alcune pagine usano Transfer-Encoding: chunked, il che significa che Content-Length non è presente. Questo è, tuttavia, ben gestito e non dovrebbe causare il fallimento della cache (sarebbe solo meno efficace).

Suggerisco di testare il codice su problemi più piccoli, un passo alla volta. Innanzitutto, crea una pagina memorizzabile nella cache che contiene solo del testo (come "ciao") senza tag HTML. Questo dovrebbe funzionare abbastanza bene, e in caso contrario, non dovrebbe essere difficile determinare dove i dati si perdono. Oppure prova a creare manualmente l'elemento della cache che non scade e contiene una pagina web senza foglio di stile (esterno) né immagini, e vedi se è persino possibile passarlo a BrowserField come fai tu. Quindi continua, aggiungi un'immagine, aggiungi un foglio di stile in modo da poter risolvere il problema.

Il codice è scritto molto bene, ma a questo punto, non è possibile per aiutarvi perché non ci sono difetti evidenti nel codice e non si spiega se stessi molto bene, non è chiaro come l'errore si manifesta , se è ogni volta o casuale, ... Se avessi un dispositivo Blackberry, potrei probabilmente provare a eseguire il codice per me stesso, ma non lo faccio.

+0

Sembra essere ogni volta. Attualmente credo che abbia a che fare con le API Blackberry per versioni inferiori a 5. –

+0

Ok, quindi prova a simulare la cache con un semplice codice html e controlla se viene caricata. –

Problemi correlati