若是你仍是第一次與app合做開發webview的頁面,那麼對於如何調試,可能有哪些問題多是不夠了解的。本文嘗試性的根據本身的經驗給你們一個入門級別的瞭解,若是是大佬級別的,能夠繞路了。javascript
爲了更好的在app中調試開發咱們的移動頁面(h5),咱們須要與app開發人員約定一些基本的原則,來保證咱們的頁面能夠很好的進行調試,包括調試工具、靈活的模擬上線時的app環境、測試交互過程當中的問題、方便自定義的修改成本身的h5地址等。前端
如下的方案僅供參考,每一條都是有實際用途的,若是公司裏的webview須要進行準確的調試和後續開發,必要性的須要考慮如下的問題。java
主題 | 方案 | 備註 |
---|---|---|
統一肯定的ua標識 | 好比ua結尾加入【xxx】 | 無 |
h5公用的app頭 | app端提供統一的app頭,參考支付寶以及微信的ua交互,提供顯示頁面標題,返回,關閉的簡單操做,默認頁面可滾動 | 以後其餘的h5默認在這個類瀏覽器外殼中,針對前端一些固定佈局的方案,須要優化完善這個外殼,前端技改時間容許的話,最好給出完整肯定的方案能夠在webview中無縫對接和展現 |
h5與app定製頭 | 針對產品以及交互特殊需求,提供的特殊頁面,好比單頁,強交互邏輯頁定製專門的頭 | 須要產品明確說明特殊性,不是瀏覽器的返回,好比返回須要加確認框,就須要定製 |
h5與app功能性交互 | 約定常規交互方法的格式,並給出相互通信的一些固定的可用的方法,好比獲取用戶信息,獲取app網絡狀態 | 這個是雙向的功能性交互,h5的一些方法也能夠設置app的狀態,頁面跳轉,數據存儲等 |
h5與app純交互性方法 | 調取相應app的加載框,加載失敗,相冊控件,掃碼控件 | 須要與產品,交互統一肯定是否使用app原生控件仍是h5效果。 |
h5與app不一樣場景的分享互通 | 好比:用戶在不一樣app中:app分享到h5詳情頁,h5詳情頁也能夠對應到app中打開 | 須要約定規則 |
app提供webview的外殼 | 能夠經過app外殼掃碼進入webview場景,模擬交互,開發階段暴露解決一些app中問題 | 以上的app解決方案集成在這個app外殼中 |
做爲常識咱們知道,通常狀況下webview的頁面是包括兩種狀況的,一種是原本就可能限定於只有app會嵌入的h5頁面,這部分在與app進行通信的時候,咱們更多的是經過約定jsBridge的方式。一種是普通的h5頁面。ios
jsBridge說的更直白一點,就是網頁在載入時,向頁面內注入一個指定的js文件,而後頁面內就會有一個前端和app都知道的方法,經過這個方法前端能夠喚起app的交互控件,甚至是跳轉到其餘的app頁面,也能夠知道app此時的一些設備狀態、網絡狀態、用戶信息等。而app也能夠經過h5知道此時頁面的狀態,進而根據須要作能夠在必要時喚起想要的操做。git
而另外一方面,webview也可看作一個普通的瀏覽器,能夠載入任何的頁面,因此咱們非app的內嵌頁的h5也能夠在app內經過webview進行打開; 而app外的h5能夠經過app自定義的協議碼來喚起app。github
相關的通信技術點可見下面的簡陋的圖說明。web
iOS與H5交互,採用是JavaScriptCore方式。原理是iOS端在WebView加載完一個URL連接的時候,手動向H5頁面綁定一個JSContext
對象。利用這個JSContext對象,能夠實現OC與JS間的雙向交互。注意:JSContext對象是在iOS的webViewDidFinishLoad:
回調裏完成綁定的,在完成綁定前沒法使用JS與OC的任何交互。objective-c
js的window.isReady方法已經觸發過了,JS才能調用OC提供的方法。緣由是iOS會在JSContext對象綁定成功後,纔會向js端調用isReady方法,因此js只有等isReady觸發了,才能經過JSContext調用OC方法。chrome
iOS在綁定JSContext對象的時候,要約定好一個字段,而後OC會將原生方法註冊到網頁window對象的這個字段上。好比window.app
。json
js端若是要異步接收原生方法的返回結果,須要在全局做用域內定義好回調方法
js調用OC原生方法,同步獲取用戶基本信息
// 約定好獲取用戶信息接口註冊到window的app屬性上
// getUserInfo方法是一個同步方法,能夠js端能夠直接獲取到返回值
// 返回的對象能夠是json字符串
var info = window.app.getUserInfo()
複製代碼
js調用OC原生方法,拍照上傳做業圖片,並異步獲取上傳結果
// 假設約定好做業相關的OC接口都註冊到window的homework屬性上
window.homework.uploadHomeworkPicture(questionID)
// 在全局做用域內定義好回調方法,用於接收返回值
// 原生方法會在上傳完做業圖片的時候,間接調用該回調方法
function homeworkPictureDidUploaded(questionID, picUrl) {
// do something...
}
複製代碼
/** 跳轉到課程詳情 @param productId 商品ID(string類型) */
app.gotoCourseDetail(productId);
/** 關閉當前頁面 */
app.finish();
/** 獲取用戶信息,已json字符串形式返回。主要字段以下: memberId: 用戶id token: 用戶登陸惟一標識 memberType: 用戶類型 */
app.getUserInfo();
/** toast提示 @param msg 提示語(string類型) */
app.toast(msg);
/** 顯示對話框 @param title 標題(string字符串) @param msg 消息(string字符串) @param actions 點擊事件(一個json數組字符串),每一個數組元素字段以下: title: 事件標題(string字符串,好比「取消」) callback: 事件的js回調方法(string字符串) 示例: var actions = "[{'title': '取消', 'callback': 'cancelPay'}, {'title': '肯定', 'callback': 'confirmPay'}]"; app.confirm("舒適提示", "是否支付訂單?", actions); */
app.confirm(title, msg, actions);
複製代碼
js方法應該申明到全局做用域內,不然OC獲取不到該方法
若是在webViewDidFinishLoad:
直接用過JSContext調用js方法,可能會出現調用無效的請求。爲了不此類問題,推薦以setTimeout
方式調用js方法
OC在webViewDidFinishLoad:中調用js的isReady
方法
// setTimeout是JS的自帶方法
// 這裏使用setTimeout的目的是爲了將isReady方法放到js調用隊列的最後
JSValue *isReadyFunc = self.jsContext[@"isReady"];
if (isReadyFunc) {
[self.jsContext[@"setTimeout"] callWithArguments:@[isReadyFunc, @100]];
}
複製代碼
OC在JS發起的原生方法中調用js的setUserInfo方法
// 注意,JavaScriptCore支持NSDictionary、NSArray類型做參數傳給js方法
NSDictionary *userInfo = ...;
[self.jsContext[@"setUserInfo"] callWithArguments:@[userInfo]];
複製代碼
/** iOS原生初始化完成後調用本方法,告訴js已經準備好了 */
function isReady();
/** * return boolean 類型返回值: true h5已經處理了返回,native不處理; false h5沒有處理返回,native直接返回上級原生頁面 */
function gobackIfNeeded();
複製代碼
定義scheme: com.xxx.app
WebView的默認UserAgent爲:"xxxx XXX/1.3.0", 其中xxxx爲系統默認UserAgent。''/''後爲app版本號
1.原生提供一個框架頁面給H5頁面。框架只提供一個叫InnerWeb
的類(這點js不須要知曉).如何須要在本地加載一個純H5的內嵌頁面,請使用IntentHelper.startWeb(Context context, String url)
方法去載入一個內嵌H5頁面。具體內部只是加載這個url。以後的邏輯都交給H5處理。
原生調用js方法分一下兩種類型的方法:
很簡單,咱們直接調用便可具體代碼示例以下:
/** * f1 爲js中聲明的函數 */
mWebView.loadUrl("javascript:f1()");
複製代碼
這樣就能夠調用js的方法了。
稍微複雜一點,以下:
/** * sum 爲js中定義的函數 */
mWebView.evaluateJavascript("sum(1,2)", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.e(TAG, "onReceiveValue value=" + value);
}
});
複製代碼
本地提供給js調用的映射對象,這須要注入,咱們同一使用一個叫app
的對象。js如要調用本地方法。都以此開頭來表明咱們原生方法。
具體以下:
<script type="text/javascript">
function s(){
// 注意下面的‘app’
var result = window.app.gotoLogin();
document.getElementById("p").innerHTML = result;
}
</script>
複製代碼
本地代碼以下:
public calss AppJavascriptInterface {
@JavascriptInterface
public void gotoLogin() {
if (mContext.get() == null) {
Log.w("web", "頁面已關閉");
return;
}
LoginActivity.start(mContext.get());
}
}
複製代碼
協議的主要以原生提供給H5的爲主, 下面是我詳細羅列的:
/** * 跳轉登陸 */
@JavascriptInterface
public void gotoLogin();
/** * 跳轉課程詳情 */
@JavascriptInterface
public void gotoCourseDetail();
/** * 關閉當前頁面 */
@JavascriptInterface
public void finish();
/** * 關閉當前頁面獲取當前用戶信息,若是爲空,說明用戶未登陸 * * 目前有以下信息(以json格式返回給H5): * memberId: 用戶id * token: 用戶登陸惟一標識 * memberType: 用戶類型 */
@JavascriptInterface
public String getUserInfo();
/** * 返回 * * 目前有以下信息(以json格式返回給H5): * memberId: 用戶id * token: 用戶登陸惟一標識 * memberType: 用戶類型 */
public void back();
/** * */
public void toast(String msg);
public void confirm(String title, String msg, String positiveFunctionName, String negativeFunctionName);
複製代碼
/** * return boolean 類型返回值: true h5已經處理了返回,native不處理; false h5沒有處理返回,native返回上一個非H5頁面 */
function gobackIfNeeded(); 複製代碼
app的喚醒方式方案:
1.定義scheme: com.xxx.app
2.另外具體頁面的打開待定
約定ua: "xxxx XXX/1.3.0", 其中xxxx爲系統默認ua。iOS與Android不同。"/"後爲app版本號
h5喚起app已經變成了目前不可或缺的功能之一,做爲前端技術棧的必備技術棧之一,咱們須要知道如何在非app環境內喚起app,以及正確識別是系統中是否安裝了app.
應用名稱 | URL Scheme |
---|---|
短信 | sms:// |
app store | itms-apps:// |
電話 | tel:// |
無線局域網 | App-Prefs:root=WIFI |
藍牙 | App-Prefs:root=Bluetooth |
蜂窩移動網絡 | App-Prefs:root=MOBILE_DATA_SETTINGS_ID |
我的熱點 | App-Prefs:root=INTERNET_TETHERING |
運營商 | App-Prefs:root=Carrier |
通知 | App-Prefs:root=NOTIFICATIONS_ID |
通用 | App-Prefs:root=General |
通用-關於本機 | App-Prefs:root=General&path=About |
通用-鍵盤 | App-Prefs:root=General&path=Keyboard |
通用-輔助功能 | App-Prefs:root=General&path=ACCESSIBILITY |
通用-語言與地區 | App-Prefs:root=General&path=INTERNATIONAL |
通用-還原 | App-Prefs:root=Reset |
牆紙 | App-Prefs:root=Wallpaper |
Siri | App-Prefs:root=SIRI |
隱私 | App-Prefs:root=Privacy |
Safari | App-Prefs:root=SAFARI |
音樂 | App-Prefs:root=MUSIC |
音樂-均衡器 | App-Prefs:root=MUSIC&path=com.apple.Music:EQ |
照片與相機 | App-Prefs:root=Photos |
FaceTime | App-Prefs:root=FACETIME |
應用名稱 | URL Scheme |
---|---|
微博 | weibo:// |
mqq:// | |
QQ羣組 | mqqapi://card/show_pslcard?src_type=internal&version=1&card_type=group&uin={QQ羣號} |
QQ聯繫人 | mqqapi://card/show_pslcard?src_type=internal&version=1&uin={QQ號碼} |
支付寶 | alipay:// |
微信 | weixin:// |
微信 | wechat:// |
微信-掃一掃 | weixin://dl/scan |
微信-反饋 | weixin://dl/feedback |
微信-朋友圈 | weixin://dl/moments |
微信-設置 | weixin://dl/settings |
微信-消息通知設置 | weixin://dl/notifications |
微信-聊天設置 | weixin://dl/chat |
微信-通用設置 | weixin://dl/general |
微信-公衆號 | weixin://dl/officialaccounts |
微信-遊戲 | weixin://dl/games |
微信-幫助 | weixin://dl/help |
微信-我的信息 | weixin://dl/profile |
微信-功能插件 | weixin://dl/features |
蝦米音樂 | xiami:// |
chrome | googlechrome:// |
微博國際版 | weibointernational:// |
摩拜單車 | mobike:// |
ofo | ofoapp:// |
有道雲筆記 | youdaonote:// |
印象筆記 | evernote:// |
今日頭條 | snssdk141:// |
網易新聞 | newsapp:// |
網易雲音樂 | orpheuswidget:// |
QQ音樂 | qqmusic:// |
QQ音樂最近播放 | qqmusic://today?mid=31&k1=2&k4=0 |
美團外賣 | meituanwaimai:// |
美團 | imeituan:// |
Gmail | googlegmail:// |
網易郵箱 | neteasemail:// |
QQ郵箱 | qqmail:// |
騰訊視頻 | tenvideo:// |
愛奇藝 | iqiyi:// |
12306 | cn.12306:// |
有道詞典 | yddict:// |
釘釘 | dingtalk:// |