編輯:安卓省電與加速
簡介: 一直以來,Web 應用程序被局限在一個單線程世界中。這的確限制了開發人員在他們的代碼中的作為,因為任何太復雜的東西都存在凍結應用程序 UI 的風險。通過將多線程引入 Web 應用程序,Web Workers 扭轉了這一不利局面。這對於大部分應用程序邏輯都位於客戶端的移動 Web 應用程序來說尤其有用。在本文中,您將了解如何使用 Web Workers 並發現哪些任務最適合它們。您還將看到如何使用其他 Html 5 技術才能提高使用那些技術的效率。
關於本系列
HTML 5 是一項被大肆宣揚的技術,但是它實至名歸。它有望成為一個技術引爆點,將桌面應用程序功能引向浏覽器。它不僅適用於傳統浏覽器,甚至也針對移動浏覽器。更好的是,最流行的移動浏覽器已經采納和實現 HTML 5 規范的很多重要部分。在這個五部分的系列中,我們將詳細了解幾個新技術,它們都是 Html 5 的一部分,可以大大影響移動 Web 應用程序開發。在每一部分中,都將開發一個可以工作的移動 Web 應用程序,展示一個可以用於現代移動 Web 浏覽器(比如 iPhone 和基於 android 的設備上的浏覽器)的 Html 5 特性。
開始
在本文中,您將使用最新的 Web 技術開發 Web 應用程序。這裡的大部分代碼只是 Html、Javascript 和 CSS — 所有 Web 開發人員的核心技術。所需的最重要的工具是用於進行測試的浏覽器。本文大部分代碼將在最新桌面浏覽器上運行,但也有一些例外,我們將在文章中進行說明。當然,您也必須在移動浏覽器上測試,為此,您需要最新的 iPhone 和 Android SDKs。本文將使用 iPhone SDK 3.1.3 和 android SDK 2.1。本文的樣例還將使用一個代理服務器來從浏覽器訪問遠程服務。這個代理服務器是一個簡單的 Java™ servlet,但也可以使用以 PHP、Ruby 以及其他語言編寫的代理輕松替換。參見 參考資料 獲取鏈接。
移動設備上的多線程 JavaScript
對於大多數開發人員來說,多線程或並發編程並不新鮮。但是,JavaScript 並不是一種支持並發編程的語言。JavaScript 的創建者認為,對於 JavaScript 這樣旨在 Web 頁面上執行簡單任務的語言來說,並發編程容易出現問題,而且沒有必要。然而,由於 Web 頁面已經發展成為 Web 應用程序,使用 Javascript 完成的任務的復雜程度已經大大增加,向 JavaScript 提出了與其他語言同等的要求。與此同時,使用其他支持並發編程的語言工作的開發人員經常面臨伴隨線程和 mutexes 這樣的並發原語而來的超高復雜性的困擾。實際上,最近像 Scala、Clojure 和 F# 這樣的幾種新語言已經發展,它們都有可能簡化並發性。
Web Worker 規范不只是向 JavaScript 和 Web 浏覽器添加並發性,而且是以一種智慧的方式添加,這種方式將增加開發人員的能力,但不會向他們提供一種會導致問題的工具。 例如,多年來,桌面應用程序開發人員一直在使用多線程來支持他們的應用程序訪問多個 I/O 資源,以避免在等待這些資源時凍結 UI。然而,當這些多線程更改共享的資源(包括 UI)時,這樣的應用程序通常會出現問題,因為這種行為可能會導致應用程序凍結或崩潰。有了 Web Workers,這種情況就不會發生。衍生線程不能訪問主 UI 線程訪問的資源。事實上,衍生線程中的代碼甚至不能與主 UI 線程執行的代碼位於同一個文件中。
您甚至必須提供相應的外部文件作為構造函數的一部分,如 清單 1所示。
這個進程使用三個資源:
讓我們首先看看 清單 1 中的頁面腳本。
清單 1.在頁面腳本中使用一個 Web Worker
var worker = new Worker("worker.JS"); worker.onmessage = function(message){ // do stuff }; worker.postMessage(someDataToDOStuffWith);
在 清單 1 中,您可以看到使用 Web Workers 的三個基本步驟。首先,您創建一個 Worker 對象並向它傳遞將在新線程中執行的腳本的 URL。Worker 將執行的所有代碼都必須包含在一個 Worker 腳本中,該腳本的 URL 將被傳遞到這個 Worker 的構造函數中。這個 Worker 腳本的 URL 受到浏覽器的同源策略的限制 — 它必須來自加載這個頁面的同一個域,該頁面已加載正在創建這個 Web Worker 的頁面腳本。
下一步是使用 onmessage
函數指定一個回調處理器函數。這個回調函數將在該 Worker 腳本執行後調用。message
是從該 Worker 腳本返回的數據,您可以隨意處理該消息。回調函數在主線程上執行,因此它能訪問 DOM。Worker 腳本在一個不同的線程內運行且不能訪問 DOM,因此,您需要將來自這個 Worker 腳本的數據返回主線程,在那裡,您可以安全地修改 DOM 來更新您的應用程序的 UI。這是 Web Workers 的無共享設計的關鍵特性。
清單 1 中的最後一行展示如何通過調用 Worker 的 postMessage
函數來啟動它。這裡,您傳遞一條消息(重申一下,它只是數據)給 Worker。當然,postMessage
是一個異步函數;您調用它,它就立即返回。
現在,檢查這個 Worker 腳本。清單 2 中的代碼是來自 清單 1 的worker.JS
文件的內容。
清單 2. 一個 Worker 腳本
importScripts("utils.JS"); var workerState = {}; onmessage = function(message){ workerState = message.data; // do stuff with the message postMessage({responseXML: this.responseText}); }
可以看到,這個 Worker 腳本擁有自己的 onmessage
函數。該函數在您從主線程調用 postMessage
時調用。從頁面腳本傳來的數據被傳遞到 message
對象中的 postMessage
函數。您通過檢索 message
對象的 data
屬性來訪問該數據。當您處理完 Worker 腳本中的數據時,調用 postMessage
函數將數據返回主線程。主線程也可以通過訪問它接收到的消息的 data 屬性來訪問該數據。
至此,您已經見識了 Web Workers 的這個簡單、但強大的語義。接下來,您將了解如何應用這個語義來加速移動 Web 應用程序。在此之前,有必要先討論一下設備支持。畢竟,這些是移動 Web 應用程序,且處理不同浏覽器之間的功能的區別對於移動 Web 應用程序開發很重要。
設備支持
從 Android 2.0 開始,android 浏覽器就擁有了對 Html 5 Web Worker 規范的全面支持。在撰寫本文之時,最新的 Android 設備(包括非常流行的 Motorola Droid)已配置了 android 2.1。另外,此特性在運行 Maemo 操作系統的 Nokia 設備上的 Mozilla Fennec 浏覽器以及 Windows Mobile 設備上受到完全支持。這裡需要引起注意的遺漏是 iPhone。iPhone OS 3.1.3 和 3.2 版(在 iPad 上運行的 OS 的版本)並不支持 Web Workers。但是,此特性已在 Safari 上受到支持,因此,此特性在運行在 iPhone 上的 Mobile Safari 浏覽器上出現應該只是一個時間問題。鑒於 iPhone 的主導地位(尤其是在美國),最好不要依賴 Web Workers 的存在,且不要只在您檢測到它們的存在時才使用它們來增強您的移動 Web 應用程序。意識到這一點後,我們來看看如何使用 Web Workers 來加速您的移動 Web 應用程序。
使用 Workers 改善性能
智能手機浏覽器上的 Web Worker 支持很不錯,而且一直在不斷改進。這就提出了一個問題:什麼時候需要在移動 Web 應用程序中使用 Workers?答案很簡單:需要完成耗時的任務的任何時候。有些示例展示了如何將 Workers 用於執行密集的數學計算,比如計算 1 萬位數的圓周率。很可能您永遠也不需要在 Web 應用程序上執行這樣一個計算,在移動 Web 應用程序上執行這種計算的幾率則更小。但是,從遠程資源檢索數據則相當常見,這也是本文示例的關注點。
在這個示例中,您將從 eBay 檢索一個 Daily Deals(每天都在變化的交易)列表。這個交易列表包含關於每筆交易的簡短信息。更詳細的信息可以通過使用 eBay 的 Shopping API 獲取。當用戶浏覽這個交易列表選擇感興趣的商品時,您將使用 Web Workers 來預取這個附加信息。要從您的 Web 應用程序訪問所有這些 eBay 數據,您需要通過使用一個泛型代理(generic proxy)來處理浏覽器的同源策略。一個簡單的 Java servlet 將用於這個代理,它包含在本文的代碼中,但不在這裡單獨展示。相反,我們將把注意力集中在處理 Web Workers 的代碼上。清單 3 展示了這個交易應用程序的基本 Html 頁面。
清單 3. 交易應用程序 Html
<!DOCTYPE HTML> <html> <head> <meta http-equiv="content-type" content="text/Html; charset=UTF-8"> <meta name = "vIEwport" content = "width = device-width"> <title>Worker Deals</title> <script type="text/Javascript" src="common.JS"></script> </head> <body onload="loadDeals()"> <h1>Deals</h1> <ol id="deals"> </ol> <h2>More Deals</h2> <ul id="moreDeals"> </ul> </body> </Html>
可以看出,這是一段非常簡單的 Html;它只是一個 shell。您使用 JavaScript 檢索數據並生成 UI。這是移動 Web 應用程序的優化設計,因為它允許將所有代碼和靜態標記緩存到設備上,用戶只需等待來自服務器的數據。注意,在 清單 3 中,一旦那個 body 加載,您就調用 loadDeals
函數,在那裡,您將加載 清單 4 中的應用程序的初始數據。
清單 4. loadDeals
函數
var deals = []; var sections = []; var dealDetails = {}; var dealsUrl = "http://deals.ebay.com/feeds/xml"; function loadDeals(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (this.readyState == 4 && this.status == 200){ var i = 0; var j = 0; var dealsXml = this.responseXML.firstChild; var childNode = {}; for (i=0; i< dealsXml.childNodes.length;i++){ childNode = dealsXml.childNodes.item(i); switch(childNode.localName){ case 'Item': deals.push(parseDeal(childNode)); break; case "MoreDeals": for (j=0;j<childNode.childNodes.length;j++){ var sectionXml= childNode.childNodes.item(j); if (sectionXml && sectionXml.hasChildNodes()){ sections.push(parseSection(sectionXML)); } } break; default: break; } } deals.forEach(function(deal){ var entry = createDealUi(deal); $("deals").appendChild(entry); }); loadDetails(deals); sections.forEach(function(section){ var ui = createSectionUi(section); $("moreDeals").appendChild(ui); loadDetails(section.deals); }); } }; xhr.open("GET", "proxy?url=" + escape(dealsUrl)); xhr.send(null); }
清單 4 展示了 loadDeals
函數,以及應用程序中使用的全局變量。您使用了一個 deals 數組和一個 sections 數組。它們是相關交易的附加組(例如,Deals under $10
)。還有一個名為 dealDetails
的映射,其鍵是 Item IDs(來自於交易數據),其值是從 eBay Shopping API 獲取的詳細信息。
您首先調用一個代理,該代理又將調用 eBay Daily Deals REST API。這將把交易列表作為一個 XML 文檔提供給您。您解析用於進行 AJax 調用的 XMLHttpRequest 對象的 onreadystatechange
函數中的文檔。您還使用其他兩個函數,parseDeal
和 parseSection
,來將 XML 節點解析為更易於使用的 JavaScript 對象。這些函數可以在可下載的代碼樣例(參見 下載 部分)中找到,但由於它們只是令人厭煩的 XML 解析函數,因此我在這裡沒有包括它們。最後,在解析了 XML 後,您還使用了另外兩個函數,createDealUi
和createSectionUi
,來修改 DOM。此時,這個 UI 如 圖 1 所示。
圖 1. Mobile Deals UI
如果您返回 清單 4,就會注意到在加載主交易之後,您對這些交易的每個部分都調用了 loadDetails
函數。在這個函數中,您通過使用 eBay Shopping API 加載每個交易的附加細節 — 但前提是浏覽器支持 Web Workers。清單 5 展示了 loadDetails
函數。
清單 5. 預取交易細節
function loadDetails(items){ if (!!window.Worker){ items.forEach(function(item){ var xmlStr = null; if (window.localStorage){ xmlStr = localStorage.getItem(item.itemId); } if (xmlStr){ var itemDetails = parseFromXml(xmlStr); dealDetails[itemDetails.id] = itemDetails; } else { var worker = new Worker("details.JS"); worker.onmessage = function(message){ var responseXmlStr =message.data.responseXml; var itemDetails=parseFromXml(responseXmlStr); if (window.localStorage){ localStorage.setItem( itemDetails.id, responseXMLStr); } dealDetails[itemDetails.id] = itemDetails; }; worker.postMessage(item.itemId); } }); } }
在 loadDetails
中,您首先檢查全局作用域(window
對象)中的Worker
函數。如果該函數不在那裡,那麼無需做任何事。反之,您首先檢查 XML 的 localStorage
以獲取這個交易的細節。這是移動 Web 應用程序常用的本地緩存策略,本系列第 2 部分(參見 參考資料 部分的鏈接)詳細介紹過這種策略。
如果 XML 位於本地,那麼您在 parseFromXML
函數中解析它並將交易細節添加到 dealDetails
對象。反之,則衍生一個 Web Worker 並使用 postMessage
向其發送 Item ID。當這個 Worker 檢索到數據並將數據發布回主線程後,您解析 XML,將結果添加到dealDetails
,然後將 XML 存儲到 localStorage
中。清單 6 展示了這個 Worker 腳本:details.JS。
清單 6. 交易細節 Worker 腳本
importScripts("common.JS"); onmessage = function(message){ var itemId = message.data; var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (this.readyState == 4 && this.status == 200){ postMessage({responseXML: this.responseText}); } }; var urlStr = generateUrl(itemId); xhr.open("GET", "proxy?url=" + escape(urlStr)); xhr.send(null); }
這個 Worker 腳本非常簡單。您使用 AJax 調用代理,該代理又調用 eBay Shopping API。當您收到來自代理的 XML 後,使用一個 JavaScript 對象文字(object literal)將其發送回主線程。注意,即使您能夠使用來自一個 Worker 的 XMLHttpRequest
,但所有信息都將返回它的 responseText
屬性,而不是它的 responseXML
屬性。這是因為這個 Worker 腳本范圍內沒有 JavaScript DOM 解析器。注意,generateUrl
函數來自 common.JS 文件(見 清單 7)。您使用 importScripts
函數導入 common.JS 文件。
清單 7. Worker 導入的腳本
function generateUrl(itemId){ var appId = "YOUR APP ID GOES HERE"; return "http://open.api.ebay.com/shopping?callname=GetSingleItem&"+ "responseencoding=XML&appid=" + appId + "&siteid=0&version=665" +"&ItemID=" + itemId; }
現在,您已經知道如何(為支持 Web Workers 的浏覽器)填充交易細節,我們返回 圖 1 研究一下如何在應用程序中使用這種方法。注意,每筆交易旁邊都有一個 Show Details 按鈕,單擊該按鈕修改這個 UI,如 圖 2 所示。
圖 2. 顯示的交易細節
這個 UI 將在您調用 showDetails
函數時顯示。清單 8 展示了這個函數。
清單 8. showDetails 函數
function showDetails(id){ var el = $(id); if (el.style.display == "block"){ el.style.display = "none"; } else { el.style.display = "block"; if (!el.innerHtml){ var details = dealDetails[id]; if (details){ var ui = createDetailUi(details); el.appendChild(ui); } else { var itemId = id; var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (this.readyState == 4 && this.status == 200){ var itemDetails = parseFromXML(this.responseText); if (window.localStorage){ localStorage.setItem( itemDetails.id, this.responseText); } dealDetails[id] = itemDetails; var ui = createDetailUi(itemDetails); el.appendChild(ui); } }; var urlStr = generateUrl(id); xhr.open("GET", "proxy?url=" + escape(urlStr)); xhr.send(null); } } } }
您收到了即將顯示的交易的 ID 並切換是否顯示它。當該函數第一次調用時,它將檢查細節是否已經存儲到 dealDetails 映射中。如果浏覽器支持 Web Workers,那麼這些細節已經存儲且它的 UI 已經創建並添加到 DOM 中。如果這些細節還沒有加載,或者,如果浏覽器不支持 Workers,那麼您需要執行一個 AJax 調用來加載此數據。這就是這個應用程序無論在有無 Workers 時都同樣能正常工作的原因。這意味著,如果 Workers 受到支持,那麼數據就已被加載且 UI 將立即響應。如果沒有 Workers,UI 仍將加載,只是需要花費幾秒鐘時間。
結束語
對於 Web 開發人員來說,Web Workers 聽起來就像一種外來的新技術。但是,如本文所述,它們是非常實用的應用程序。這對於移動 Web 應用程序來說尤其正確。這些 Workers 可用於預取數據或執行其他預先操作,從而提供一個更加實時的 UI。這對於需要通過網速可能較慢的網絡加載數據的移動 Web 應用程序來說尤其正確。結合使用這種技術和緩存策略,您的應用程序的快捷反應將使您的用戶感到驚喜!
第一步,確保您的三星i9300已經ROOT。 第二步,下載並安裝RE管理器。 第三步,打開RE管理器,進入/system/etc目錄,找到文件gps.conf。
安卓5.0耗電怎麼辦?如果擔心最新安卓系統很快掉電其實可以學一些省點技巧,下面就一起來看看安卓5.0省電模式怎麼開啟的方法吧,希望能幫到大家!省電模式此前已經在許多廠商的
ROM介紹: ROM大小:131 MB 版本:2.3.7 UI類型:其它 1.小破孩兒MIUI開機動畫。 2.優化了ROM啟動速度、優化了GPS秒定。 3.全局滑動開
安卓手機耗電的問題一直是困擾著很多的安卓用戶,不少使用者發現這手機突然間會變得很耗電,這對於手機的待機有很大的影響