crosswalk学习整理

简单说明

Crosswalk使用了Chromium内核,这意味着只能在Android 4.0以上才能使用,安卓4.4已经采用Chrominum内核,如果只是做简单的网页显示,原生的足够了,4.4以下的webkit内核有许多bug,各个版本不一致,api使用都有些不同(文件上传在4.4~4.4.2这之间的根本无解)。对于要做hybrid应用以及想解决平台一致性问题可以使用Crosswalk,但是牺牲的是apk的体积,集成Crosswalk体积至少要增大20M+(官方提供了shared模式,apk里不带runtime环境,首次运行时下载安装,可以自由配置下载地址)

开发方式一

如果要使用Crosswalk提供的开发方式,需要配置开发环境,并且后续的创建项目打包都是基于命令行形式的,适合不会Android的web开发人员使用,比如下面这些都需要安装

  • Java JDK
  • Apache Ant
  • Android SDK
  • NPM
  • Crosswalk App Tools

中文参考
官方文档

开发方式二 Embedding the Crosswalk Project

相当于嵌入一个WebView
Using the embedding API is only recommended for cases where you have a substantial amount of Java code in your application, but want to write the UI (or parts of the UI) using web technologies.

  • XWalkView默认不能设置Visible和动画
    XWalkView represents an Android view for web apps/pages. Thus most of attributes for Android view are valid for this class. Since it internally uses android.view.SurfaceView for rendering web pages by default, it can’t be resized, rotated, transformed and animated due to the limitations of SurfaceView. Alternatively, if the preference key ANIMATABLE_XWALK_VIEW is set to True, XWalkView can be transformed and animated because TextureView is intentionally used to render web pages for animation support. Besides, XWalkView won’t be rendered if it’s invisible.
  • XWalkView需要开启硬件加速
    XWalkView needs hardware acceleration to render web pages. As a result, the AndroidManifest.xml of the caller’s app must be appended with the attribute “android:hardwareAccelerated” and its value must be set as “true”.
1
2
<application android:name="android.app.Application" android:label="XWalkUsers"
android:hardwareAccelerated="true">
  • 注意生命周期的接管
    Unlike other Android views, this class has to listen to system events like application life cycle, intents, and activity result. The web engine inside this view need to get and handle them. And the onDestroy() method of XWalkView MUST be called explicitly when an XWalkView won’t be used anymore, otherwise it will cause the memory leak from the native side of the web engine. It’s similar to the destroy() method of Android WebView. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import android.app.Activity;
import android.os.Bundle;

import org.xwalk.core.XWalkResourceClient;
import org.xwalk.core.XWalkUIClient;
import org.xwalk.core.XWalkView;

public class MyActivity extends Activity {
XWalkView mXwalkView;

class MyResourceClient extends XWalkResourceClient {
MyResourceClient(XWalkView view) {
super(view);
}

@Override
WebResourceResponse shouldInterceptLoadRequest(XWalkView view, String url) {
// Handle it here.
...
}
}

class MyUIClient extends XWalkUIClient {
MyUIClient(XWalkView view) {
super(view);
}

@Override
void onFullscreenToggled(XWalkView view, String url) {
// Handle it here.
...
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
mXwalkView = new XWalkView(this, null);
setContentView(mXwalkView);
mXwalkView.setResourceClient(new MyResourceClient(mXwalkView));
mXwalkView.setUIClient(new MyUIClient(mXwalkView));
mXwalkView.load("http://www.crosswalk-project.org", null);
}

@Override
protected void onPause() {
super.onPause();
if (mXwalkView != null) {
mXwalkView.pauseTimers();
mXwalkView.onHide();
}
}

@Override
protected void onResume() {
super.onResume();
if (mXwalkView != null) {
mXwalkView.resumeTimers();
mXwalkView.onShow();
}
}

@Override
protected void onDestroy() {
super.onDestroy();
if (mXwalkView != null) {
mXwalkView.onDestroy();
}
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mXwalkView != null) {
mXwalkView.onActivityResult(requestCode, resultCode, data);
}
}

@Override
protected void onNewIntent(Intent intent) {
if (mXwalkView != null) {
mXwalkView.onNewIntent(intent);
}
}
}

配置gradle

  • 添加仓库
    1
    2
    3
    4
    5
    6
    repositories {
    mavenLocal()
    maven {
    url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'
    }
    }
  • 添加cpu配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    android {
    //...
    splits {
    abi {
    enable true
    reset()
    include 'x86', 'armeabi-v7a' //select ABIs to build APKs for
    universalApk true //generate an additional APK that contains all the ABIs
    }
    }
    productFlavors {
    armv7 {
    versionCode defaultConfig.versionCode + 2
    ndk {
    abiFilters "armeabi-v7a", ""
    }
    }
    x86 {
    versionCode defaultConfig.versionCode + 4
    ndk {
    abiFilters "x86", ""
    }
    }
    }
    }
  • 添加XWalkview依赖
    1
    compile 'org.xwalk:xwalk_core_library:18.48.477.13'

混淆配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-keepclasseswithmembernames class * {native <methods>;}

-keepattributes JNINamespace
-keepattributes CalledByNative
-keepattributes Annotation
-keepattributes EnclosingMethod

-keep class org.xwalk.{ *; }
-keep interface org.xwalk.{ *; }
-keep class com.example.extension.**{ *; }
-keep class org.crosswalkproject.*{ *; }

-keep class org.chromium.base.Native
-keep class org.chromium.base.annotations.JNINamespace
-keepclasseswithmembers class org.chromium. { @org.chromium.base.AccessedByNative <methods>; }
-keepclasseswithmembers class org.chromium. { @org.chromium.base.Native <methods>; }

-keep class org.chromium.** { native <methods>; }

-keep class org.chromium.base.UsedBy
#-keep @org.chromium.base.UsedBy class
-keepclassmembers class * { @org.chromium.base.UsedBy *; }

#-keep @org.chromium.base.annotations.JNINamespace class
-keepclassmembers class * { @org.chromium.base.annotations.CalledByNative *; }
-dontnote org.chromium.net.AndroidKeyStore
-dontnote org.chromium.net.UrlRequest$ResponseHeadersMap
-keep class org.chromium.ui.ColorPickerAdvanced { <methods>; }
-keep class org.chromium.ui.ColorPickerMoreButton { <methods>; }
-keep class org.chromium.ui.ColorPickerSimple { <methods>; }

实现文件选择: Implementing a file input with Camera support in Android with CrossWalk

1
2
3
4
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
xWalkView.onActivityResult(requestCode, resultCode, data);
}

should be overridden into the class XWalkUIClient:

1
2
3
4
@Override
public void openFileChooser(XWalkView view, final ValueCallback uploadFile, String acceptType, String capture) {
uploadFile.onReceiveValue(null);
}

大量sample
Crosswalk Project — replacement of Android WebView. Integration problems

XWalkPreferences.setValue只能在XWalkView未初始化之前调用,全局性配置

操蛋的回退键问题

XWalkView会接管Activity的onKeyUp,默认返回键就是回退历史,但是没有提供接口去控制。导致,一个页面有输入框,弹出输入法后不是关闭输入法,而是直接回退历史记录。不明白为什么要这么设计
解决方案:重写dispatchKeyEvent,当发现是回退键按下直接返回false,扔给外层处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class XWalkViewEx extends XWalkView {
public XWalkViewEx(Context context) {
super(context);
init();
}


public XWalkViewEx(Context context, Activity activity) {
super(context, activity);
init();
}

public XWalkViewEx(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
}

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
//我要自己处理回退键
return false;
}
return super.dispatchKeyEvent(event);
}

public static void iniConfig() {
XWalkPreferences.setValue(XWalkPreferences.ANIMATABLE_XWALK_VIEW, true);
}
}
1
2
3
4
5
6
7
8
@Override
public boolean onKeyBack() {
if (mXWalkView.getNavigationHistory().canGoBack()) {
mXWalkView.getNavigationHistory().navigate(XWalkNavigationHistory.Direction.BACKWARD, 1);
return true;
}
return false;
}