Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 中級開發 >> Android學習之使用 HTML 5 開啟移動 Web 應用程序的本地存儲

Android學習之使用 HTML 5 開啟移動 Web 應用程序的本地存儲

編輯:中級開發

簡介: HTML 5 中一個最有用的新特性是本地存儲的標准化。最終,Web 開發人員可以不再試圖將所有客戶端數據都填塞到 4 KB 的 CookIEs 中。現在您可以利用一個簡單的 API 將大量數據存儲在客戶機上。這是一個完美的緩存機制,可以大大提高應用程序的速度 —— 速度對於移動 Web 應用程序是一個至關重要的因素,因為它們相對於桌面應用程序來說,依賴的是慢得多的連接。在這個關於 Html 5 的系列的第二篇文章中,將了解如何使用本地存儲,如何調試它,以及使用它來改善 Web 應用程序的各種方式。

關於本系列

本系列其他文章

  • 使用 HTML 5 創建移動 Web 應用程序,第 1 部分:聯合使用 Html 5、地理定位 API 和 Web 服務來創建移動混搭程序

HTML 5 是一項被大肆宣揚的技術,但是它實至名歸。它有望成為一個技術引爆點,將桌面應用程序功能引向浏覽器。它不僅適用於傳統浏覽器,甚至也針對移動浏覽器。更好的是,最流行的移動浏覽器已經采納和實現 Html 5 規范的很多重要部分。

在這個五部分的系列中,我們將詳細了解幾個新技術,它們都是 Html 5 的一部分,可以大大影響移動 Web 應用程序開發。在每一部分中,都將開發一個可以工作的移動 Web 應用程序,展示一個可以用於現代移動 Web 浏覽器(比如 iPhone 和基於 android 的設備上的浏覽器)的 Html 5 特性。


先決條件

常用縮略語

  • API: 應用程序編程接口
  • CSS: 層疊樣式表
  • DOM: 文檔對象模型
  • Html: 超文本標記語言
  • HTTP: 超文本傳輸協議
  • JSON: JavaScript 對象表示法
  • JSONP: 帶填充的 JSON
  • SDK: 軟件開發工具包
  • UI: 用戶界面
  • URL: 統一資源定位符
  • W3C: 萬維網聯盟

在本文中,您將使用最新 Web 技術開發 Web 應用程序。這裡的大多數代碼只是 Html、JavaScript 和 CSS — 任何 Web 開發人員的核心技術。需要的最重要的東西是用於測試代碼的浏覽器。本文中的大多數代碼將運行在最新的桌面浏覽器上,例外的情況會指出來。當然,還必須在移動浏覽器上進行測試,您肯定希望最新的 iPhone 和 Android SDK 支持這些代碼。本文中使用的是 iPhone SDK 3.1.3 和 android SDK 2.1。參見 參考資料 中的鏈接。


本地存儲基礎

Web 開發人員多年來一直在嘗試將數據存儲在客戶機上。HTTP CookIEs 被濫用於此目的。開發人員將大量數據擠放在 HTTP 規范分配的 4KB 上。原因很簡單。出於各種原因,交互式 Web 應用程序需要存儲數據,並且將這些數據存儲在服務器上通常效率低下、不安全或者不適當。多年來,這個問題有了好幾種備選方法。各種各樣的浏覽器已經引入了專有存儲 API。開發人員也利用了 Flash Player 中的擴展存儲功能(通過 Javascript 實現)。類似地,Google 為各種浏覽器創建了 Gears 插件,並且它包含了存儲 API。毫不奇怪的是,一些 JavaScript 庫試圖抹平這些差異。換句話說,這些庫提供一個簡單的 API,然後檢查有哪些存儲功能(可能是一個專有浏覽器 API 或者是一個諸如 Flash 的插件)。

對 Web 開發人員來說幸運的是,Html 5 規范最終包含了一個針對本地存儲的標准,被廣泛的浏覽器所實現。事實上,該標准是最快被采納的標准,在所有主要浏覽器的最新版本中都受到支持:Microsoft®、Internet Explorer®、Mozilla Firefox、Opera、Apple Safari 和 Google Chrome。對於移動開發人員更為重要的是,它在基於 WebKit 的浏覽器(諸如 iPhone 和使用 android(版本 2.0 或更高版本)的手機中的浏覽器)以及其他移動浏覽器(比如 Mozilla 的 Fennec)中受到支持。記住這一點,我們來看一下這個 API。

Storage API

localStorage API 十分簡單。實際上,根據 HTML 5 規范,它實現了 DOM Storage 接口。差別的原因是,Html 5 指定兩個不同的對象實現該接口:localStorage 和 sessionStoragesessionStorage對象是一個只在會話期間存儲數據的 Storage 實現。更確切地說,只要沒有可以訪問 sessionStorage 的腳本正在運行,浏覽器就可以刪除 sessionStorage 數據。這是與 localStorage 相對的,後者跨多個用戶會話。兩個對象共享相同的 API,所以我將只著重介紹localStorage

Storage API 是一種經典的名/值對數據結構。您將使用的最常見的方法是 getItem(name) 和 setItem(name, value)。這些方法完全跟您預期的一樣:getItem 返回與名稱相關聯的值,如果什麼都不存在,則返回 null,而 setItem 要麼是將名/值對添加到localStorage,要麼是取代現有值。還有一個 removeItem(name),顧名思意,它從 localStorage 刪除一個名/值對(如果存在的話,否則什麼都不做)。最後,對於在所有名/值對上迭代,存在兩個 API。一個是長度屬性,給出正在存儲的名/值對的總數。對應地,一個 key(index) 方法從存儲中使用的所有名稱中返回一個名稱。

利用這些簡單的 API,可以完成大量任務,比如說個性化或跟蹤用戶行為。這些可以說對移動 Web 開發人員是重要的用例,但是還有一個更為重要的用例:高速緩存。利用 localStorage,可以在客戶機的本地機器上容易地從服務器高速緩存數據。這讓您無需等待可能緩慢的服務器回調,並且最小化了對服務器上數據的需求量。現在來看一個例子,演示了如何使用 localStorage 來獲得這種高速緩存。


例子:利用本地存儲實現高速緩存

本例建立在本系列第 1 部分中的例子之上,那時您最先開始了 t0 開發。那個例子展示了如何通過利用地理定位 API 取得用戶的位置而執行 Twitter 的本地搜索。從那個例子開始,對它進行簡化,並大大提高它的性能。首先,將那個例子簡化成不帶地理位置的 Twitter 搜索。清單 1 展示了簡化的 Twitter 搜索應用程序。


清單 1. 最基本的 Twitter 搜索

				
<html>
<head>
<meta http-equiv="Content-Type" content="text/Html; charset=UTF-8">
<meta name = "vIEwport" content = "width = device-width"/>
<title>Basic Twitter Search</title>
<script type="text/Javascript">
    function searchTwitter(){
        var query = "http://search.twitter.com/search.JSon?callback
=showResults&q=";
        query += $("kwBox").value;
        var script = document.createElement("script");
        script.src = query;
        document.getElementsByTagName("head")[0].appendChild(script);
    }
    // ui code deleted for brevity
    function showResults(response){
        var tweets = response.results;
        tweets.forEach(function(tweet){
            tweet.linkUrl = "http://twitter.com/" + tweet.from_user 
+ "/status/" + tweet.id;
        });
        makeResultsTable(tweets);
    }
</script>
<!--  CSS deleted for brevity -->
</head>
<body>
    <div id="main">
        <label for="kwBox">Search Twitter:</label>
        <input type="text" id="kwBox"/>
        <input type="button" value="Go!" onclick="searchTwitter()"/>
    </div>
    <div id="results">
    </div>
</body>
</Html>

 

在這個應用程序中,使用了 Twitter 搜索 API 對 JSONP 的支持。用戶提交搜索時,會動態添加一個腳本標記到頁面並指定回調函數的名稱,從而進行一次 API 調用。這允許您從 Web 頁面進行一次跨域調用。一旦調用返回,回調函數(showResults)就會被調用。您添加一個鏈接 URL 到 Twitter 返回的每個 tweet,然後創建一個簡單的表格用於顯示這些 tweet。為了提速,您可以高速緩存從搜索查詢得到的結果,然後在用戶每次提交查詢時使用這些緩存的結果。首先來看如何使用 localStorage 來本地存儲 tweet。

本地保存

基本的 Twitter 搜索將從 Twitter 搜索 API 提供一組 tweet。如果您可以本地保存這些 tweet,並將它們與生成它們的關鍵詞搜索相關聯,那麼您就具有了一個有用的高速緩存。要保存 tweet,您只需要修改當對 Twitter 搜索 API 的調用返回時將被調用的 callback函數。清單 2 展示了修改後的函數。


清單 2. 搜索和保存

				
function searchTwitter(){
    var keyword = $("kwBox").value;
    var query = "http://search.twitter.com/search.json?callback
=processResults&q=";
    query += keyword;
    var script = document.createElement("script");
    script.src = query;
    document.getElementsByTagName("head")[0].appendChild(script);
}
function processResults(response){
    var keyword = $("kwBox").value;
    var tweets = response.results;
    tweets.forEach(function(tweet){
        saveTweet(keyword, tweet);
        tweet.linkUrl = "http://twitter.com/" + tweet.from_user + "/status/" + tweet.id;
    });
    makeResultsTable();
    addTweetsToResultsTable(tweets);
}
function saveTweet(keyword, tweet){
    // check if the browser supports localStorage
    if (!window.localStorage){
        return;
    }
    if (!localStorage.getItem("tweet" + tweet.id)){
        localStorage.setItem("tweet" + tweet.id, JSON.stringify(tweet));
    }
    var index = localStorage.getItem("index::" + keyword);
    if (index){
        index = JSON.parse(index);
    } else {
        index = [];
    }
    if (!index.contains(tweet.id)){
        index.push(tweet.id);
        localStorage.setItem("index::"+keyWord, JSON.stringify(index));
    } 
}

 

從第一個函數 searchTwitter 開始。這在用戶提交搜索時被調用。相對於 清單 1 做了改動的惟一的地方是 callback 函數。不只是在 tweet 返回時顯示它們,您還需要處理它們(除了顯示,還要保存它們)。因此,您指定一個新的 callback 函數 processResults。您針對每個 tweet 調用 saveTweet。您還傳遞被用於生成搜索結果的關鍵詞。這是因為您想要將這些 tweet 與該關鍵詞相關聯。

在 saveTweet 函數中,首先進行檢查,確保 localStorage 真正受到浏覽器的支持。正如前面所提到的,localStorage 在桌面和移動浏覽器中都受到廣泛支持,但是在使用這樣的新特性時進行檢查總是一個好主意。如果它不受支持,那麼您簡單地從函數返回。顯然不會保存任何東西,但是也不會報錯 — 應用程序在這種情況下只是不會具有高速緩存。如果 localStorage 受到支持,那麼首先進行檢查,看這個 tweet 是否已經存儲。如果沒有存儲,那麼使用 setItem 本地存儲它。接下來,檢索一個對應於關鍵詞的索引對象。這只是一組與關鍵詞相關聯的 tweet 的 ID。如果 tweet ID 還不是索引的一部分,那麼添加它並更新索引。

注意,在 清單 3 中保存和加載 JSON 時,您使用了JSON.stringify 和 JSON.parse。JSON 對象(或者更確切地說,是window.JSON)是 Html 5 規范的一部分,作為一個總是存在的 原生對象。stringify 方法將把任何 JavaScript 對象轉換成一個序列化的字符串,而 parse 方法則進行相反的操作,它從序列化的字符串表示還原 JavaScript 對象。這是很必要的,因為 localStorage 只存儲字符串。但是,原生 JSON 對象並不被廣泛實現為localStorage。例如,它不出現在 iPhone(在撰寫本文時是版本 3.1.3)的最新 Mobile Safari 浏覽器上。它在最新 android 浏覽器上受支持。您可以容易地檢查它是否在那裡,如果不在,就加載一個額外的 JavaScript 文件。您可以通過訪問 JSon.org Web 站點(參見 參考資料),獲得原生使用的相同 JSON 對象。要本地查看這些序列化的字符串是什麼樣的,可以使用各種浏覽器工具檢查 localStorage 中為給定站點存儲的內容。圖 1 展示了一些高速緩存的 tweet,它們存儲在本地,使用 Chrome 的 Developer Tools 進行查看。


圖 1. 本地高速緩存的 tweet
一系列本地高速緩存的 tweet 的屏幕截圖(帶有 Key 和 Value 字段) 
 

Chrome 和 Safari 都內置了開發人員工具,可以用於查看任何保存在 localStorage 中的數據。這對於調試使用 localStorage 的應用程序非常有用。它以純文本形式展示本地存儲的鍵/值對。既然您已經開始保存來自 Twitter 的搜索 API 的 tweet,以便它們可以被用作高速緩存,所以您只需開始從 localStorage 讀取它們即可。下面來看這是如何做到的。

快速本地數據加載

在 清單 2 中,您看到了一些例子使用 getItem 方法從localStorage 讀取數據。現在當一個用戶提交搜索時,您可以檢查高速緩存命中情況,並立即加載緩存的結果。當然,您仍將針對 Twitter 搜索 API 進行查詢,因為人們一直在產生 tweet 並添加到搜索結果。但是,通過只尋找還沒在高速緩存中的結果,現在您也有了讓查詢更為高效的方式。清單 3 展示了更新後的搜索代碼。


清單 3. 首先進行本地搜索

				
function searchTwitter(){
    if ($("resultsTable")){
        $("resultsTable").innerHtml = ""; // clear results
    }
    makeResultsTable();
    var keyword = $("kwBox").value;
    var maxId = loadLocal(keyword);
    var query = "http://search.twitter.com/search.json?callback=processResults&q=";
    query += keyword;
    if (maxId){
        query += "&since_id=" + maxId;
    }
    var script = document.createElement("script");
    script.src = query;
    document.getElementsByTagName("head")[0].appendChild(script);
}
function loadLocal(keyword){
    if (!window.localStorage){
        return;
    }
    var index = localStorage.getItem("index::" + keyWord);
    var tweets = [];
    var i = 0;
    var tweet = {};
    if (index){
        index = JSON.parse(index);
        for (i=0;i<index.length;i++){
            tweet = localStorage.getItem("tweet"+index[i]);
            if (tweet){
                tweet = JSON.parse(tweet);
                tweets.push(tweet);
            }
        }
    }
    if (tweets.length < 1){
        return 0;
    }
    tweets.sort(function(a,b){
        return a.id > b.id;
    });
    addTweetsToResultsTable(tweets);
    return tweets[0].id;
}

 

您將注意到的第一件事情是,當一個搜索被提交時,您首先調用新的loadLocal 函數。該函數返回一個整數,即高速緩存中找到的最新 tweet 的 ID。loadLocal 函數接受一個 keyWord 作為參數,該關鍵詞也被用於在 localStorage 高速緩存中尋找相關 tweet。如果具有一個 maxId,那麼使用它來修改對 Twitter 的查詢,添加 since_id參數。您在告訴 Twitter API 只返回比該參數中給定的 ID 新的 tweet。潛在地,這可以減少從 Twitter 返回的結果數量。您任何時候都可以為移動 Web 應用程序優化服務器調用,因為它可以真正改善慢速移動網絡上的用戶體驗。現在更仔細地來看一下 loadLocal

在 loadLocal 函數中,您利用了存儲在前面 清單 2 中的數據結構。通過使用 getItem,您首先加載與關鍵詞相關聯的索引。如果沒找到任何索引,那麼就沒有緩存的 tweet,所以就沒有展示的東西,並且沒有可對查詢進行的優化(您返回一個 0 值以指示這一點)。如果找到一個索引,那麼您從它得到 ID 列表。這些 tweet 中的每一個都被本地高速緩存,所以您只需再次使用 getItem 方法,從高速緩存加載每一個 tweet。加載的 tweet 然後被排序。使用addTweetsToResultsTable 函數來顯示 tweet,然後返回最新 tweet 的 ID。在本例中,得到新 tweet 的 代碼直接調用更新 UI 的函數。您可能會對此感到驚訝,因為它在存儲和檢索 tweet 的代碼與顯示它們的代碼之間創建了耦合,全都通過 processResults 函數。使用存儲事件會提供一種備選的、更少耦合的方法。

存儲事件

現在擴展示例應用程序,展示最可能具有緩存結果的前 10 個搜索條目。這可能代表用戶最常提交的搜索。清單 4 展示了一個用於計算並顯示前 10 個搜索條目的函數。


清單 4. 計算前 10 個搜索條目

				
function displayStats(){
    if (!window.localStorage){ return; }
    var i = 0;
    var key = "";
    var index = [];
    var cachedSearches = [];
    for (i=0;i<localStorage.length;i++){
        key = localStorage.key(i);
        if (key.indexOf("index::") == 0){
            index = JSON.parse(localStorage.getItem(key));
            cachedSearches.push ({keyword: key.slice(7), numResults: index.length});
        }
    }
    cachedSearches.sort(function(a,b){
        if (a.numResults == b.numResults){
            if (a.keyword.toLowerCase() < b.keyword.toLowerCase()){
                return -1;
            } else if (a.keyword.toLowerCase() > b.keyword.toLowerCase()){
                return 1;
            }
            return 0;
        }
        return b.numResults - a.numResults;
    }).slice(0,10).forEach(function(search){
        var li = document.createElement("li");
        var txt = document.createTextNode(search.keyWord + " : " + search.numResults);
        li.appendChild(txt);
        $("stats").appendChild(li);
    });
}

 

該函數充分展示了 localStorage API。您首先得到存儲在localStorage 中的條目的總數,然後再迭代這些條目。如果條目是索引,那麼您就解析該對象並創建一個表示您要處理的數據的對象:與索引相關聯的關鍵詞和索引中 tweet 的數量。該數據存儲在一個叫做 cachedSearches 的數組中。接下來,排序 cachedSearches,將具有最多結果的搜索排在第一位,如果兩個搜索具有相同數量的緩存結果,就再使用一個不區分大小寫的字母排序。然後對於前 10 個搜索,為每個搜索創建 Html,並將它們附加到一個排好序的列表。讓我們在頁面初次加載時調用該函數,如 清單 5 所示。


清單 5. 初始化頁面

				
window.onload = function() {
    displayStats();
    document.body.setAttribute("onstorage", "handleOnStorage();");
}

 

第一行在頁面加載時調用 清單 4 中的函數。第二次加載是變得更有趣的地方。您在這裡為 onstorage 事件設置一個事件處理程序。每當 localStorage.setItem 函數執行完成,該事件就會激活。這將允許您重新計算前 10 個搜索。清單 6 展示了該事件處理程序。


清單 6. Storage 事件處理程序

				
function handleOnStorage() {
    if (window.event && window.event.key.indexOf("index::") == 0){
        $("stats").innerHtml = "";
        displayStats();
    }
}

 

onstorage 事件將與窗口相關聯。它具有幾個有用的屬性:keyoldValue 和 newValue。除了這些自解釋的屬性之外,它還有一個 url(更改值的頁面的 URL)和 source(包含更改值的腳本的窗口)。如果用戶具有多個到應用程序的窗口或選項卡或者甚至是 iFrames,那麼這最後兩個屬性就更有用,但是沒有哪一個在移動應用程序中特別常見。回到 清單 6,您真正需要的惟一的屬性是key 屬性。您使用該屬性來看它是不是一個已修改的索引。如果是的,那麼您重新設置前 10 名列表,並通過再次調用 displayStats函數而重新繪制它。該技術的優點是,其他函數都不需要了解前 10 名列表,因為它是自包含的。

前面 我提到過,DOM Storage(它包含 localStorage 和sessionStorage)總體來說是一個被廣泛采納的 Html 5 特性。但是,存儲事件對於這一點來說是一個例外 — 至少在桌面浏覽器上如此。在撰寫本文時,僅有的支持存儲事件的桌面浏覽器是 Safari 4+ 和 Internet Explorer 8+。在 Firefox、Chrome 和 Opera 中不受支持。但是在移動領域,情況稍有好轉。iPhone 和 android 浏覽器的最新版都完全支持存儲事件,並且這裡給出的代碼都能在這些浏覽器中完美地運行。


結束語

本系列其他文章

  • 使用 HTML 5 創建移動 Web 應用程序,第 1 部分:聯合使用 Html 5、地理定位 API 和 Web 服務來創建移動混搭程序

作為一名開發人員,突然在客戶機上擁有巨額的存儲空間,您會覺得自己獲得了很大的解放。對於長期的 Web 開發人員來說,為做到他們多年來一直想做、卻苦於找不到好的方式來做的事情帶來了轉機。對於移動開發人員來說,則更為振奮人心,因為它真正開啟了數據的本地高速緩存。除了大大改善應用程序的性能之外,本地高速緩存對於推進移動 Web 應用程序的另一個新的令人振奮的功能 —— 離線 —— 是很關鍵的。這將是本系列下一篇文章的主題。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved