几年前使用Cordova 进行两个app之间的相互调用和通讯;当时也是几经折腾,今天把它整理出来,理一下思路,也方便有同样需求的朋友参考
一、require引入 plugin
require("cordova!com.lampa.startapp-master");
这个插件下载 https://github.com/dengxiaoning/com.lampa.startapp
(申明:该插件参考https://github.com/lampaa/com.lampa.startapp自己实现了ios的参数传递以及 downloadApps
应用下载的功能)
二、源文件分析
首选来看看这个插件的实现
plugin.xml
<js-module src="www/startApp.js" name="startapp">
<merges target="startApp" />
</js-module>
指定了js module 的路径和调用时的名称target="startApp"
<!-- android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="startApp">
<param name="android-package" value="com.lampa.startapp.startApp"/>
</feature>
</config-file>
<source-file src="src/android/startApp.java" target-dir="src/com/lampa/startapp" />
</platform>
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="startApp">
<param name="ios-package" value="startApp"/>
</feature>
</config-file>
<header-file src="src/ios/startApp.h"/>
<source-file src="src/ios/startApp.m"/>
</platform>
指定插件源文件路径,根据不同平台,定义插件包名、将文件写入平台指定的路径下
startApp.js
var exec = require('cordova/exec');
module.exports = {
set: function(params, extra) {
var output = [params];
if(extra != undefined) {
output.push(extra);
}
else {
output.push(null);
}
return {
start: function(completeCallback, errorCallback) {
completeCallback = completeCallback || function() {};
errorCallback = errorCallback || function() {};
exec(completeCallback, errorCallback, "startApp", "start", output);
},
},
/**
* extra values
*/
getExtras: function(completeCallback, errorCallback) {
exec(completeCallback, errorCallback, "startApp", "getExtras", []);
},
}
该js 实现了使用Cordova 调用android 和 ios 原生接口然后返回参数,
如:exec(completeCallback, errorCallback, "startApp", "start", output);
其中startApp
指定调用的类名
【ios是@interface startApp : CDVPlugin
,android是public class startApp extends CordovaPlugin
】;
start
指定调用该类的方法名;其他参数就是cordova 导出的成功、错误回调和携带返回数据。
startApp.java(中增加方法)
/**
* download application from market
* */
public void downloadApps(JSONArray args, CallbackContext callback){
JSONObject params;
try {
if(args.get(0) instanceof JSONObject){
params = args.getJSONObject(0);
if(params.has("application")) {
Uri uri = Uri.parse("market://details?id="+params.getString("application")+"");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
cordova.getActivity().startActivity(it);
}
if(params.has("downloadurl")){
cordova.getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(params.getString("downloadurl"))));
}
}
}
catch (JSONException e) {
callback.error("JSONException: " + e.getMessage());
e.printStackTrace();
}
catch (ActivityNotFoundException e) {
callback.error("ActivityNotFoundException: " + e.getMessage());
e.printStackTrace();
}
}
增加Android 根据路径下载apk的方法
startApp.h(中增加方法)
- (void)getExtras:(CDVInvokedUrlCommand*)command;
- (void)downloadApps:(CDVInvokedUrlCommand*)command;
- (void)exitApplication:(CDVInvokedUrlCommand*)command;
增加ISO平台下 获取应用调用时传入的参数,在未安装时根据url下载应用
startApp.m(中增加方法)
- (void)getExtras:(CDVInvokedUrlCommand*)command{
CDVPluginResult* pluginResult = nil;
// 从 url中获取保存的参数,将其返回给Cordova
NSString *userurl = [[NSUserDefaults standardUserDefaults] objectForKey:@"url"];
if(userurl == nil || userurl == NULL){
NSString *addResult = @"returnFalse";
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:addResult];
}else{
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:userurl];
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"url"];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
-(void)downloadApps:(CDVInvokedUrlCommand*)command{
CDVPluginResult* pluginResult = nil;
NSString* scheme = [command.arguments objectAtIndex:0];
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:scheme]]) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:scheme]];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:(true)];
}
else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsBool:(false)];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)exitApplication:(CDVInvokedUrlCommand*)command {
exit(0);
}
三、配置应用Scheme
3.1 使用xcode 手动配置
首先将该ios文件导入xcode,点击.xcodeproj后缀的文件,xcode将会自动打开,再找到 LSApplicationQueriesScheme 为应用添加允许访问的app的白名单。 然后添加Scheme(只有这样另一个app在添加白名单时才知道写什么)。具体操作如下图
|
|
|
|
同时配置ios访问设备的一些权限,否则无权限访问时应用会崩溃
找到 你的项目名-info.plist
文件打开,在最后一个array标签下加入配置
<!-- 相册 -->
<key>NSPhotoLibraryUsageDescription</key>
<string>App需要您的同意,才能访问相册</string>
<!-- 相机 -->
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能访问相机</string>
<!-- 麦克风 -->
<key>NSMicrophoneUsageDescription</key>
<string>App需要您的同意,才能访问麦克风</string>
<!-- 位置 -->
<key>NSLocationUsageDescription</key>
<string>App需要您的同意,才能访问位置</string>
<!-- 在使用期间访问位置 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>App需要您的同意,才能在使用期间访问位置</string>
<!-- 始终访问位置 -->
<key>NSLocationAlwaysUsageDescription</key>
<string>App需要您的同意,才能始终访问位置</string>
<!-- 日历 -->
<key>NSCalendarsUsageDescription</key>
<string>App需要您的同意,才能访问日历</string>
<!-- 提醒事项 -->
<key>NSRemindersUsageDescription</key>
<string>App需要您的同意,才能访问提醒事项</string>
<!-- 运动与健身 -->
<key>NSMotionUsageDescription</key> <string>App需要您的同意,才能访问运动与健身</string>
<!-- 健康更新 -->
<key>NSHealthUpdateUsageDescription</key>
<string>App需要您的同意,才能访问健康更新 </string>
<!-- 健康分享 -->
<key>NSHealthShareUsageDescription</key>
<string>App需要您的同意,才能访问健康分享</string>
<!-- 蓝牙 -->
<key>NSBluetoothPeripheralUsageDescription</key>
<string>App需要您的同意,才能访问蓝牙</string>
<!-- 媒体资料库 -->
<key>NSAppleMusicUsageDescription</key>
<string>App需要您的同意,才能访问媒体资料库</string>
操作截图
|
|
3.2 使用plugin.xml自动配置
自己写的一个配置ios 相关权限和Scheme的xml
创建如上类似文件夹考入即可:
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-plistconfig" version="5.3.0">
<name>开启第三方应用</name>
<description>新开启第三方应用相关配置</description>
<license>MIT</license>
<keywords>cordova,sina</keywords>
<!--require cordova version -->
<engines>
<engine name="cordova" version=">=3.5.0" />
</engines>
<!-- ios -->
<platform name="ios">
<!-- 允许访问的应用的 Scheme白名单,如打包A应用;此处应写B应用的scheme,(假如 将A定义为 aapp B定义为bapp 配置如下)。 反之打包的是B应用下面的配置就要反过来写了 -->
<config-file platform="ios" target="*-Info.plist" parent="LSApplicationQueriesSchemes">
<array>
<string>bapp</string>
</array>
</config-file>
<!-- 当前应用自己的 Scheme -->
<config-file platform="ios" target="*-Info.plist" parent="CFBundleURLTypes">
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>aapp</string>
</array>
</dict>
</array>
</config-file>
<!-- 配置相关权限 -->
<config-file platform="ios" target="*-Info.plist" parent="NSAppleMusicUsageDescription">
<string>App需要您的同意,才能访问媒体资料库</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSBluetoothPeripheralUsageDescription">
<string>App需要您的同意,才能访问蓝牙</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSCalendarsUsageDescription">
<string>App需要您的同意,才能访问日历</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSCameraUsageDescription">
<string>App需要您的同意,才能访问相机</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSHealthShareUsageDescription">
<string>App需要您的同意,才能访问健康分享</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSHealthUpdateUsageDescription">
<string>App需要您的同意,才能访问健康更新 </string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSLocationAlwaysUsageDescription">
<string>App需要您的同意,才能始终访问位置</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSLocationUsageDescription">
<string>App需要您的同意,才能访问位置</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSLocationWhenInUseUsageDescription">
<string>App需要您的同意,才能在使用期间访问位置</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSMainNibFile">
<string></string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSMainNibFile~ipad">
<string></string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSMicrophoneUsageDescription">
<string>App需要您的同意,才能访问麦克风</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSMotionUsageDescription">
<string>App需要您的同意,才能访问运动与健身</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSPhotoLibraryUsageDescription">
<string>App需要您的同意,才能访问相册</string>
</config-file>
<config-file platform="ios" target="*-Info.plist" parent="NSRemindersUsageDescription">
<string>App需要您的同意,才能访问提醒事项</string>
</config-file>
</platform>
</plugin>
3.4 在ios 的 AppDelegate.m中增后参数接收(这个是重点)
// 在该方法中新增如下代码
- (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation{
NSString *URLString= [url absoluteString];
[[NSUserDefaults standardUserDefaults] setObject:URLString forKey:@"url"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
操作截图
四、js中调用
4.1 android平台
A应用调用B应用的方法
invokeBapp(){
var sApp;
sApp = startApp.set({// 设置应用包名----注意修改----该包名对应【被叫application】
"application" : "com.myapplication.bapp"// 替换为你真实的包名
}, {//传递给B应用的参数
"username" : "lili"
"userId" : "123456"
});
/*
* 监测应用是否安装
*/
cordova.plugins.fileOpener2.appIsInstalled(packageName, {
success : function(res) {
if (res.status === 0) {
startApp.downloadApps({
"downloadurl":“https://192.168.1.1:8080/bapp/bapp.apk” //替换为你服务器真实的apk路径
},function(success){
console.log("success");
},function(error){
alert(error);
});
} else {
sApp.start(function(success) { // success
console.log("OK");
}, function(error) { // fail
alert(error);
});
}
}
});
}
4.2 iOS平台
A应用调用B应用的方法
invokeBapp(){
var sApp;
var sendParams = "username:lili;userId:123456"; //传递给B应用的参数(ios不能传递json对象)
var twitter = “bapp://”; // B应用的Scheme(就是上面 第三 步配置的那个 再加上冒号和双斜杠就ok了)
sApp = startApp.set(twitter + sendParams + "");
/* 监测是否安装应用 */
sApp.check(function(values) {
sApp.start(function(success) { // success
}, function(error) { // fail
alert(error);
});
}, function(error) {
startApp.downloadApps({
"downloadurl":"itms-services:///?action=download-manifest&url=https://192.168.1.1:8080/bapp/dependence.plist" //替换为你服务器真实的plis路径(这里使用的是plist进行ipa下载,如果你已经发布到AppStore那就直接写AppStore下载路径了)
});
}
4.3 B应用接收参数
receiveAappParams(){
startApp.getExtras(function(res){
// to do something...
console.log(res);
});
}
五、配置 plist文件
由于没u有上传到商店,ipa无法在ios应用中下载,所有使用plis进行ipa映射,然后调用Safari进行下载安装
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<!-- ipa位于服务器的真实路径 -->
<string>https://192.168.1.1:8080/bapp/bapp.ipa</string>
</dict>
<dict>
<key>kind</key>
<string>full-size-image</string>
<key>needs-shine</key>
<true/>
<key>url</key>
<!-- app下载时显示的图标 -->
<string>https://192.168.1.1:8080/bapp/icon.png</string>
</dict>
<dict>
<key>kind</key>
<string>display-image</string>
<key>needs-shine</key>
<true/>
<key>url</key>
<!-- app下载时显示的图标 -->
<string>https://192.168.1.1:8080/bapp/icon.png</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<!-- app包名 -->
<string>com.myapplication.bapp</string>
<key>bundle-version</key>
<!-- app当前版本 -->
<string>1.0.0</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<!-- app名称 -->
<string>客户咨询平台</string>
</dict>
</dict>
</array>
</dict>
</plist>
ok到这里就结束了,写得不对 的地方请指正,有更好的方法请分享