Windows phone 应用开发[14]-调用WebBrowser
很久沒有更新博客了.最近一直陷身在項目中難以有時間抽身梳理總結(jié).關(guān)于博客確實很多想寫的主題.節(jié)前大概草草 的梳理一下大概就有十幾個主題.只能趁著放假的時間來逐漸把這批文章力所能及系統(tǒng)的更新出來. 主要涉及到我們團隊現(xiàn)在Windows phone 項目開發(fā)中實際碰到一些問題和對應(yīng)解決方案.如果想關(guān)注即時了解每天動態(tài)信息可以直接在Sina微博@chenkaiHome 溝通交流.
在開始更新這批博文前.一直在顧慮先更新那個主題為好.回頭一想索性就說說這半個月有些苦惱的Windows phone中處理 WebBrowser在我們項目中表現(xiàn)出來問題.
話說去年.技術(shù)團隊提出要優(yōu)化產(chǎn)品在各個平臺[IOS/Android/WP7/QT]客戶端開發(fā)業(yè)務(wù)流程.提出這個問題主要是為了把原來通用業(yè)務(wù)邏輯流程封裝到能力更大的服務(wù)器端來做.各個客戶端在通過WebView統(tǒng)一的形式調(diào)用.這樣做目的.主要是解決原來各個客戶端在業(yè)務(wù)升級后更新客戶端版本時減少重新開發(fā)量.這樣一來把核心的業(yè)務(wù)邏輯變動全部集中服務(wù)器端.需求變化自上而下傳遞過程中.在各個平臺之間可以復(fù)用. 在每次更迭客戶端版本時提升開發(fā)團隊效率.
最開始我們采用的方案焦點主要是考慮到WebView封裝通用的業(yè)務(wù)邏輯流程涉及到與對應(yīng)平臺原生應(yīng)用程序的交互問題上.所以也就理所當(dāng)然有人提出跨平臺移動框架PhoneGap[平臺交互]+HTML 5[UI呈現(xiàn)]的處理方案. 原來我們設(shè)想真的很簡單.也天真的認(rèn)為PhoneGap+HTML 5搭配會把通用業(yè)務(wù)流程問題迎刃而解.
首先.PhoneGap[PS參考:Windows phone 應(yīng)用開發(fā)[8]-體驗PhoneGap]作為移動跨平臺框架.著重解決的問題是通過JavaScript實現(xiàn)跨平臺API交互.并沒有UI.頁面還需要借助HTML 5效果.即使如此.也是難以和原生應(yīng)用程序界面相媲美的.這就需要在使用過程中.要犧牲掉大量原生應(yīng)用程序交互細(xì)節(jié).用戶體驗上打了一個折扣.當(dāng)然這和我們解決的核心問題做出犧牲還是值得.但問題各個平臺兼容性需要調(diào)整各個平臺適配問題大大出乎我們預(yù)估.而在性能差異上更是難以兼顧保證的.而對于初次使用PhoneGap團隊解決這些問題所耗費的開發(fā)周期時間.卻遠大于開發(fā)Native Application原生應(yīng)用時間還要長.這完全和使用初衷相背離. 其實問題只是換了一種形式存在. 我們只是從一個熟悉能夠預(yù)估量的泥潭跳到另外完全未知泥潭中.不斷嘗試掙扎…
談到這.說一個細(xì)節(jié).類似在WebBrowser瀏覽器控件中.打開一個新窗口的問題.Android平臺可以采用LoadUrl方法直接打開一個新窗體實現(xiàn)Js控制的頁面跳轉(zhuǎn). 而目前Windows phone WebBrowser情況不支持在當(dāng)前頁打開新窗體.雖然可以通過InvokeJavaScript()放在LoadComplated方法中注入Js控制方法替換打開方式來實現(xiàn). 但在實際調(diào)試中會發(fā)現(xiàn).開發(fā)人員在C# 后臺代碼中調(diào)試JavaScript來說是一個挑戰(zhàn). 一來WebBrowser在注入和執(zhí)行Js過程返回的錯誤或異常都是簡單的代碼80開頭Code.而沒有具體的堆棧信息. 這對找錯和確認(rèn)問題照成很大障礙.另外在C#后臺代碼處理JS缺乏有效的調(diào)試工具支持.這對PhoneGap封裝出來頁面復(fù)雜的Js調(diào)用或數(shù)據(jù)交互操作.照成一定難題.
說白了.在PhoenGap中通過JS實現(xiàn)Windows phone應(yīng)用平臺交互主要體現(xiàn)在兩個點上.第一就是通過在Js中調(diào)用:
【JAvaScript:】
window.external.Notify(“”);
方法把頁面交互數(shù)據(jù)通過WebBrowser控件SCriptNotify事件接收傳遞給原生應(yīng)用程序. 另外一個點.就是可以在在Windows phone應(yīng)用程序直接調(diào)用WEbBrowser控件InvokeScript()方法來調(diào)用JavaScript函數(shù). 這兩個方法.實現(xiàn)了PhoneGap數(shù)據(jù)傳遞和交互整個過程.
那在Windows phone應(yīng)用程序使用WebBrowser有哪些常見需要解決的問題?
[1]異常處理.
well.這里不得不首先說在WebBrowser調(diào)用JavaSCript時需要處理的異常問題.Windows Phone 提供一個基于桌面版本的 Silverlight 的 WebBrowser 控件,也就是說WindowsPhone 目前的WEbBrowser控件是基于Silverlight桌面版本的WebBrowser控件而來,但仍然有幾處不同[WEbBrowser與Silverlight版本不同地方].其中兩個版本在開發(fā)最大不同主要有如下幾點:
Windows phone WebBrowser控件與Silverlight 桌面版本的不同:
[1]Windows phone 版本相對Silverlight版本具有直接使用 IsolateStroage獨立存儲的權(quán)限
[2]相對Silverlight在執(zhí)行InvokeScript()方法時限制了執(zhí)行范圍必須是XAP 程序包相同的站點中加載的腳本.而Windows phone 解除該限制.
[3]在Windows phone版本時從獨立存儲加載的內(nèi)容或使用 NavigateToString(String) 方法加載的內(nèi)容沒有跨站點訪問限制。
那么在Windows phone WebBrowser中調(diào)用JavaScript常見的可能出現(xiàn)的異常主要有兩個,如下重現(xiàn)這兩個異常情況.首先創(chuàng)建一個Windows Phone Application 應(yīng)用程序. Mainpage.CS:
1: <!--ContentPanel - place additional content here--> 2: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> 3: <StackPanel> 4: <phone:WebBrowser x:Name="ComponentContent_WB" Height="450"/> 5: <Button x:Name="ExcuteScript_BT" Content="Excute JavaScript" Margin="0,50,0,0" Click="ExcuteScript_BT_Click"></Button> 6: </StackPanel> 7: </Grid>定義一個WebBrowser和Button按鈕用來在頁面加載完成后執(zhí)行JavaSCripti函數(shù)事件.當(dāng)然在執(zhí)行JavaSCript需要設(shè)置WEbbrowser可以調(diào)用JS的. 設(shè)置IsScriptEnabled="True" BehindCode 如下:
1: // Constructor 2: public MainPage() 3: { 4: InitializeComponent(); 5: this.Loaded += new RoutedEventHandler(MainPage_Loaded); 6: } 7: ? 8: void MainPage_Loaded(object sender, RoutedEventArgs e) 9: { 10: string navigateUrl = @"http://www.163.com"; 11: this.ComponentContent_WB.Navigate(new Uri(navigateUrl, UriKind.RelativeOrAbsolute)); 12: } 13: ? 14: private void ExcuteScript_BT_Click(object sender, RoutedEventArgs e) 15: { 16: //Button Client Event Excute InvokeJavaScript Method 17: try 18: { 19: this.ComponentContent_WB.InvokeScript("DefineNoExistJSMethod"); 20: } 21: catch (Exception se) 22: { 23: MessageBox.Show("Excute JavaScript Have Exception:" + se.Message); 24: } 25: }調(diào)用通用網(wǎng)易站點.在加載頁面完成后通過Button按鈕執(zhí)行一個不來就不存在JavaScript函數(shù).執(zhí)行效果如下":
因這個JavaScript函數(shù)不存在所以執(zhí)行肯定報錯.注意這里報錯信息是以80020006為開頭的UnKnowError如下:
可見在堆棧的異常信息一欄中對JavaScripit提供的信息非常有限.這個Message代碼為80020006.其實就是在當(dāng)前應(yīng)用程序執(zhí)行范圍找不到該JavaScript方法.另外一種情況恰恰相反.在執(zhí)行已經(jīng)定義JavaScript Function 函數(shù)出現(xiàn)的異常. 類似找到163.com站點中一個任意JAvaScript函數(shù)在后臺方法調(diào)用:
1: function NTESAutoComplete ( inputElem, nextElem ) { 2: var t = this; 3: t._inputElem = inputElem; 4: t._nextElem = nextElem; 5: t._idName = "login_auto_list"; 6: t._className = "login-auto-list"; 7: }注意InvokeScript方法在執(zhí)行帶有JAvaScript參數(shù)時. 參數(shù)傳遞是以String[]數(shù)組方式傳遞給JAvaScript函數(shù).調(diào)用:
1: private void ExcuteScript_BT_Click(object sender, RoutedEventArgs e) 2: { 3: //Button Client Event Excute InvokeJavaScript Method 4: try 5: { 6: this.ComponentContent_WB.InvokeScript("NTESAutoComplete",new string[]{"NoExistElement", "NoExistStringArgument"}); 7: } 8: catch (Exception se) 9: { 10: MessageBox.Show("Excute JavaScript Have Exception:" + se.Message); 11: } 12: }執(zhí)行效果如下:
執(zhí)行過程中InvokeScript得到異常 "An unknown error has occurred. Error: 80020101".而這個異常是在往往執(zhí)行過程JavaScript內(nèi)部錯誤引起.因在后臺代碼沒有有效的工具.支持.所以對于JavaScript的錯誤是很難查找確認(rèn)問題具體在那. 這個問題出現(xiàn)一般會有兩種大概原因.
第一點.在調(diào)用InvokeScript()是WebBrowser控件事件執(zhí)行順序.其實針對WEbBrowser控件.除了從FrameworkElement類和Control類繼承了通用了UIElement屬性和方法外.WEbBrowser重點擴展自身導(dǎo)航操作.類似其中三個比較中重要的方法.Navigating、Navigated 和 LoadCompleted事件.
那么說到這 這個三個事件在實際操作執(zhí)行順序是?
WebBrowser導(dǎo)航事件的執(zhí)行順序:
Navigating > Navigated > LoadCompleted
Navigating是執(zhí)行Navigate方法表示當(dāng)前WEbBrowser正在執(zhí)行加載URL操作、Navigated事件WebBrowser 控件成功導(dǎo)航后發(fā)生 和 LoadCompleted事件在 WebBrowser 控件成功加載內(nèi)容后發(fā)生.
如果在這種情況下.即使我們發(fā)現(xiàn)我們Codebehind中InvokeScript()調(diào)用JS沒有問題.同時HTML JavaScript函數(shù)測試也沒有問題.這就導(dǎo)致我們始終無法通過程序測試找到JS 報錯80020101異常在那. 這是在后臺代碼調(diào)試JAvaScript最讓人痛苦的地方.比如我們在如下方法掉用如上JavaScript函數(shù):
void Wb_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) { Wb.InvokeScript("eval", "document.forms[0].submit();"); // Throws 80020101 } ? private void MainPage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Wb.InvokeScript("eval", "document.forms[0].submit();"); // Works }可以發(fā)現(xiàn).如果在Navigated事件觸發(fā)時.即使我們后臺代碼調(diào)用和JavaScript函數(shù)都沒有錯誤.依然還是爆出80020101的異常.這主要是因為DOM對象操作在頁面觸發(fā)Navigated事件還沒有完全初始化.導(dǎo)致調(diào)用頁面執(zhí)行時出現(xiàn)異常.
第二點.則是比較常見的即使需要對JavaSCript做一定修改.確保JS函數(shù)在執(zhí)行時不會出錯. 則這個80020101異常一般都會在如上兩種情況下出現(xiàn).
[2]現(xiàn)實靜態(tài)頁面.
在WEbBrowser中.可能需要在沒有網(wǎng)絡(luò)情況下.需要在某一些情況下通過后臺應(yīng)用程序操作HTML頁面. 而在Windows phone提供兩種方式來加載本地靜態(tài)的HTML頁面.
在Windows phone 中WEbBrowser中提供NavigateToString方法將 HTML 字符串置于 Web 瀏覽器控件中以便進行呈現(xiàn).操作也是簡單的:
1: string defineHtmlStr = @"<html> 2: <head> 3: <script> 4: function DefineExistFun(elementStr) 5: { 6: var getElems=document.getElementByTag(elementStr); 7: alert(elementStr); 8: } 9: </script> 10: <body> 11: <a href=" + "http://chenkai.cnblogs.com" + ">Test</a>" 12: + "</body>" 13: + "</head></html>"; 14: this.ComponentContent_WB.NavigateToString(defineHtmlStr);加載頁面效果:
另外一種方式.則是加載一定定制好靜態(tài)HTML頁面.使用 WebBrowser 控件在應(yīng)用程序中顯示已設(shè)置格式的靜態(tài)內(nèi)容。例如,開發(fā)人員可能希望在應(yīng)用程序包中包含幫助文本,以便用戶可以隨時訪問.創(chuàng)建一個靜態(tài)HTML界面:
1: <html> 2: <head> 3: <script> 4: function DefineExistFun(elementStr) 5: { 6: var getElems=document.getElementByTag(elementStr); 7: alert(elementStr); 8: } 9: </script> 10: <body> 11: <a href="http://chenkai.cnblogs.com">Test</a> 12: </body> 13: </head> 14: </html>在執(zhí)行第一步需要把該CreateProduct.html頁面添加解決方案.設(shè)置引用資源為Content.需要向獨立存儲中添加存儲靜態(tài)文件.:
1: private void SaveFilesToIsoStore() 2: { 3: //These files must match what is included in the application package, 4: //or BinaryStream.Dispose below will throw an exception. 5: string[] files = { 6: "CreateProduct.html" 7: }; 8: ? 9: IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication(); 10: ? 11: if (false == isoStore.FileExists(files[0])) 12: { 13: foreach (string f in files) 14: { 15: StreamResourceInfo sr = Application.GetResourceStream(new Uri(f, UriKind.Relative)); 16: using (BinaryReader br = new BinaryReader(sr.Stream)) 17: { 18: byte[] data = br.ReadBytes((int)sr.Stream.Length); 19: SaveToIsoStore(f, data); 20: } 21: } 22: } 23: } 24: ? 25: private void SaveToIsoStore(string fileName, byte[] data) 26: { 27: string strBaseDir = string.Empty; 28: string delimStr = "/"; 29: char[] delimiter = delimStr.ToCharArray(); 30: string[] dirsPath = fileName.Split(delimiter); 31: ? 32: //Get the IsoStore. 33: IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication(); 34: ? 35: //Re-create the directory structure. 36: for (int i = 0; i < dirsPath.Length - 1; i++) 37: { 38: strBaseDir = System.IO.Path.Combine(strBaseDir, dirsPath[i]); 39: isoStore.CreateDirectory(strBaseDir); 40: } 41: ? 42: //Remove the existing file. 43: if (isoStore.FileExists(fileName)) 44: { 45: isoStore.DeleteFile(fileName); 46: } 47: ? 48: //Write the file. 49: using (BinaryWriter bw = new BinaryWriter(isoStore.CreateFile(fileName))) 50: { 51: bw.Write(data); 52: bw.Close(); 53: } 54: }把需要展示的靜態(tài)HTML頁面在調(diào)用前需要存儲到獨立存儲中.調(diào)用如下:
1: void MainPage_Loaded(object sender, RoutedEventArgs e) 2: { 3: SaveFilesToIsoStore(); 4: ComponentContent_WB.Navigate(new Uri("CreateProduct.html", UriKind.Relative)); 5: } 成功加載的頁面:針對在Windows phone WEbBrowser中于JavaScript交付問題的問題出現(xiàn)的異常.在后臺代碼上處理是非常弱的.首先在CodeBehind中沒有成行JS調(diào)試工具支持.這對不熟悉前段JavaSCript代碼的開發(fā)人員來說是一個挑戰(zhàn). 另外一個問題就是一旦調(diào)用JavaScript出現(xiàn)異常情況.很難確認(rèn)問題源頭.這也大大影響開發(fā)效率.
當(dāng)然在WEbBroser還涉及到頁面加載控制. 新窗口打開. 控制WEbBrowser頁面縮放等問題.這里就不再一一贅述.
關(guān)于本片源碼詳見:https://github.com/chenkai/WebBrowser-Case-Windows-phone-Sample
源碼下載:/Files/chenkai/WebBrowserWP7Demo.rar
如有問題可以Weibo上溝通交流:http://weibo.com/chenkaihome
?
轉(zhuǎn)載于:https://www.cnblogs.com/chenkai/archive/2012/04/04/2432120.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的Windows phone 应用开发[14]-调用WebBrowser的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 想以游戏纸娃娃系统专利主张暴雪的暗黑3侵
- 下一篇: windows phone 7 中文天气