Android 集成Chrome 浏览器内核 Crosswalk
Crosswalk 內核的興起與消亡
Android 4.4 版本之前,使用的是基于 androidWebKit 的 WebView
但實際上,由于 Android 的碎片化問題(大量存在不同的 Android 系統版本,并且各個廠商對內置應用進行定制化,有可能用的不是最新的瀏覽器內核)。這就導致 WebView 在真實環境中對 API 的支持根本無跡可尋,越發混亂。
Android 碎片化問題集中表現在下面幾個方面:
- 設備繁多,硬件配置參差不棄,設備性能各異,差距很大
- 品牌眾多,廠商標準不一致,定制化系統體驗不同
- 版本各異,國內外系統環境差異巨大
- 分辨率不統一,各種類型尺寸眾多
隨著混合開發的興起,前端對 API 的支持程度和網頁的表現效果都有了更嚴格的要求,原生WebView 由于碎片化嚴重,API支持程度未知,容易引發很多意料之外的BUG。
這時候,就誕生了一些第三方瀏覽器內核
-
Intel 開源的基于 Chrome 的 Crosswalk 內核 和 XWalkView 瀏覽器(2017年5月停止維護)
https://github.com/crosswalk-project/crosswalk
https://github.com/tenta-browser/crosswalk -
騰訊出品的 X5 瀏覽器內核
https://x5.tencent.com/tbs/sdk.html
從 Android 5.0 開始,Google 把 Chromium blink內核 webview 作為 apk 單獨從系統抽離出去,可以在應用市場(Google Play)上面接收安裝更新。應用可以直接使用該webview內核,Google也可以及時發布更新,不用再通過更新系統才能更新瀏覽器內核,也避免部分了 Android 系統碎片化問題。
因此 Intel 的 Crosswalk 就停止維護了。然而由于國內被墻,并沒有接入谷歌服務,因此 騰訊X5 內核 還流傳至今,并且被廣泛的應用
集成原因
現在代的手機上,原生的 webkit 內嵌的谷歌內核版本并不是很統一,這就導致了有些手機支持的API到另一個手機,又不支持了。為了達到體驗一致,也方便測試,我建議在國內,盡量使用騰訊X5進行替換,X5的API和原生的基本一致,僅需要改動較小的部分。
那么corsswalk,一個包40M,是不是就毫無用處了呢?答案是否定的,crosswalk現在多用于集成到
智能設備中。智能設備的網絡不一定好用,更別說安裝QQ微信了,而且即使安裝了,也不一定支持騰訊X5,因為現在還是有部分手機無法兼容X5轉而降級為原生瀏覽器內核的。
集成方式
添加依賴
可以在項目根路徑下的 build.gradle 中添加,針對所有module
buildscript {repositorities {……} } allprojects {repositories {……maven { url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'}} }兩個位置的 repositories 的區別
也可以僅在對應 module 的 build.gradle 中添加 respositories,然后再添加對應依賴
android { } repositories {maven { url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'} } dependencies {implementation 'org.xwalk:xwalk_core_library:23.53.589.4' }注意:添加依賴后不可能一次就同步成功,需要多同步好幾次
申請權限
在 AndroidManifest.xml 中添加如下權限聲明
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />開啟硬件加速
從Android3.0(API Level 11)開始,Android 2D渲染管道k開始支持硬件加速(默認是關閉的)。
可以在 AndroidManifest.xml 中,為 Application 添加屬性,開啟全局硬件加速
<Application……android:hardwareAccelerated="true" >…… </Application>硬件加速執行的所有的繪圖操作,都是使用GPU在 View 對象的畫布上來進行的。因為啟用硬件加速會增加資源的需求,因此這樣的應用會占用更多的內存。
為了讓應用能申請使用更多的內存,還需要添加一個 largeHeap 屬性。機器的內存限制,在/system/build.prop文件中配置的,例如
dalvik.vm.heapsize=128m dalvik.vm.heapgrowthlimit=64mheapgrowthlimit 是一個普通應用的內存限制,用ActivityManager.getLargeMemoryClass() 獲得的值就是這個。而 heapsize 是在 manifest 中設置了 largeHeap=true 之后,可以使用最大內存值。
習慣性的為應用多申請一點內存,可以使用如下代碼
<Application……android:hardwareAccelerated="true"android:largeHeap="true" >…… </Application>布局文件
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><org.xwalk.core.XWalkViewandroid:id="@+id/webview"android:layout_width="match_parent"android:layout_height="match_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>測試代碼
XWResourceClient.java
public class XWResourceClient extends XWalkResourceClient {private static final String TAG = "XWalkResourceClient";public XWResourceClient(XWalkView view) {super(view);}@Overridepublic void onLoadStarted(XWalkView view, String url) {Log.i(TAG, "onLoadStarted " + url);super.onLoadStarted(view, url);}@Overridepublic void onLoadFinished(XWalkView view, String url) {Log.i(TAG, "onLoadFinished " + url);super.onLoadFinished(view, url);}@Overridepublic void onProgressChanged(XWalkView view, int progressInPercent) {Log.i(TAG, "onProgressChanged " + progressInPercent);super.onProgressChanged(view, progressInPercent);}@Overridepublic boolean shouldOverrideUrlLoading(XWalkView view, String url) {Log.i(TAG, "shouldOverrideUrlLoading " + url);return super.shouldOverrideUrlLoading(view, url);}@Overridepublic WebResourceResponse shouldInterceptLoadRequest(XWalkView view, String url) {Log.i(TAG, "shouldInterceptLoadRequest " + url);return super.shouldInterceptLoadRequest(view, url);}@Overridepublic XWalkWebResourceResponse shouldInterceptLoadRequest(XWalkView view, XWalkWebResourceRequest request) {Log.i(TAG, "shouldInterceptLoadRequest " + request.isForMainFrame() + ", " + request.getUrl() + ", " + new JSONObject(request.getRequestHeaders()).toString());return super.shouldInterceptLoadRequest(view, request);}@Overridepublic void onReceivedSslError(XWalkView view, ValueCallback<Boolean> callback, SslError error) {Log.i(TAG, "onReceivedSslError " + error.toString());super.onReceivedSslError(view, callback, error);}@Overridepublic void onReceivedLoadError(XWalkView view, int errorCode, String description, String failingUrl) {Log.i(TAG, "onReceivedLoadError " + errorCode + ", " + description + ", " + failingUrl);super.onReceivedLoadError(view, errorCode, description, failingUrl);}@Overridepublic void onDocumentLoadedInFrame(XWalkView view, long frameId) {Log.i(TAG, "onDocumentLoadedInFrame " + frameId);super.onDocumentLoadedInFrame(view, frameId);}@Overridepublic void onReceivedClientCertRequest(XWalkView view, ClientCertRequest handler) {Log.i(TAG, "onReceivedClientCertRequest " + handler.getHost() + ", " + handler.getPort() + ", " + Arrays.toString(handler.getKeyTypes()));super.onReceivedClientCertRequest(view, handler);}@Overridepublic void onReceivedHttpAuthRequest(XWalkView view, XWalkHttpAuthHandler handler, String host, String realm) {Log.i(TAG, "onReceivedHttpAuthRequest " + host + ", " + realm);super.onReceivedHttpAuthRequest(view, handler, host, realm);}@Overridepublic void onReceivedResponseHeaders(XWalkView view, XWalkWebResourceRequest request, XWalkWebResourceResponse response) {Log.i(TAG, "onReceivedResponseHeaders " + request.isForMainFrame() + ", " + request.getUrl() + ", " + new JSONObject(request.getRequestHeaders()).toString());super.onReceivedResponseHeaders(view, request, response);}@Overridepublic XWalkWebResourceResponse createXWalkWebResourceResponse(String mimeType, String encoding, InputStream data) {Log.i(TAG, "createXWalkWebResourceResponse " + mimeType + ", " + encoding);return super.createXWalkWebResourceResponse(mimeType, encoding, data);}@Overridepublic XWalkWebResourceResponse createXWalkWebResourceResponse(String mimeType, String encoding, InputStream data, int statusCode, String reasonPhrase, Map<String, String> responseHeaders) {Log.i(TAG, "createXWalkWebResourceResponse " + mimeType + ", " + encoding + ", " + statusCode + ", " + reasonPhrase + ", " + new JSONObject(responseHeaders));return super.createXWalkWebResourceResponse(mimeType, encoding, data, statusCode, reasonPhrase, responseHeaders);}@Overridepublic void doUpdateVisitedHistory(XWalkView view, String url, boolean isReload) {Log.i(TAG, "doUpdateVisitedHistory " + url + ", " + isReload);super.doUpdateVisitedHistory(view, url, isReload);}@Overrideprotected Object getBridge() {Object obj = super.getBridge();if(obj != null) {Log.i(TAG, "getBridge " + obj.getClass().getSimpleName());} else {Log.i(TAG, "getBridge()");}return obj;} }XWUIClient.java
public class XWUIClient extends XWalkUIClient {private static final String TAG = "XWalkUIClient";public XWUIClient(XWalkView view) {super(view);}@Overridepublic void onPageLoadStarted(XWalkView view, String url) {Log.i(TAG, "onPageLoadStarted " + url);super.onPageLoadStarted(view, url);}@Overridepublic void onPageLoadStopped(XWalkView view, String url, LoadStatus status) {Log.i(TAG, "onPageLoadStopped " + url + ", " + status.toString());super.onPageLoadStopped(view, url, status);}@Overridepublic boolean onJsAlert(XWalkView view, String url, String message, XWalkJavascriptResult result) {Log.i(TAG, "onJsAlert " + url + ", " + message);return super.onJsAlert(view, url, message, result);}@Overridepublic boolean onJsConfirm(XWalkView view, String url, String message, XWalkJavascriptResult result) {Log.i(TAG, "onJsConfirm " + url + ", " + message);return super.onJsConfirm(view, url, message, result);}@Overridepublic boolean onJsPrompt(XWalkView view, String url, String message, String defaultValue, XWalkJavascriptResult result) {Log.i(TAG, "onJsPrompt " + url + ", " + message);return super.onJsPrompt(view, url, message, defaultValue, result);}@Overridepublic boolean onConsoleMessage(XWalkView view, String message, int lineNumber, String sourceId, ConsoleMessageType messageType) {Log.i(TAG, "onConsoleMessage " + message + ", " + lineNumber + ", " + sourceId + ", " + messageType.toString());return super.onConsoleMessage(view, message, lineNumber, sourceId, messageType);}@Overridepublic void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) {Log.i(TAG, "onShowCustomView " + requestedOrientation);super.onShowCustomView(view, requestedOrientation, callback);}@Overridepublic void onShowCustomView(View view, CustomViewCallback callback) {Log.i(TAG, "onShowCustomView");super.onShowCustomView(view, callback);}@Overridepublic boolean onCreateWindowRequested(XWalkView view, InitiateBy initiator, ValueCallback<XWalkView> callback) {Log.i(TAG, "onCreateWindowRequested");return super.onCreateWindowRequested(view, initiator, callback);}@Overridepublic boolean onJavascriptModalDialog(XWalkView view, JavascriptMessageType type, String url, String message, String defaultValue, XWalkJavascriptResult result) {Log.i(TAG, "onJavascriptModalDialog " + type.toString() + ", " + url + ", " + message + ", " + defaultValue);return super.onJavascriptModalDialog(view, type, url, message, defaultValue, result);}@Overridepublic void onFullscreenToggled(XWalkView view, boolean enterFullscreen) {Log.i(TAG, "onFullscreenToggled " + enterFullscreen);super.onFullscreenToggled(view, enterFullscreen);}@Overridepublic void onHideCustomView() {Log.i(TAG, "onHideCustomView");super.onHideCustomView();}@Overridepublic void onIconAvailable(XWalkView view, String url, Message startDownload) {Log.i(TAG, "onIconAvailable " + url + ", " + startDownload.toString());super.onIconAvailable(view, url, startDownload);}@Overridepublic void onJavascriptCloseWindow(XWalkView view) {Log.i(TAG, "onJavascriptCloseWindow");super.onJavascriptCloseWindow(view);}@Overridepublic void onReceivedIcon(XWalkView view, String url, Bitmap icon) {Log.i(TAG, "onReceivedIcon " + url);super.onReceivedIcon(view, url, icon);}@Overridepublic void onReceivedTitle(XWalkView view, String title) {Log.i(TAG, "onReceivedTitle " + title);super.onReceivedTitle(view, title);}@Overridepublic void onRequestFocus(XWalkView view) {Log.i(TAG, "onRequestFocus");super.onRequestFocus(view);}@Overridepublic void onScaleChanged(XWalkView view, float oldScale, float newScale) {Log.i(TAG, "onScaleChanged " + oldScale + ", " + newScale);super.onScaleChanged(view, oldScale, newScale);}@Overridepublic void onUnhandledKeyEvent(XWalkView view, KeyEvent event) {Log.i(TAG, "onUnhandledKeyEvent " + event.getAction() + ", " + event.getKeyCode());super.onUnhandledKeyEvent(view, event);}@Overridepublic void openFileChooser(XWalkView view, ValueCallback<Uri> uploadFile, String acceptType, String capture) {Log.i(TAG, "openFileChooser " + acceptType + ", " + capture);super.openFileChooser(view, uploadFile, acceptType, capture);}@Overridepublic boolean shouldOverrideKeyEvent(XWalkView view, KeyEvent event) {Log.i(TAG, "shouldOverrideKeyEvent " + event.getAction() + ", " + event.getKeyCode());return super.shouldOverrideKeyEvent(view, event);}@Overrideprotected Object getBridge() {Object obj = super.getBridge();if(obj != null) {Log.i(TAG, "getBridge " + obj.getClass().getSimpleName());} else {Log.i(TAG, "getBridge()");}return obj;} }MainActivity.java
public class MainActivity extends XWalkActivity {private XWalkView webView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);webView = findViewById(R.id.webview);// Crosswalk's APIs are not ready yet}@Overrideprotected void onXWalkReady() {initSettings();webView.setUIClient(new XWUIClient(webView));webView.setResourceClient(new XWResourceClient(webView));webView.addJavascriptInterface(new AppShell(this), AppShell.TAG);webView.loadUrl("http://www.baidu.com");}@Overridepublic void onPointerCaptureChanged(boolean hasCapture) {}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);if(isXWalkReady()) webView.onNewIntent(intent);}@Overrideprotected void onPause() {super.onPause();if(isXWalkReady()) {webView.pauseTimers();webView.onHide();}}@Overrideprotected void onResume() {super.onResume();if(isXWalkReady()) {webView.resumeTimers();webView.onShow();}}@Overrideprotected void onDestroy() {super.onDestroy();if(isXWalkReady()) {webView.onDestroy();}}@Overridepublic void onBackPressed() {if(isXWalkReady()) {XWalkNavigationHistory history = webView.getNavigationHistory();if (history.canGoBack()) {history.navigate(XWalkNavigationHistory.Direction.BACKWARD, 1);} else {super.onBackPressed();}} else {super.onBackPressed();}}/*** 沒有允許定位的設置*/public void initSettings() {XWalkSettings webSettings = webView.getSettings();//啟用JavaScriptwebSettings.setJavaScriptEnabled(true);//允許js彈窗alert等,window.open方法打開新的網頁,默認不允許webSettings.setJavaScriptCanOpenWindowsAutomatically(true);//localStorage和sessionStoragewebSettings.setDomStorageEnabled(true);//Web SQL DatabaseswebSettings.setDatabaseEnabled(true);//是否可訪問Content Provider的資源,默認值 truewebSettings.setAllowContentAccess(true);/*是否允許訪問文件系統,默認值 truefile:///androMSG_asset和file:///androMSG_res始終可以訪問,不受其影響*/webSettings.setAllowFileAccess(true);//是否允許通過file url加載的Javascript讀取本地文件,默認值 falsewebSettings.setAllowFileAccessFromFileURLs(true);//是否允許通過file url加載的Javascript讀取全部資源(包括文件,http,https),默認值 falsewebSettings.setAllowUniversalAccessFromFileURLs(true);//設置是否支持縮放webSettings.setSupportZoom(false);//設置內置的縮放控件webSettings.setBuiltInZoomControls(false);/*當該屬性被設置為false時,加載頁面的寬度總是適應WebView控件寬度;當被設置為true,當前頁面包含viewport屬性標簽,在標簽中指定寬度值生效,如果頁面不包含viewport標簽,無法提供一個寬度值,這個時候該方法將被使用。*/webSettings.setUseWideViewPort(false);//縮放至屏幕大小webSettings.setLoadWithOverviewMode(true);//支持多窗口webSettings.setSupportMultipleWindows(true);/*緩存模式LOAD_CACHE_ONLY 不使用網絡,只讀取本地緩存LOAD_DEFAULT 根據cache-control決定是否從網絡上獲取數據LOAD_NO_CACHE 不使用緩存,只從網絡獲取數據LOAD_CACHE_ELSE_NETWORK 只要本地有,無論是否過期,或者no-cache,都使用緩存中的數據*/webSettings.setCacheMode(XWalkSettings.LOAD_DEFAULT);//設置是否加載圖片webSettings.setLoadsImagesAutomatically(true);//允許遠程調試XWalkPreferences.setValue("enable-javascript", true);XWalkPreferences.setValue(XWalkPreferences.REMOTE_DEBUGGING, true);} }生命周期
XWalk 內核加載的生命周期
在Activity中初始化XWalk內核
解壓、激活、將內核附著在Activity上
下載模式禁用
下載模式和共享模式差不多,只是共享模式是把APK下載下來當成一個應用安裝到手機上,而下載模式干脆把APK下載到自己的私有目錄下,把所有的so文件、資源解壓出來保存到自己的內部私有目錄下只供自己使用。
通過反射,api版本是否等于lib版本(mApiVersion == minLibVersion)
| getBridge | XWalkUIClient 和 XWalkResourceClient 獲取反射的對象 |
| shouldInterceptLoadRequest | 攔截請求,在這里可以使用緩存 |
| onPageLoadStarted、onPageLoadStopped | 頁面加載開始、結束,可以用來做自定義定時器 |
| onLoadStarted、onLoadStopped | 頁面中的元素加載開始和結束 |
| onReceivedResponseHeaders | 處理接收的頭部 |
| shouldOverrideUrlLoading | 用來處理意圖 |
| doUpdateVisitedHistory | 更新訪問歷史記錄 |
| onReceivedTitle | 收到標題 |
遇到的問題
1. Crosswalk‘s APIs are not ready yet
首先, org.xwalk.core.XWalkView 控件,只能在 XWalkActivity 里,也就是當前 Activity 必須繼承自 XWalkActivity。
對 XWalkView 的設置,只能 onXWalkReady 里。
在 Activity 的生命周期函數中調用 XWalkView 的方法需要先判斷是否初始化完畢。
2. Mismatch of CPU Architecture
so分32位和64位!
理論上官方應該提供2種包含不同so的aar包。一種是32位的另一種是64位,32位的aar中只包含x86和armeabi-v7a兩種so文件,同理64位的包中只包含x86_64和arm64-v8a兩種so文件。
由于arm64-v8a平臺能兼容32位的so文件、x86_64也能兼容32位的x86 so文件,在不考慮性能(暫時未知性能問題)的情況下就可以直接集成32位的aar包,可以大大減少安裝包的大小。
騰訊X5的話,是僅支持32位的。我猜測 crosswalk 應該也是這個問題,因此僅集成32位即可。
因此可以在 module 的 build.gradle 中添加如下內容
android {defaultConfig {ndk {abiFilters 'armeabi', 'armeabi-v7a'}} }然后,再跟騰訊 X5 的做法一樣,在 src/mian/jniLibs/armeabi-v7a 目錄下,創建一個空的 so
3. 文件選擇的問題
在 HTML5 中共有 4 種選擇文件的方式,代碼如下
<label>下面是選擇文件</label> <input type="file" name="filename" /><label>下面是通過攝像頭獲取文件</label> <input capture="camera" id="cameraFile" name="imgFile" type="file"><label>下面是打開攝像頭拍照</label> <input accept="image/*" capture="camera" id="imgFile" name="imgFile" type="file"><label>下面是打開攝像頭錄像</label> <input accept="video/*" capture="camera" id="videoFile" name="imgFile" type="file">在 CrossWalk 中對應的函數是 XWalkUIClient.java 中的 openFileChooser。如果主動不觸發回調函數,即為未選擇,那么內部會一直處于等待的過程中,即使下一次點擊選擇文件,也不會有響應。
public void openFileChooser(XWalkView view, ValueCallback<Uri> uploadFile, String acceptType, String capture) {…… }獲取文件(文件、拍照、相冊、錄像)
| 1 | false | 獲取文件 | |
| 2 | true | 打開攝像頭獲取文件 | |
| 3 | image/* | true | 打開攝像頭拍照 |
| 4 | video/* | true | 打開攝像頭錄像 |
實現大致邏輯(再細的代碼就不貼了,講個思路)
@Override public void openFileChooser(XWalkView view, ValueCallback<Uri> uploadFile, String acceptType, String capture) {Log.i(TAG, "openFileChooser " + acceptType + ", " + capture);super.openFileChooser(view, uploadFile, acceptType, capture);if ("true".equals(capture)) {//判斷是拍視頻,還是拍照boolean isVideo = "video/*".equals(acceptType);String path;DataCallback1<Uri> callback = uri -> {LogUtil.i(TAG, "uri: " + (uri == null ? null : uri.toString()));uploadFile.onReceiveValue(uri);};if (isVideo) {//拍視頻path = genMoviesPath(activity);LogUtil.i(TAG, path);if(path == null) return;FileUtil.mkParentDirs(path);LogUtil.d(TAG, "videoCapture: " + path);IntentUtil.videoCapture(activity, path, callback);} else {//拍照片path = genPicturePath(activity);LogUtil.i(TAG, path);if(path == null) return;FileUtil.mkParentDirs(path);LogUtil.d(TAG, "videoCapture: " + path);IntentUtil.imageCaptureToUri(activity, path, callback);}} else {if(TextUtils.isEmpty(acceptType)) acceptType = "*/*";IntentUtil.selectFile(activity, acceptType, (uri, path, mimeType) -> uploadFile.onReceiveValue(uri));} }4. 注解 @JavascriptInterface
注意一個細節,原生 webkit 乃至騰訊X5,用的都是 android.webkit.JavascriptInterface ,然而 crosswalk 不同,它用的是 org.xwalk.core.JavascriptInterface。如果注解用錯了,抱歉 Uncaught TypeError: <JavaScriptInterfaceName.Method> is not a function
5. 截圖
普通的 View 控件,直接用下面函數即可截圖。但 Crosswalk 用下面方法,只能得到一張白紙
public static Bitmap getView(View view) {view.setDrawingCacheEnabled(true);view.buildDrawingCache();Bitmap cache = view.getDrawingCache();Bitmap bitmap = BitmapUtil.clone(cache);view.setDrawingCacheEnabled(false);view.destroyDrawingCache();return bitmap; }網上有人說是 “硬件加速” 的問題,如下兩種方法關閉硬件加速
- 在 AndroidManifest.xml 的 application 或 activity 里添加 android:hardwareAccelerated="false"
- 在代碼中設置 webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
經過測試發現行不通,最后發現 XWalkView 自帶異步的截圖方法,源碼如下
public void captureBitmapAsync(XWalkGetBitmapCallback callback) {try {this.captureBitmapAsyncXWalkGetBitmapCallbackInternalMethod.invoke(new Object[]{callback.getBridge()});} catch (UnsupportedOperationException var3) {if (this.coreWrapper == null) {throw new RuntimeException("Crosswalk's APIs are not ready yet");}XWalkCoreWrapper.handleRuntimeError(var3);} }6. 攔截請求
public class XWResourceClient {@Overridepublic XWalkWebResourceResponse shouldInterceptLoadRequest(XWalkView view, XWalkWebResourceRequest request) {String url = request.getUrl().toString(); if(!TextUtils.isEmpty(url)&&url.endsWith(".png")) {Log.i(TAG, String.format("攔截網址:%s", url));return createXWalkWebResourceResponse("text/html","UTF-8",null);//不向過濾的地址發出請求}return super.shouldInterceptLoadRequest(view, request);} }7. CrossWalk 支持 websocket
注意,if (typeof WebSocket != 'undefined') 并不能判斷 websocket 是否被支持。因為 Android 中,即使瀏覽器不支持 WebSocket ,但是它還是存在這個屬性。
正確的驗證方法如下
function isSupportWebsocket() {if (!!window.WebSocket && window.WebSocket.prototype.send) {return true;} else {return false;} }部分方法不支持
總結
以上是生活随笔為你收集整理的Android 集成Chrome 浏览器内核 Crosswalk的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓图片三级缓存策略与实现
- 下一篇: python最佳编程语言_前十大编程语言