Android 集成Chrome 浏览器内核 Crosswalk

article/2025/9/15 16:19:41

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 的区别

  1. buildscript 里是 gradle 脚本执行所需依赖,分别是对应的 maven 库和插件
  2. allprojects 里是项目本身需要的依赖

也可以仅在对应 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=64m  

heapgrowthlimit 是一个普通应用的内存限制,用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 内核加载的生命周期

  1. 预加载 xwalk 内核
  2. 将共享的XWalk内核与当前Activity绑定
public static void reserveReflectObject(Object object) {String tag = (String)sReservedActivities.getLast();Log.d("XWalkLib", "Reserve object " + object.getClass() + " to " + tag);((LinkedList)sReservedActions.get(tag)).add(new XWalkCoreWrapper.ReservedAction(object));
}
  1. 在Activity中初始化XWalk内核

  2. 解压、激活、将内核附着在Activity上

  3. 下载模式禁用
    下载模式和共享模式差不多,只是共享模式是把APK下载下来当成一个应用安装到手机上,而下载模式干脆把APK下载到自己的私有目录下,把所有的so文件、资源解压出来保存到自己的内部私有目录下只供自己使用。

  4. 通过反射,api版本是否等于lib版本(mApiVersion == minLibVersion)

private boolean checkCoreVersion() {Log.d("XWalkLib", "[Environment] SDK:" + VERSION.SDK_INT);Log.d("XWalkLib", "[App Version] build:23.53.589.4, api:" + this.mApiVersion + ", min_api:" + this.mMinApiVersion);try {Class<?> clazz = this.getBridgeClass("XWalkCoreVersion");String buildVersion = "";try {buildVersion = (String)(new ReflectField(clazz, "XWALK_BUILD_VERSION")).get();} catch (RuntimeException var5) {}int libVersion = (Integer)(new ReflectField(clazz, "API_VERSION")).get();int minLibVersion = (Integer)(new ReflectField(clazz, "MIN_API_VERSION")).get();Log.d("XWalkLib", "[Lib Version] build:" + buildVersion + ", api:" + libVersion + ", min_api:" + minLibVersion);if (XWalkEnvironment.isDownloadMode() && XWalkEnvironment.isDownloadModeUpdate() && !buildVersion.isEmpty() && !buildVersion.equals("23.53.589.4")) {this.mCoreStatus = 8;return false;}if (this.mMinApiVersion > libVersion) {this.mCoreStatus = 3;return false;}if (this.mApiVersion < minLibVersion) {this.mCoreStatus = 4;return false;}} catch (RuntimeException var6) {Log.d("XWalkLib", "XWalk core not found");this.mCoreStatus = 2;return false;}Log.d("XWalkLib", "XWalk core version matched");return true;}
  1. 打印出,当前库是基于ARM架构编译的。当前设备是arm64-v8a,使用嵌入模式运行
  2. 搭建环境、初始化内核、初始化视图,激活任务执行完毕,开始在 SurfaceView 上进行绘制。
    在这里插入图片描述
函数用途
getBridgeXWalkUIClient 和 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) {……
}

获取文件(文件、拍照、相册、录像)

序号acceptTypecapture含义
1false获取文件
2true打开摄像头获取文件
3image/*true打开摄像头拍照
4video/*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;}
}

部分方法不支持

  1. Object #<HTMLElement> has no method 'remove'

http://chatgpt.dhexx.cn/article/6DdnxtL7.shtml

相关文章

Android集成三方浏览器之Crosswalk

上一篇讲解了腾讯 X5 内核的集成&#xff0c;这一篇是讲解 Crosswalk 的集成 Crosswalk 也是采用了Chromenium 内核&#xff0c;是一款开源的 web 引擎&#xff0c;开发者可以直接把 Crosswalk 嵌入到应用之中&#xff0c;当然也支持共享模式&#xff08;系统中没有对应的 Cros…

CROSSWALK入门

Crosswalk是一款开源的web引擎。目前Crosswalk正式支持的移动操作系统包括Android和Tizen&#xff0c;在Android 4.0及以上的系统中使用Crosswalk的Web应用程序在HTML5方面可以有一致的体验&#xff0c;同时和系统的整合交互方面&#xff08;比如启动画面、权限管理、应用切换、…

Java和大数据的关系如何?

非常多的人&#xff0c;尤其是没有研究过&#xff0c;没有关注过这块知识的人都容易误解Java 和大数据的概念&#xff0c;甚至有些人认为大数据就是用java写的。 一&#xff1a;两者关系 java是计算机的一门编程语言&#xff1b;可以用来做很多工作&#xff0c;大数据开发属于…

java,大数据,

辅导&#xff0c;环境安装

Java?还是大数据?

IT 行业的人可能听过“ Java 大数据开发”这个名词&#xff0c;其实这并不是一个新岗位&#xff0c;而是因为大数据开发的编程语言以 Java 为主&#xff0c;掌握 Java 是进行大数据开发的基础&#xff0c;这才形成了“ Java 大数据开发”的说法。 随着 Java 开发岗人才供应趋于…

大数据开发-Java入门1

目录 1、计算机语言发展史1.1 计算机语言&#xff1a;1.2 常用的DOS命令&#xff1a; 2、java介绍与安装3、Java程序初体验3.1 Java开发三步走3.2 常见错误3.3 代码解释3.4 源文件名和类型 1、计算机语言发展史 1.1 计算机语言&#xff1a; 第一代&#xff1a;机器语言第二代…

JAVA 大数据方向 - 知识体系概况

JAVA 大数据方向 - 知识体系概况 想学后端&#xff0c;面临选方向&#xff0c;补充一下

学习Java大数据需要掌握哪些Java技能?

学习Java大数据需要掌握哪些Java技能&#xff1f;现在大数据发展很速度很多小伙伴想要学习Java大数据技术开发&#xff0c;但是学习大数据为什么需要掌握Java技能呢&#xff1f; 一、学大数据为什么要掌握Java&#xff1f; 首先&#xff0c;我们学习大数据&#xff0c;为什么要…

Java——处理大数据 BigInteger

整型 int型表示范围是&#xff1a;-(2的31次方) ~ (2的31次方) - 1 long型表示范围是&#xff1a;-(2的64次方) ~(2的64次方) - 1 浮点型 float和double是表示浮点型的数据类型&#xff0c;他们之间的区别在于他们的精确度不同float 3.402823e38 ~ 1.401298e-45&#xff08;e…

大数据必学Java基础(一百一十七):三个案例开发

文章目录 三个案例开发 一、记录请求日志 二、统计实时在线人数 三、项目重启免登录

大数据开发 - Java入门2

目录 Java基础知识注释关键字常量标识符测试题回顾 Java基础知识 注释 对程序的解释说明 分类&#xff1a; 单行注释&#xff1a;// 对本行后面的内容进行注释多行注释&#xff1a;/*解释内容 */文档注释 &#xff1a;/** 注释内容*/ --用于产生帮助文档&#xff0c;也有多行…

Java和大数据有什么联系?

提到Java&#xff0c;我们经常将它和大数据联系在一起。Java是一门面向对象编程语言&#xff0c;大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是一个概念性的东西。那么&#xff0c;Java和大数据有什么关系呢? 简单的说Java语言是…

java到大数据学习路线

计算机网络 操作系统 数据结构 计算机组成原理 可重点学习如下知识点 计算机网络&#xff08;重点看 OSI七层模型 或 TCP/IP五层模型 理解每层含义&#xff09;数据结构&#xff08;重点看 数组、栈、队列、链表、树&#xff09;算法&#xff08;重点看 各种 排序算法、查…

Java大数据文件处理方法

前言 Java大数据文件处理是一种使用Java编写的大型数据处理技术&#xff0c;特别适用于处理大型数据文件。在这篇文章中&#xff0c;我们将了解什么是Java大数据文件处理&#xff0c;为什么它很重要以及Java大数据文件处理的方法和技术。 一、什么是Java大数据文件处理&#…

大数据必学Java基础(一):Java体系结构、特性和优势

文章目录 Java体系结构、特性和优势 一、JAVA简史 【1】SUN公司 【2】Java为什么被发明

如果通过Java来完成大数据开发,需要学习哪些内容

首先&#xff0c;当前大数据领域的开发岗位确实比较多&#xff0c;近两年有不少大数据方向的研究生也会从事大数据开发岗位&#xff0c;而Java语言则是大数据开发人员的常用工具&#xff0c;所以如果要从事大数据开发岗位&#xff0c;可以重点学习一下Java语言。 大数据开发有…

Java大数据开发做什么?Java大数据开发成长路线

Java开发是IT行业的经典岗位&#xff0c;行业当中存在普遍的需求&#xff0c;Web开发、Android开发、游戏开发等岗位&#xff0c;基本上Java语言是主力队伍。而进入大数据时代&#xff0c;Java又在大数据方向上有了用武之地。今天我们主要来讲讲Java大数据开发做什么&#xff0…

23个java大数据处理框架

本文转自&#xff1a;https://www.cnblogs.com/stm32stm32/p/6413557.html 目前&#xff0c;编程人员面对的最大挑战就是复杂性&#xff0c;硬件越来越复杂&#xff0c;OS越来越复杂&#xff0c;编程语言和API越来越复杂&#xff0c;我们构建的应用也越来越复杂。根据外媒的一…

大数据开发要学Java框架吗?

大数据开发要学Java框架吗&#xff1f;学习大数据要去学习Java而且还要精通&#xff0c;不仅要掌握Java基础知识还要掌握一些核心的Java架构。从java基础开始,学习大数据开发过程中必备的离线数据分析、实时数据分析和内存数据计算等&#xff0c;掌握大数据体系中几乎所有的核心…

java 大数据以及Hadoop相关概念介绍

一、大数据的基本概念 1.1、什么是大数据 大数据指的就是要处理的数据是TB级别以上的数据。大数据是以TB级别起步的。在计算机当中&#xff0c;存放到硬盘上面的文件都会占用一定的存储空间&#xff0c;例如&#xff1a; 文件占用的存储空间代表的就是该文件的大小&#xff0…