把 Cordova Webview 嵌入 Android Native App

Steps:

1. 把 Requirements 都下載安裝好

2. 建立 Cordova Android Project

如果 Cordova projects 放置的位置與 Android App projects 是不同的目錄的話,test_cordova 可以改成與 android app project 相同名稱,這樣會比較好知道 cordova project 是對應到哪個 android project,此外 com.example.hello 也可以改成跟 android app 一樣。

cordova create test_cordova com.example.hello HelloWorld
cordova platform add android
cordova build

在 build 之前可以視情況加入需要的 plugins,例如:

cordova plugin add cordova-plugin-device
cordova plugin add cordova-plugin-x-toast

3. 建立 Android App Project

用預設的即可,有一個 Empty Activity (MainActivity)。

4. 安裝 Cordova Project 環境到 Android Project

Compile Cordova Lib

參考 Android WebViews - Apache Cordova 步驟 1~3,把 cordova-x.x.x.jar 複製到 /libs 之後,編輯 app/build.gradle,加上:

compile files('libs/cordova-x.x.x.jar')

Example:

dependencies {
	compile fileTree(include: ['*.jar'], dir: 'libs')
	testCompile 'junit:junit:4.12'
	compile 'com.android.support:appcompat-v7:23.4.0'
	compile files('libs/cordova-5.2.2.jar')
}

複製依存檔案 (左邊是 Cordova project 的 path,右邊是 Android project 的 path)

www folder

platforms/android/assets/www -> src/main/assets/www

plugins (com/example/hello 以外的目錄,安裝 plugins 多出來的那些)

platforms/android/src/plugin_folder -> src/main/java/

config.xml

platforms/android/res/xml/config.xml -> src/main/res/xml/

然後 sync 一下 Gradle 情報。

5. 建立基本運作 Activity

把 Cordova Webview 嵌入 Android Native App 的基本作業到第四步驟就完成了,接下來可以建立基本運作的 Activity,來測試看看是不是正常運作。

建立以下兩個 Empty Activity: TestCordovaActivity, TestCordovaWithLayoutActivity,然後編輯 java 及 layout 如下:

TestCordovaActivity

public class TestCordovaActivity extends CordovaActivity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		super.init();
		// Load your application
		launchUrl = "file:///android_asset/www/index.html";
		loadUrl(launchUrl);
	}
}

@Override
public void onDestroy() {
	ViewGroup viewGroup = (ViewGroup)this.findViewById(android.R.id.content);
	SystemWebView webView = (SystemWebView) viewGroup.getChildAt(0);
	viewGroup.removeView(webView);
	webView.removeAllViews();
	super.onDestroy();
}

TestCordovaWithLayoutActivity

public class TestCordovaWithLayoutActivity extends CordovaActivity {

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_test_cordova_with_layout);
		super.init();

		// Load your application
		launchUrl = "file:///android_asset/www/index2.html";
		loadUrl(launchUrl);

	}

	@Override
	protected CordovaWebView makeWebView() {
		SystemWebView webView = (SystemWebView)findViewById(R.id.cordovaWebView);
		return new CordovaWebViewImpl(new SystemWebViewEngine(webView));
	}

	@Override
	protected void createViews() {
		// Why are we setting a constant as the ID? This should be investigated
		/*
		appView.getView().setId(100);
		appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
			ViewGroup.LayoutParams.MATCH_PARENT,
			ViewGroup.LayoutParams.MATCH_PARENT));
		setContentView(appView.getView());
		*/

		if (preferences.contains("BackgroundColor")) {
			int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
			// Background of activity:
			appView.getView().setBackgroundColor(backgroundColor);
		}

		appView.getView().requestFocusFromTouch();
	}

	@Override
	public void onDestroy() {
		SystemWebView webView = (SystemWebView)findViewById(R.id.cordovaWebView);
		((ViewGroup)webView.getParent()).removeView(webView);
		webView.removeAllViews();
		// If we called webView.destory(), we will get 'mWebViewCore is null' error.
		//webView.destroy();
		super.onDestroy();
	}

}

activity_test_cordova_with_layout.xml

<?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"
	android:paddingBottom="@dimen/activity_vertical_margin"
	android:paddingLeft="@dimen/activity_horizontal_margin"
	android:paddingRight="@dimen/activity_horizontal_margin"
	android:paddingTop="@dimen/activity_vertical_margin"
	tools:context="com.fandratec.fssfordksh.TestCordovaWithLayoutActivity">

	<TextView
		android:layout_width="match_parent"
		android:layout_height="100dp"
		android:background="#FF0000"
		android:textColor="#FFFFFF"
		android:gravity="center"
		android:text="This is native text view"
		/>

	<org.apache.cordova.engine.SystemWebView
		android:id="@+id/cordovaWebView"
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		/>

</RelativeLayout>

MainActivity

public class MainActivity extends AppCompatActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	public void startCordovaActivity(View view) {
		Intent intent = new Intent(this, TestCordovaActivity.class);
		startActivity(intent);
	}

	public void startCordovaActivityWithLayout(View view) {
		Intent intent = new Intent(this, TestCordovaWithLayoutActivity.class);
		startActivity(intent);
	}

}

activity_main.xml

<?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"
	android:paddingBottom="@dimen/activity_vertical_margin"
	android:paddingLeft="@dimen/activity_horizontal_margin"
	android:paddingRight="@dimen/activity_horizontal_margin"
	android:paddingTop="@dimen/activity_vertical_margin"
	tools:context="com.fandratec.fssfordksh.MainActivity">

	<TextView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:text="Hello World!"
		android:id="@+id/textView"/>

	<Button
		android:text="Start Cordova Activity Without Layout"
		android:onClick="startCordovaActivity"
		android:layout_width="200dp"
		android:layout_height="100dp"
		android:id="@+id/button"
		android:layout_below="@+id/textView"
		android:layout_alignParentStart="true"/>

	<Button
		android:text="Start Cordova Activity With Layout"
		android:onClick="startCordovaActivityWithLayout"
		android:layout_width="200dp"
		android:layout_height="100dp"
		android:layout_below="@+id/button"
		android:layout_alignParentStart="true"/>

</RelativeLayout>

以上都弄好後,就可以 run 了。

6. 延伸

如果這個 Android App 要連線到 Internet 或是取得 GPS 座標,記得在 AndroidManifest.xml 加入權限需求宣告,如果沒加上的話會出現錯誤。(例如 loadUrl(REMOTE_URL) 就會直接獲得 "Application 錯誤")

<manifest>
	<uses-permission android:name="android.permission.INTERNET" />
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

另外,現在版本的 Cordova project 預設啟用 whitelist 機制,想從 CordovaWebView 連到local (/assets/www) 外的內容並在其中呈現,得在 config.xml 加上設定,要不然會另外呼叫預設瀏覽器:

<widget>
	<allow-navigation href="https://YOUR.SITE/*" />
</widget>

References:

分類: 軟體開發,標籤: 。這篇內容的永久連結