Hybrid混合開發知識點(一)
Hybrid是半Native半Web開發模式,充分利用H5的跨平臺、快速迭代能力以及Native的流暢性、系統API呼叫能力,具有可複用性高、開發成本低、跨平臺開發的特點。
在闡述Hybrid混合開發知識點之前,我們先梳理下WebView載入H5頁面及H5與Android的互動等方面的知識點。
WebView常用API及屬性配置
WebViewClient類:處理各種通知&請求事件。常用方法如下:
- shouldOverrideUrlLoading():在本WebView中開啟網頁, 只在重定向和點選網頁內連結時觸發 ,且先於onPageStarted()。
返回false表示預設由webview載入URL,返回true表示不載入URL,使用者自行處理。 - onPageStarted():載入頁面時呼叫,載入頁面、點選連結、返回上一個網頁都會觸發。
- onLoadResource():載入頁面資源時回撥,如每張圖片載入都會呼叫。
- onReceivedSslError():webview預設不處理https請求,此方法針對https請求做處理。如handler.proceed();
- onReceivedError():頁面載入異常時會觸發,可根據errorCode替換assets目錄下預配置的對應載入反饋頁面。
WebChromeClient類:輔助webview處理javaScript對話方塊,載入進度,網站圖示,網站標題等。
WebSettings常用屬性:
- setJavaScriptEnabled(true):設定允許執行javaScript指令碼,預設false.
- setSupportZoom(true):設定是否支援縮放,預設true.
- setBlockNetworkImage(false):把圖片載入放在最後來載入渲染.
- setAllowFileAccess(true):設定可以訪問檔案.
- setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN):支援內容重新佈局.
- setUseWideViewPort(true):擴大比例的縮放.
- setLoadWithOverviewMode(true):自適應螢幕,配合setUseWideViewPort一起使用.
- setLoadsImagesAutomatically(true):是否下載圖片資源,預設true.
- setCacheMode:設定瀏覽器快取模式:
- LOAD_DEFAULT:預設值。當存在快取且未過期時載入快取資料,否則通過網路載入資料。
- LOAD_NO_CACHE:不使用快取。僅通過網路載入資料。
- LOAD_CACHE_ONLY:只使用快取。僅載入快取資料,不通過網路載入資料。
- LOAD_CACHE_ELSE_NETWORK:只要有快取,是否過期都載入快取,否則載入網路資料。
WebView常用API呼叫:
- getOriginalUrl:獲取當前網頁的原始url,針對某些執行重定向操作的網頁。
- loadData:以字串形式載入html片段(若包含中文,會亂碼,須設定mimeType型別)。
- postUrl:以post請求載入url(post攜帶的資料須是application/x-www-form-urlencoded編碼)。
- reload:重新整理頁面,過載資源。
- stopLoading:停止載入。
- pageUp:向上翻頁至頁面頂部。
- clearCache(includeDiskFiles):清除快取,傳參false僅清除記憶體快取,傳參true同時清除記憶體和磁碟快取
- clearFormData:清除表單自動填充資料(不含已儲存本地的表單資料)。
- onPause:儘可能暫停webview行為,如動畫、定位,不暫停js指令碼執行。onResume恢復被暫停的行為。
- pauseTimers:暫停所有webview全部行為,resumeTimers恢復被pauseTimers暫停的行為。
- destroy:銷燬webview,須先從viewGroup中移除webview才有效。
- getContentHight:獲取網頁內容高度。
- loadUrl:載入遠端或本地的url。
- assets資源:file:///android_asset/
- res資源:file:///android_res/
- raw資源:file:///android_res/raw/
- sdcard資源:file:///sdcard/
Android呼叫H5
WebView需要設定setJavaScriptEnabled(true);使WebView支援執行JavaScript指令碼。
//支援Android 4.4以前的版本,支援頁面重新整理,但無返回值。 webview.post(new Runnable()){ @Override public void run(){ webview.loadUrl("javaScript:callJs('params')"); } } //支援Android 4.4+版本,能接收返回值,不支援頁面重新整理,呼叫及回撥均在UI執行緒中。 webview.evaluateJavascipt("javascript:callJS('params')", new ValueCallback<String>(){ @Override public void onReceiveValue(String value){ //處理從js返回的值value } });
以上兩種Android呼叫H5方法,均在onPageFinished()方法回撥後執行,即頁面載入完畢後。
H5呼叫Android
//為js新增Java對映物件。 webview.addJavascriptInterface(new JsCallAndroid(),"jscallandroid"); public class JsCallAndroid { //需新增@JavascriptInterface註解,避免webview漏洞。 @JavascriptInterface public void hello(String msg) { System.out.println("JS呼叫了Android的hello方法"); } }
js通過指令碼對映addJavascriptInterface()中的Object物件,實現H5呼叫Android方法。前提須設定settings.setJavaScriptEnabled(true)。
Scheme協議
Android中的Scheme是一種頁面跳轉協議,常處理以下場景互動:
- App端不同頁面跳轉;
- 通過shouldOverrideUrlLoading()回撥攔截URL,實現H5頁面跳轉App頁面;
- 根據URI跳轉到另一個App指定頁面;
- 接收服務端下發URI跳轉App對應頁面;
在使用Scheme前,須在AndroidManifest.xml中對Activity新增< intent-filter/>過濾器註冊。
<activity android:name=".ui.activity.TestActvity" android:screenOrientation="portrait" android:windowSoftInputMode="stateAlwaysHidden|adjustPan"> <!--Scheme協議配置,intent-filter過濾器支援頁面跳轉--> <intent-filter> <!--協議部分,path前須加/,除了sheme外其他選填--> <data android:host="book" android:path="/bookDetail" android:port="8888" android:scheme="app" /> <!--下面也要設定--> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <action android:name="android.intent.action.VIEW" /> </intent-filter> </activity>
常規的URL Scheme格式為:[scheme]://[host]/[path]?[query],如 app://book:8888/bookDetail?bookId=10011002
app代表Scheme協議名稱,book代表作用域,8888代表埠號(如Http協議的預設埠號為80),bookDetail代表頁面路徑,bookId代表拼接引數。
App內頁面跳轉,示例1:
- 頁面A跳轉及結果回撥;
String uri = "app://book:8888/bookDetail?bookId=10011002"; schemeToActivity(uri, 100); /** * 採用Scheme協議頁面跳轉,校驗scheme協議是否有效 * @param uri * @param requestCode */ private void schemeToActivity(String uri, int requestCode) { if (TextUtils.isEmpty(uri)) return; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri)); List<ResolveInfo> activities = getPackageManager().queryIntentActivities(intent, 0); if (activities != null && !activities.isEmpty()) { if (requestCode > 0) { startActivityForResult(intent, requestCode); } else { startActivity(intent); } } else { throw new RuntimeException("can not find the activity's URL Scheme"); } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); String string = data.getStringExtra("result"); if (requestCode == 100) { Log.d("test", string); } }
- 頁面B解析Scheme及setResult;
/** * 解析Scheme協議的uri資料 */ private void getSchemeIntent() { Intent intent = getIntent(); Uri uri = intent.getData(); if (uri == null)return; String scheme = uri.getScheme(); String host = uri.getHost(); int port = uri.getPort(); String path = uri.getPath(); List<String> pathSegments = uri.getPathSegments(); String query = uri.getQuery(); String bookId = uri.getQueryParameter("bookId"); Log.d("test",bookId); } //退回頁面A回傳值 Intent intent = new Intent(); intent.putExtra("result","hello world!"); setResult(RESULT_OK,intent); finish();
通過debug模式,可獲得從Scheme中解析出的引數值,如下:

H5頁面跳轉App頁面,示例2:
若要跳轉AndroidManifest.xml中定義Scheme的Activity,H5端配置如下:
<a href="app://book:8888/bookDetail?bookId=10011002">圖書詳情頁</a>
在App內點選該WebView頁面連結會觸發WebViewClient#shouldOverrideUrlLoading()方法,解析如下:
@Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { //判定重定向 WebView.HitTestResult testResult = view.getHitTestResult(); if (testResult == null || testResult.getType() == WebView.HitTestResult.UNKNOWN_TYPE) { return false; } //解析URL Scheme Uri uri = request.getUrl(); //根據schemeToActivity(uri.toString(),0);方案處理跳轉 return true; } return false; }
如上所示,和常規的URL Scheme解析同理,注意shouldOverrideUrlLoading()的版本相容。
重定向問題的處理方案:WebView的getHitTestResult()的函式可以獲取點選頁面元素的型別,若獲取的HitTestResult為null或UNKNOWN_TYPE,則認定為重定向URL,對此情況直接return false。
shouldOverrideUrlLoading()處理H5與Android互動的存在的問題:
- 重定向在低版本上返回問題,成熟方案不多;
- 攔截URI處理頁面跳轉或點選事件,硬編碼導致的臃腫問題;
- shouldOverrideUrlLoading()本身呼叫時機的侷限性;
硬編碼問題雖然借鑑了ARouter路由跳轉思想能緩解頁面跳轉,但是在互動方面addJavascriptInterface對映物件的方案更優。
URL Scheme更適合處理App頁面之間的跳轉。