Cordova 开发日记 08:与原生 Android 交互
以下是记录 JS 调用 Native 的全过程
定义 Java Class
java
package com.junl.cordova;
import android.widget.Toast;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaArgs;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
public class NUI extends CordovaPlugin {
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
// 前端回调的方法名
if ("showToast".equals(action)) {
return executeShowToast(args, callbackContext);
} else {
callbackContext.error("发生异常,请检查API使用是否正确");
return false;
}
}
/**
* 显示toast的原生方法
*/
private boolean executeShowToast(JSONArray args, CallbackContext callbackContext) {
try {
CordovaArgs cordovaArgs = new CordovaArgs(args);
String text = cordovaArgs.getJSONObject(0).getString("text");
android.widget.Toast.makeText(cordova.getActivity(), text, Toast.LENGTH_LONG).show();
callbackContext.success();
return true;
} catch (Exception e) {
e.printStackTrace();
callbackContext.error("显示toast异常");
return false;
}
}
}
声明对外暴露的配置
在 config.xml
中:
xml
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.example.hello" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<feature name="Whitelist">
<param name="android-package" value="org.apache.cordova.whitelist.WhitelistPlugin" />
<param name="onload" value="true" />
</feature>
<feature name="NUI">
<param name="android-package" value="com.junl.cordova.NUI"/>
</feature>
<name>HelloWorld</name>
<description>
A sample Apache Cordova application that responds to the deviceready event.
</description>
<author email="[email protected]" href="http://cordova.io">
Apache Cordova Team
</author>
<content src="index.html" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<allow-intent href="market:*" />
<preference name="loglevel" value="DEBUG" />
</widget>
JS 调用示例
原生方法的调用方式:exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);
html
<script src="cordova.js"></script>
js
function success(msg) {}
function error(msg) {}
function onDeviceReady() {
const params = [
{
text: "你好,我是 Toast",
},
];
cordova.exec(success, error, "NUI", "showToast", params);
}
document.addEventListener("deviceready", onDeviceReady, false);
问题:webview 中进行跳转时打开的是外部浏览器
希望直接替换 webview 的页面,这个需要在 config.xml
配置一下白名单
xml
<widget id="com.example.hello"
version="1.0.0"
xmlns="http://www.w3.org/ns/widgets">
<!--允许加载所有url-->
<allow-navigation href="http://*/*" />
<allow-navigation href="https://*/*" />
</widget>
问题:android 9.0 webview 不能使用 http 协议打开页面
增加 android:usesCleartextTraffic
为 true
xml
<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hello"
android:hardwareAccelerated="true"
android:versionCode="10000"
android:versionName="1.0.0">
<!--...-->
<application
android:hardwareAccelerated="true"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">
<!--...-->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
问题:如何避免启动页白屏
我的解决思路是把启动图设置为第一个 activity
的背景。这样启动时会立即生效。
在 res/values/styles.xml
中定义样式:
xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 应用启动页(StartingWindow)的theme -->
<style name="AppTheme.StartingWindowTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@drawable/img_welcome</item>
</style>
<style name="AppTheme.MainTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar"></style>
</resources>
在 AndroidManifest.xml
中应用样式:
xml
<activity
android:name="MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale"
android:label="@string/activity_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme.StartingWindowTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter android:label="@string/launcher_name">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
在 MainActivity.java
中还原样式:
java
package com.example.hello;
import android.os.Bundle;
import com.junl.cordova.utils.PermissionUtil;
import org.apache.cordova.CordovaActivity;
public class MainActivity extends CordovaActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppTheme_MainTheme); // 恢复原有的样式
// 允许 Cordova 后台运行
Bundle extras = getIntent().getExtras();
if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
moveTaskToBack(true);
}
/* 申请程序所需权限 */
if (!PermissionUtil.hasPermission(this)) {
PermissionUtil.requestPermission(this);
}
loadUrl(launchUrl);
}
}