一、mui离线打包
工具:Android Studio、HBuilder、5+ SDK
1、下载5+ SDK(http://ask.dcloud.net.cn/article/103)
2、使用Android Studio创建一个新项目
3、复制SDK->libs->lib.5plus.base-release.aar文件到原生工程工程的app->libs目录下
4、打开项目app目录下的build.gradle文件,将aar包添加引用,加入如下代码:
implementation fileTree(dir: 'libs', include: ['*.aar'])
5、修改工程的targetSdkVersion大于等于21
6、打开工程的Androidmanifest.xml文件,复制以下内容替换该文件中原有application节点下的内容
<application
android:name="io.dcloud.application.DCloudApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<!-- 启动页 -->
<activity
android:name="io.dcloud.PandoraEntry"
android:configChanges="orientation|keyboardHidden|screenSize|mcc|mnc|fontScale"
android:hardwareAccelerated="true"
android:screenOrientation="user"
android:theme="@style/TranslucentTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
注:若自定义application,可采取继承DCloudApplication,添加tools:replace="android:name"至application节点下,避免merge冲突。
7、复制SDK->assets->data目录和目录下的文件到工程的src->main->assets目录下,新创建的工程默认没有assets目录,可在与java同级目录下创建assets目录。
注: apps目录下应用资源的路径为[appid].www, appid为应用资源manifest.json文件中id节点的值,也就是mui项目manifest.json里的appid,如下图:
8、将HBuilder里的代码进行本地打包,将应用资源打包进入Android项目的assets->apps对应目录下,HBuilder点击发行->本地打包->生成本地打包App资源,将路径选择到apps即可。
9、修改assets->data->dcloud_control.xml文件的apps->app->appid属性的值改为当前应用manifest.json文件id节点的值
运行项目,App便可进入到mui的页面,但是你会发现第一个页面是这样的
这是因为io.dcloud.PandoraEntry入口会先加载一个Splash页面,暂时不知道如何去掉,我们需要将应用的图标(文件名为icon.png)和启动图片(文件名为splash.png)放入drawable中,这样Splash页面就显示你的splash图片,如图
二、插件开发
官方文档一开始可能看的有点蛋疼,这里就写个通俗点的例子,利用插件开发来进行动态权限申请,也就是在mui利用插件到原生里进行动态申请权限,然后将结果回调到mui这边js里。
1、JS扩展插件编写
实现同步扩展方法时,调用JS Plugin Bridge的window.plus.bridge.execSync()
方法,该方法可同步获取Native插件返回的运行结果。
void plus.bridge.execSync( String service, String action, Array<String> args );
实现异步扩展方法时,调用JS Plugin Bridge的plus.bridge.exec()方法,该方法会通知Native层插件执行指定方法,运行结果会通过回调的方式通知JS层。
void plus.bridge.exec( String service, String action, Array<String> args );
2、实现扩展插件类
(1) 创建一个继承自StandardFeature的类
创建一个继承自StandardFeature的类,实现第三方插件扩展。
创建插件类需要引入的包
import io.dcloud.DHInterface.IWebview;
import io.dcloud.DHInterface.StandardFeature;
import io.dcloud.util.JSUtil;
(2) 实现扩展方法
Native层扩展插件的方法名需要和JS Plugin Bridge里windows.plus.bridge.exec()或windows.plus.bridge.execSync()方法的第二个传入参数相同,否则无法调用到指定的方法。
public void PluginTestFunction(IWebview pWebview, JSONArray array)
(3) 返回值到js层
同步执行方法:
同步执行方法在返回结果时可以直接将结果以return的形式返回给js层,返回的结果需要调用如下方法处理要返回的字符串。
JSUtil.wrapJsVar("Html5 Plus Plugin Hello1!",true);
异步执行方法:
JSUtil.execCallback(pWebview, cbId, (which==AlertDialog.BUTTON_POSITIVE)?"ok":"cancel", JSUtil.OK, false, false);
3、关联JS插件名和原生类
在Android原生工程的assets\data\dcloud_properties.xml文件中声明插件类别名和Native层扩展插件类的对应关系
<properties>
<features>
...
<feature name="plugintest" value="com.kmvc.H5PlusPlugin.PGPlugintest"/>
</features>
<services>
...
<service name="plugintest" value="com.kmvc.H5PlusPlugin.PGPlugintest"></service>
</services>
</properties>
注:上面的value值便是扩展插件类的路径。
在应用的manifest.json文件中还需要添加扩展插件的应用使用权限,permissions节点下添加如下:
4、使用实例
上面便是插件开发的核心步骤,下面举个例子,就是上面说的动态权限申请
(1)、html调用上面写好的js扩展插件,js扩展插件将调用原生那边的插件类相应方法
plus.plugintest.PluginTestFunction("PermissionCheck", "Plus", "AsyncFunction", "MultiArgument!", function(
result) {
if(result[0] == "MyPermissionOK") {
//getVersion();
} else if(result[0] == "MyPermissionDenied") {
mui.alert("关键权限必须打开,请进入设置打开相关权限,否则将影响应用正常使用!", "提示", function() {
//getVersion();
})
} else {
mui.alert("关键权限必须打开,请进入设置打开相关权限,否则将影响应用正常使用!", "提示", function() {
//getVersion();
})
}
}, function(result) {
mui.alert("权限申请失败,请进入设置打开相关权限,否则将影响应用正常使用!", "提示", function() {
//getVersion();
})
});
(2)、原生插件类被调用之后获取js传过来的值,并跳转到权限申请的原生页面
public void PluginTestFunction(IWebview pWebview, JSONArray array) {
// 原生代码中获取JS层传递的参数,
// 参数的获取顺序与JS层传递的顺序一致
mWebview=pWebview;
CallBackID = array.optString(0);
String Method = array.optString(1);
JSONArray newArray = new JSONArray();
String par1 = array.optString(2);
String par2 = array.optString(3);
String par3 = array.optString(4);
String par14 = array.optString(5);
if("PermissionCheck".equals(Method)){
Intent intent = new Intent(mContext, XXX.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("CallBackID",CallBackID);
mContext.startActivity(intent);
}
}
(3)、权限申请的原生页面获取完权限申请结果后调用异步或同步方法回调到js层,这里用的异步,如下
if(permission.granted){
// All permissions are granted !
JSUtil.execCallback(iWebview, callBackID, new JSONArray().put("MyPermissionOK"), JSUtil.OK, false);
finish();
}else if(permission.shouldShowRequestPermissionRationale){
// At least one denied permission without ask never again
JSUtil.execCallback(iWebview, callBackID, new JSONArray().put("MyPermissionDenied"), JSUtil.OK, false);
finish();
}else{
// At least one denied permission with ask never again
JSUtil.execCallback(iWebview, callBackID, new JSONArray().put("MyPermissionNeverAsked"), JSUtil.OK, false);
finish();
}
上面的代码重点在JSUtil.execCallback方法,就是用来回调到js层的,具体参数如下
iWebView:扩展插件方法运行的窗口
callBackID:回调函数的唯一标识
pMessage:回调函数的参数
pStatus:操作是否成功,成功则使用JSUtil.OK,否则使用错误代码
isJson:回调函数参数是否为JSON数据
回调到js层后便可根据返回的结果进行处理,如上面第(1)步,我们根据权限申请的情况进行相应提示,并做了版本检查更新操作。
最终效果:
ps:动态权限申请,需要targetSdkVersion大于等于23。