2016/9/12

Crosswalk in Android


曾經有一段時間,Facebook 為了達成跨平台的開發,是利用網頁的方式,製作 APP 的頁面,但最終還是敵不過效能的問題,改以原生的方式製作 APP。但從另一個角度來看,手機的瀏覽器在 HTML5 的支援進度不一,為了在不同瀏覽器都達成相同的畫面展示,工程師需要歷經很多時間去努力,相信大家都知道那一段 IE 的黑暗時代。


在 Android 雖然可以使用 WebView 進行頁面瀏覽,但其實在 Android SDK 升級之後,瀏覽器也跟著有些變化,因此雖然同樣使用 WebView,在不同版本的手機,也可能會發生一些問題。為了解決網頁程式在不同瀏覽器的展示差異,crosswalk 採用了另一種方式來解決,就是將原生的 Chromium 專案,整個包裝成一個手機開發的 Library,利用這個內建的瀏覽器,解決在不同 Android 版本的手機上,都可以達成相同的 UI 展示的問題。


使用 crosswalk 可以將整個瀏覽器封裝在 APP 中,這樣在使用 APP 時,雖然裡面是網頁,但實際上卻像是一個完整的 APP。但 crosswalk 也有缺點,就是在不同的 CPU Platform 都需要有對應的 binary library,以 Android 來說,要同時支援 x86 及 ARM CPU,必須增加數十 MB 的空間浪費。


就 crosswalk 的 FAQ 說明,目前已經有超過 300 個 APP 在 app store 中,大部分都是遊戲,或許是因為遊戲開發如果是以跨平台的角度來開發,網頁的遊戲可以利用 crosswalk 很快就包裝成 APP 出版,而使用者在面對遊戲這種應用,也比較能容忍 APP 本身消耗很多手機的儲存空間。The Power of Crosswalk 還提到了 crosswalk 能夠處理 WebCL、SIMD,這是舊版本 SDK 的 WebView 無法達到的功能。


測試怎麼使用 crosswalk


crosswalk android 文件 是以 command line 的方式進行包裝,我們測試時,是改用 Android Studio,由建立一個 Android Project 開始。


首先建立一個新的 Android 專案


Application name: CrosswalkTest
Company Domain: testme.com.tw

勾選 Phone and Tablet
Minimum SDK: API 21: Android 5.0

Empty Activity

Activity Name: MainActivity
勾選 Generate Layout File
Layout Name: activity_main

修改 AndroidManifest.xml,增加 APP 使用手機資源的權限


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="tw.com.testme.crosswalktest">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.NETWORK_ACCESS" />
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

其中最重要的是 android.permission.INTERNET、ndroid.permission.NETWORK_ACCESS 使用網路的權限,其他的部分都是附加的。


修改 app/build.gradle,增加 repositories 以及 dependencies 的內容


apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.1"

    defaultConfig {
        applicationId "tw.com.testme.crosswalktest"
        minSdkVersion 21
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

repositories {
    maven {
        url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.1.1'
    compile 'org.xwalk:xwalk_core_library:19.49.514.5'
}

修改 activity_main.xml,畫面的 Layout。crosswalk 是以 org.xwalk.core.XWalkView 取代原本 Android SDK 的 WebView。


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="tw.com.testme.crosswalktest.MainActivity">
    
    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:focusable="true"
        android:hint="Enter Text"
        android:text="http://www.maxkit.com.tw/"
        android:textColorHighlight="#ff7eff15"
        android:textColorHint="#ffff25e6" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_below="@+id/editText1"
        android:text="Enter" />

    <org.xwalk.core.XWalkView
        android:id="@+id/xwalkWebView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentEnd="true"
        android:layout_below="@+id/button1"
        android:orientation="vertical" />

</RelativeLayout>

修改 MainActivity.java


package tw.com.testme.crosswalktest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import org.xwalk.core.XWalkPreferences;
import org.xwalk.core.XWalkSettings;
import org.xwalk.core.XWalkView;

public class MainActivity extends AppCompatActivity {

    private Button button1;
    private EditText editText1;

    private XWalkView xWalkWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        button1 =(Button)findViewById(R.id.button1);
        editText1 =(EditText)findViewById(R.id.editText1);

        xWalkWebView=(XWalkView)findViewById(R.id.xwalkWebView);

        xWalkWebView.clearCache(true);

        XWalkSettings webSettings = xWalkWebView.getSettings();
//        webSettings.setJavaScriptEnabled(true);
//        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);

        //xWalkWebView.load("javascript:document.body.contentEditable=true;", null);
        //xWalkWebView.load("https://www.google.com.tw", null);

        // turn on debugging
        XWalkPreferences.setValue(XWalkPreferences.REMOTE_DEBUGGING, true);
        XWalkPreferences.setValue(XWalkPreferences.JAVASCRIPT_CAN_OPEN_WINDOW, true);
        XWalkPreferences.setValue(XWalkPreferences.ALLOW_UNIVERSAL_ACCESS_FROM_FILE, true);

        xWalkWebView.setKeepScreenOn(true);
        xWalkWebView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
        String urlToBeLoaded = editText1.getText().toString();

        xWalkWebView.load(urlToBeLoaded, null);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String url = editText1.getText().toString();

                xWalkWebView.load(url, null);

            }
        });
    }

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

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

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

執行 APP


在上面的 EditText 填寫網址,點 "Enter" 就可以在下面顯示網頁,但也不是每個網頁都可以顯示出來,這部分還需要去了解原因,目前判斷有可能是網頁的內容,crosswalk 沒辦法 render 出來的關係。



References


Crosswalk入門


Android Studio如何Import Module 即項目依賴(針對非Gradle項目,以Crosswalk為例)


Embedding Crosswalk in Android Studio


[Android] Crosswalk: 取代 WebView 的函式庫


為什麼放棄WebView